Skip to content

Commit 53e356e

Browse files
committed
using a go script to test update
1 parent f97fdf1 commit 53e356e

File tree

5 files changed

+308
-89
lines changed

5 files changed

+308
-89
lines changed

.github/workflows/test-update.yml

Lines changed: 4 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,11 @@ on:
1010
permissions:
1111
contents: read
1212

13+
1314
jobs:
1415
build-and-update:
1516
runs-on: ubuntu-22.04
1617

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-
2918
steps:
3019
- name: Checkout
3120
uses: actions/checkout@v4
@@ -35,81 +24,8 @@ jobs:
3524
with:
3625
go-version-file: go.mod
3726

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
27+
- name: Run dep package update test
4328
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
29+
GH_TOKEN: ${{ secrets.ARDUINOBOT_TOKEN }}
11430
run: |
115-
docker exec --user arduino apt-test-update arduino-app-cli version<
31+
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: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
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+
t.Helper()
118+
cwd, err := os.Getwd()
119+
if err != nil {
120+
panic(err)
121+
}
122+
outputDir := filepath.Join(cwd, "build")
123+
124+
cmd := exec.Command(
125+
"go", "tool", "task", "build-deb",
126+
fmt.Sprintf("VERSION=%s", tagVersion),
127+
fmt.Sprintf("ARCH=%s", arch),
128+
fmt.Sprintf("OUTPUT=%s", outputDir),
129+
)
130+
131+
if err := cmd.Run(); err != nil {
132+
log.Fatalf("failed to run build command: %v", err)
133+
}
134+
}
135+
136+
func majorTag(t *testing.T, tag string) string {
137+
t.Helper()
138+
139+
parts := strings.Split(tag, ".")
140+
last := parts[len(parts)-1]
141+
142+
// Remove potential prefix 'v' from the first part, but not from the patch
143+
lastNum, _ := strconv.Atoi(strings.TrimPrefix(last, "v"))
144+
lastNum++
145+
146+
parts[len(parts)-1] = strconv.Itoa(lastNum)
147+
newTag := strings.Join(parts, ".")
148+
149+
return newTag
150+
}
151+
152+
func minorTag(t *testing.T, tag string) string {
153+
t.Helper()
154+
155+
parts := strings.Split(tag, ".")
156+
last := parts[len(parts)-1]
157+
158+
lastNum, _ := strconv.Atoi(strings.TrimPrefix(last, "v"))
159+
if lastNum > 0 {
160+
lastNum--
161+
}
162+
163+
parts[len(parts)-1] = strconv.Itoa(lastNum)
164+
newTag := strings.Join(parts, ".")
165+
166+
if !strings.HasPrefix(newTag, "v") {
167+
newTag = "v" + newTag
168+
}
169+
return newTag
170+
}
171+
172+
func buildDockerImage(t *testing.T, dockerfile, name string) {
173+
t.Helper()
174+
175+
cmd := exec.Command("docker", "build", "-t", name, "-f", dockerfile, ".")
176+
out, err := cmd.CombinedOutput()
177+
if err != nil {
178+
t.Fatalf("docker build failed: %v\nOutput:\n%s", err, string(out))
179+
}
180+
181+
}
182+
183+
func runDockerCommand(t *testing.T, containerImageName string) {
184+
t.Helper()
185+
186+
cmd := exec.Command(
187+
"docker", "run", "--rm", "-d",
188+
"--privileged",
189+
"--cgroupns=host",
190+
"-v", "/sys/fs/cgroup:/sys/fs/cgroup:rw",
191+
"-v", "/var/run/docker.sock:/var/run/docker.sock",
192+
"-e", "DOCKER_HOST=unix:///var/run/docker.sock",
193+
"--name", "apt-test-update",
194+
containerImageName,
195+
)
196+
197+
if err := cmd.Run(); err != nil {
198+
t.Fatalf("failed to run container: %v", err)
199+
}
200+
201+
}
202+
203+
func runDockerSystemVersion(t *testing.T, containerName string) string {
204+
t.Helper()
205+
206+
cmd := exec.Command(
207+
"docker", "exec",
208+
"--user", "arduino",
209+
containerName,
210+
"arduino-app-cli", "version",
211+
)
212+
213+
output, err := cmd.CombinedOutput()
214+
if err != nil {
215+
log.Fatalf("command failed: %v\nOutput: %s", err, output)
216+
}
217+
218+
return string(output)
219+
220+
}
221+
222+
func runDockerSystemUpdate(t *testing.T, containerName string) {
223+
t.Helper()
224+
var buf bytes.Buffer
225+
226+
cmd := exec.Command(
227+
"docker", "exec",
228+
containerName,
229+
"sh", "-lc",
230+
`su - arduino -c "yes | arduino-app-cli system update"`,
231+
)
232+
233+
cmd.Stdout = io.MultiWriter(os.Stdout, &buf)
234+
235+
if err := cmd.Run(); err != nil {
236+
fmt.Fprintf(os.Stderr, "Error running system update: %v\n", err)
237+
os.Exit(1)
238+
}
239+
240+
}
241+
242+
func runDockerCleanUp(t *testing.T, containerName string) {
243+
244+
cleanupCmd := exec.Command("docker", "rm", "-f", containerName)
245+
246+
fmt.Println("🧹 Removing Docker container " + containerName)
247+
if err := cleanupCmd.Run(); err != nil {
248+
fmt.Printf("⚠️ Warning: could not remove container (might not exist): %v\n", err)
249+
}
250+
251+
}
252+
253+
func moveDeb(t *testing.T, startDir, targetDir, repo string, tagVersion string, arch string) {
254+
tagPath := strings.TrimPrefix(tagVersion, "v")
255+
256+
debFile := fmt.Sprintf("%s/%s_%s-1_%s.deb", startDir, repo, tagPath, arch)
257+
258+
moveCmd := exec.Command("mv", debFile, targetDir)
259+
260+
fmt.Printf("📦 Moving %s → %s\n", debFile, targetDir)
261+
if err := moveCmd.Run(); err != nil {
262+
panic(fmt.Errorf("failed to move deb file: %w", err))
263+
}
264+
}

0 commit comments

Comments
 (0)