Skip to content

Commit 12ad028

Browse files
committed
using a go script to test update
1 parent f97fdf1 commit 12ad028

File tree

5 files changed

+305
-89
lines changed

5 files changed

+305
-89
lines changed

.github/workflows/test-update.yml

Lines changed: 3 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,6 @@ jobs:
1414
build-and-update:
1515
runs-on: ubuntu-22.04
1616

17-
env:
18-
TAG_VERSION: "v0.6.7"
19-
ARCH: amd64
20-
APPCLI_REPO: arduino/arduino-app-cli
21-
ROUTER_REPO: arduino/arduino-router
22-
23-
APPCLI_TAG: ""
24-
APPCLI_REGEX: "amd64\\.deb$"
25-
26-
ROUTER_TAG: ""
27-
ROUTER_REGEX: "amd64\\.deb$"
28-
2917
steps:
3018
- name: Checkout
3119
uses: actions/checkout@v4
@@ -35,81 +23,8 @@ jobs:
3523
with:
3624
go-version-file: go.mod
3725

38-
- name: Build deb
39-
run: |
40-
go tool task build-deb VERSION=${TAG_VERSION} ARCH=${ARCH}
41-
42-
- name: Fetch .debs dynamically into build/stable
26+
- name: Run dep package update test
4327
env:
44-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45-
run: |
46-
set -euo pipefail
47-
mkdir -p build/stable
48-
49-
fetch_deb () {
50-
local repo="$1" tag="${2:-}" regex="$3"
51-
52-
echo "==> Resolving release for ${repo} (tag='${tag:-<latest>}')"
53-
if [ -n "${tag}" ]; then
54-
url="https://api.github.com/repos/${repo}/releases/tags/${tag}"
55-
else
56-
url="https://api.github.com/repos/${repo}/releases/latest"
57-
fi
58-
59-
rel="$(curl -sfL -H "Authorization: token ${GH_TOKEN}" -H "Accept: application/vnd.github+json" "${url}")"
60-
61-
name="$(echo "$rel" | jq -r --arg re "${regex}" '.assets[] | select(.name | test($re)) | .name' | head -n1)"
62-
dl="$(echo "$rel" | jq -r --arg re "${regex}" '.assets[] | select(.name | test($re)) | .browser_download_url' | head -n1)"
63-
64-
if [ -z "${name}" ] || [ "${name}" = "null" ] || [ -z "${dl}" ] || [ "${dl}" = "null" ]; then
65-
echo "!! No asset found in ${repo} matching regex: ${regex}"
66-
echo " Available assets:"
67-
echo "$rel" | jq -r '.assets[].name'
68-
exit 1
69-
fi
70-
71-
echo "Found: ${name}"
72-
echo "Downloading: ${dl}"
73-
74-
curl -sfL -H "Authorization: token ${GH_TOKEN}" \
75-
-o "build/stable/${name}" \
76-
"${dl}"
77-
78-
ls -lh "build/stable/${name}"
79-
}
80-
81-
fetch_deb "${APPCLI_REPO}" "${APPCLI_TAG}" "${APPCLI_REGEX}"
82-
fetch_deb "${ROUTER_REPO}" "${ROUTER_TAG}" "${ROUTER_REGEX}"
83-
84-
echo "✅ Downloaded files:"
85-
ls -lh build/stable/
86-
ls -lh build/
87-
88-
- name: Build Docker image (no cache)
89-
run: |
90-
docker build -t mock-apt-repo -f test.Dockerfile .
91-
92-
- name: Run mock-apt-repo container
93-
run: |
94-
docker run --rm -d \
95-
--privileged \
96-
--cgroupns=host \
97-
-v /sys/fs/cgroup:/sys/fs/cgroup:rw \
98-
-v /var/run/docker.sock:/var/run/docker.sock \
99-
-e DOCKER_HOST=unix:///var/run/docker.sock \
100-
--name apt-test-update \
101-
mock-apt-repo
102-
103-
- name: Run arduino-app-cli current version
104-
run: |
105-
docker exec --user arduino apt-test-update arduino-app-cli version
106-
107-
- name: Run arduino-app-cli with auto-yes (as arduino)
108-
run: |
109-
mkdir -p artifacts
110-
docker exec apt-test-update sh -lc 'su - arduino -c "yes | arduino-app-cli system update"' \
111-
| tee artifacts/arduino-system-update.log
112-
113-
- name: Run arduino-app-cli version updated
28+
GH_TOKEN: ${{ env.GITHUB_TOKEN }}
11429
run: |
115-
docker exec --user arduino apt-test-update arduino-app-cli version<
30+
go test -v ./internal/testtools/deb_test.go --arch amd64

Taskfile.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,10 @@ tasks:
105105
deps:
106106
- build-deb:clone-examples
107107
cmds:
108-
- docker build --build-arg BINARY_NAME=arduino-app-cli --build-arg DEB_NAME=arduino-app-cli --build-arg VERSION={{ .VERSION }} --build-arg ARCH={{ .ARCH }} --build-arg RELEASE={{ .RELEASE }} --output=./build -f debian/Dockerfile .
108+
- docker build --build-arg BINARY_NAME=arduino-app-cli --build-arg DEB_NAME=arduino-app-cli --build-arg VERSION={{ .VERSION }} --build-arg ARCH={{ .ARCH }} --build-arg RELEASE={{ .RELEASE }} --output={{ .OUTPUT }} -f debian/Dockerfile .
109109
vars:
110110
ARCH: '{{.ARCH | default "arm64"}}'
111+
OUTéPUT: '{{.OUTPUT | default "./build"}}'
111112

112113
build-deb:clone-examples:
113114
desc: "Clones the examples repo directly into the debian structure"

internal/testtools/deb_test.go

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
package testtools
2+
3+
import (
4+
"bytes"
5+
"flag"
6+
"fmt"
7+
"io"
8+
"log"
9+
"os"
10+
"os/exec"
11+
"path/filepath"
12+
"strconv"
13+
"strings"
14+
"testing"
15+
16+
"github.com/stretchr/testify/require"
17+
)
18+
19+
var arch = flag.String("arch", "amd64", "target architecture")
20+
21+
func TestStableToUnstable(t *testing.T) {
22+
tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch)
23+
FetchDebPackage(t, "arduino-router", "latest", *arch)
24+
majorTag := majorTag(t, tagAppCli)
25+
_ = minorTag(t, tagAppCli)
26+
27+
fmt.Printf("Updating from stable version %s to unstable version %s \n", tagAppCli, majorTag)
28+
fmt.Printf("Building local deb version %s \n", majorTag)
29+
buildDebVersion(t, majorTag, *arch)
30+
31+
fmt.Println("**** BUILD docker image *****")
32+
buildDockerImage(t, "test.Dockerfile", "test-apt-update")
33+
fmt.Println("**** RUN docker image *****")
34+
runDockerCommand(t, "test-apt-update")
35+
preUpdateVersion := runDockerSystemVersion(t, "apt-test-update")
36+
runDockerSystemUpdate(t, "apt-test-update")
37+
postUpdateVersion := runDockerSystemVersion(t, "apt-test-update")
38+
runDockerCleanUp(t, "apt-test-update")
39+
require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli)
40+
require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag)
41+
42+
}
43+
44+
func TestUnstableToStable(t *testing.T) {
45+
tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch)
46+
FetchDebPackage(t, "arduino-router", "latest", *arch)
47+
minorTag := minorTag(t, tagAppCli)
48+
moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch)
49+
50+
fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli)
51+
fmt.Printf("Building local deb version %s \n", minorTag)
52+
buildDebVersion(t, minorTag, *arch)
53+
moveDeb(t, "build/", "build/stable", "arduino-app-cli", tagAppCli, *arch)
54+
55+
fmt.Println("**** BUILD docker image *****")
56+
buildDockerImage(t, "test.Dockerfile", "test-apt-update")
57+
fmt.Println("**** RUN docker image *****")
58+
runDockerCommand(t, "test-apt-update")
59+
preUpdateVersion := runDockerSystemVersion(t, "apt-test-update")
60+
runDockerSystemUpdate(t, "apt-test-update")
61+
postUpdateVersion := runDockerSystemVersion(t, "apt-test-update")
62+
runDockerCleanUp(t, "apt-test-update")
63+
require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli)
64+
require.Equal(t, postUpdateVersion, "Arduino App CLI "+minorTag)
65+
66+
}
67+
68+
func FetchDebPackage(t *testing.T, repo, version, arch string) string {
69+
t.Helper()
70+
71+
cmd := exec.Command(
72+
"gh", "release", "list",
73+
"--repo", "github.com/arduino/"+repo,
74+
"--exclude-pre-releases",
75+
"--limit", "1",
76+
)
77+
78+
output, err := cmd.CombinedOutput()
79+
if err != nil {
80+
log.Fatalf("command failed: %v\nOutput: %s", err, output)
81+
}
82+
83+
fmt.Println(string(output))
84+
85+
fields := strings.Fields(string(output))
86+
if len(fields) == 0 {
87+
log.Fatal("could not parse tag from gh release list output")
88+
}
89+
tag := fields[0]
90+
tagPath := strings.TrimPrefix(tag, "v")
91+
92+
debFile := fmt.Sprintf("build/stable/%s_%s-1_%s.deb", repo, tagPath, arch)
93+
fmt.Println(debFile)
94+
if _, err := os.Stat(debFile); err == nil {
95+
fmt.Printf("✅ %s already exists, skipping download.\n", debFile)
96+
return tag
97+
}
98+
fmt.Println("Detected tag:", tag)
99+
cmd2 := exec.Command(
100+
"gh", "release", "download",
101+
tag,
102+
"--repo", "github.com/arduino/"+repo,
103+
"--pattern", "*",
104+
"--dir", "./build/stable",
105+
)
106+
107+
out, err := cmd2.CombinedOutput()
108+
if err != nil {
109+
log.Fatalf("download failed: %v\nOutput: %s", err, out)
110+
}
111+
112+
return tag
113+
114+
}
115+
116+
func buildDebVersion(t *testing.T, tagVersion, arch string) {
117+
cwd, err := os.Getwd()
118+
if err != nil {
119+
panic(err)
120+
}
121+
outputDir := filepath.Join(cwd, "build")
122+
123+
cmd := exec.Command(
124+
"go", "tool", "task", "build-deb",
125+
fmt.Sprintf("VERSION=%s", tagVersion),
126+
fmt.Sprintf("ARCH=%s", arch),
127+
fmt.Sprintf("OUTPUT=%s", outputDir),
128+
)
129+
130+
if err := cmd.Run(); err != nil {
131+
log.Fatalf("failed to run build command: %v", err)
132+
}
133+
}
134+
135+
func majorTag(t *testing.T, tag string) string {
136+
137+
parts := strings.Split(tag, ".")
138+
last := parts[len(parts)-1]
139+
140+
// Remove potential prefix 'v' from the first part, but not from the patch
141+
lastNum, _ := strconv.Atoi(strings.TrimPrefix(last, "v"))
142+
lastNum++
143+
144+
parts[len(parts)-1] = strconv.Itoa(lastNum)
145+
newTag := strings.Join(parts, ".")
146+
147+
return newTag
148+
}
149+
150+
func minorTag(t *testing.T, tag string) string {
151+
152+
parts := strings.Split(tag, ".")
153+
last := parts[len(parts)-1]
154+
155+
lastNum, _ := strconv.Atoi(strings.TrimPrefix(last, "v"))
156+
if lastNum > 0 {
157+
lastNum--
158+
}
159+
160+
parts[len(parts)-1] = strconv.Itoa(lastNum)
161+
newTag := strings.Join(parts, ".")
162+
163+
if !strings.HasPrefix(newTag, "v") {
164+
newTag = "v" + newTag
165+
}
166+
return newTag
167+
}
168+
169+
func buildDockerImage(t *testing.T, dockerfile, name string) {
170+
t.Helper()
171+
172+
cmd := exec.Command("docker", "build", "-t", name, "-f", dockerfile, ".")
173+
174+
if err := cmd.Run(); err != nil {
175+
fmt.Fprintf(os.Stderr, "Error running docker build: %v\n", err)
176+
os.Exit(1)
177+
}
178+
179+
}
180+
181+
func runDockerCommand(t *testing.T, containerImageName string) {
182+
t.Helper()
183+
184+
cmd := exec.Command(
185+
"docker", "run", "--rm", "-d",
186+
"--privileged",
187+
"--cgroupns=host",
188+
"-v", "/sys/fs/cgroup:/sys/fs/cgroup:rw",
189+
"-v", "/var/run/docker.sock:/var/run/docker.sock",
190+
"-e", "DOCKER_HOST=unix:///var/run/docker.sock",
191+
"--name", "apt-test-update",
192+
containerImageName,
193+
)
194+
195+
if err := cmd.Run(); err != nil {
196+
t.Fatalf("failed to run container: %v", err)
197+
}
198+
199+
}
200+
201+
func runDockerSystemVersion(t *testing.T, containerName string) string {
202+
t.Helper()
203+
204+
cmd := exec.Command(
205+
"docker", "exec",
206+
"--user", "arduino",
207+
containerName,
208+
"arduino-app-cli", "version",
209+
)
210+
211+
output, err := cmd.CombinedOutput()
212+
if err != nil {
213+
log.Fatalf("command failed: %v\nOutput: %s", err, output)
214+
}
215+
216+
return string(output)
217+
218+
}
219+
220+
func runDockerSystemUpdate(t *testing.T, containerName string) {
221+
t.Helper()
222+
var buf bytes.Buffer
223+
224+
cmd := exec.Command(
225+
"docker", "exec",
226+
containerName,
227+
"sh", "-lc",
228+
`su - arduino -c "yes | arduino-app-cli system update"`,
229+
)
230+
231+
cmd.Stdout = io.MultiWriter(os.Stdout, &buf)
232+
233+
if err := cmd.Run(); err != nil {
234+
fmt.Fprintf(os.Stderr, "Error running system update: %v\n", err)
235+
os.Exit(1)
236+
}
237+
238+
}
239+
240+
func runDockerCleanUp(t *testing.T, containerName string) {
241+
242+
cleanupCmd := exec.Command("docker", "rm", "-f", containerName)
243+
244+
fmt.Println("🧹 Removing Docker container " + containerName)
245+
if err := cleanupCmd.Run(); err != nil {
246+
fmt.Printf("⚠️ Warning: could not remove container (might not exist): %v\n", err)
247+
}
248+
249+
}
250+
251+
func moveDeb(t *testing.T, startDir, targetDir, repo string, tagVersion string, arch string) {
252+
tagPath := strings.TrimPrefix(tagVersion, "v")
253+
254+
debFile := fmt.Sprintf("%s/%s_%s-1_%s.deb", startDir, repo, tagPath, arch)
255+
256+
moveCmd := exec.Command("mv", debFile, targetDir)
257+
258+
fmt.Printf("📦 Moving %s → %s\n", debFile, targetDir)
259+
if err := moveCmd.Run(); err != nil {
260+
panic(fmt.Errorf("failed to move deb file: %w", err))
261+
}
262+
}

0 commit comments

Comments
 (0)