From 6a5ca62d02516cc8ea2b7acb682fb82d5a26ff64 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 29 Oct 2025 10:53:15 +0100 Subject: [PATCH 01/17] test .deb update --- Taskfile.yml | 42 +++++++++++++++++++++++++++++ test.Dockerfile | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 test.Dockerfile diff --git a/Taskfile.yml b/Taskfile.yml index d11bae23..303c792a 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -12,6 +12,9 @@ vars: RUNNER_VERSION: "0.5.0" VERSION: # if version is not passed we hack the semver by encoding the commit as pre-release sh: echo "${VERSION:-0.0.0-$(git rev-parse --short HEAD)}" + NEW_PACKAGE: + sh: ls -1 ./build/arduino-app-cli_*.deb 2>/dev/null | head -n 1 + GITHUB_TOKEN_FILE: ./github_token.txt tasks: init: @@ -123,6 +126,45 @@ tasks: echo "Examples successfully cloned." silent: false + build-image: + desc: "Builds the mock-repo Docker image (requires GITHUB_TOKEN_FILE)" + deps: [build-deb] + vars: + PKG_PATH: '{{.NEW_PACKAGE}}' + cmds: + # --- MODIFIED --- + # Check for both the package and the token file + - | + if [ ! -f "{{.GITHUB_TOKEN_FILE}}" ]; then + echo "Error: GitHub token file not found at {{.GITHUB_TOKEN_FILE}}" + echo "Please create this file and add your GitHub PAT to it." + exit 1 + fi + - | + echo "Using package: {{.PKG_PATH}}" + echo "Using GitHub token from: {{.GITHUB_TOKEN_FILE}}" + + # Enable BuildKit and pass the secret + DOCKER_BUILDKIT=1 docker build \ + --secret id=github_token,src={{.GITHUB_TOKEN_FILE}} \ + --build-arg NEW_PACKAGE_PATH={{.PKG_PATH}} \ + -t newdeb \ + -f test.Dockerfile . + status: + - '[[ -f "{{.PKG_PATH}}" ]]' + - '[[ -f "{{.DOCKERFILE_NAME}}" ]]' + # Re-build if token file changes + - '[[ -f "{{.GITHUB_TOKEN_FILE}}" ]]' + + test-deb: + desc: Test the debian package locally + deps: + - build-deb + vars: + VERSION: "0.6.3" + cmds: + - docker build --no-cache -t mock-apt-repo -f test.Dockerfile . + arduino-app-cli:build:local: desc: "Build the arduino-app-cli locally" cmds: diff --git a/test.Dockerfile b/test.Dockerfile new file mode 100644 index 00000000..d2fa9dfb --- /dev/null +++ b/test.Dockerfile @@ -0,0 +1,71 @@ +# 1. Use Debian base +FROM debian:trixie + +ENV DEBIAN_FRONTEND=noninteractive + +# 2. Install necessary tools +RUN apt update && apt install -y \ + dpkg-dev \ + apt-utils \ + adduser \ + && rm -rf /var/lib/apt/lists/* + +# 3. Symlink addgroup for minimal system scripts +RUN ln -s /usr/sbin/addgroup /usr/bin/addgroup || true + +# 4. Build args for parameterization +ARG OLD_PACKAGE_PATH=build/old_package +ARG NEW_PACKAGE_PATH=build +ARG APP_PACKAGE_NAME=arduino-app-cli +ARG ROUTER_PACKAGE_NAME=arduino-router +ARG ARCH=arm64 +ARG VERSION=0.6.3 + +# 5. Copy packages dynamically +COPY ${OLD_PACKAGE_PATH}/${APP_PACKAGE_NAME}*.deb /tmp/old_app.deb +COPY ${NEW_PACKAGE_PATH}/${APP_PACKAGE_NAME}*.deb /tmp/new_app.deb +COPY ${NEW_PACKAGE_PATH}/${ROUTER_PACKAGE_NAME}*.deb /tmp/new_router.deb + +# 6. Install old package + router dependency +RUN apt update && apt install -y \ + /tmp/old_app.deb \ + /tmp/new_router.deb \ + && rm /tmp/old_app.deb + +# 7. Setup local APT repo with new packages +RUN mkdir -p /var/www/html/myrepo/dists/trixie/main/binary-${ARCH} + +# Rename new packages to match their real package/version/arch +RUN mv /tmp/new_app.deb /var/www/html/myrepo/dists/trixie/main/binary-${ARCH}/${APP_PACKAGE_NAME}_${VERSION}_${ARCH}.deb +RUN mv /tmp/new_router.deb /var/www/html/myrepo/dists/trixie/main/binary-${ARCH}/${ROUTER_PACKAGE_NAME}_0.6.2-1_${ARCH}.deb + +# 8. Generate Packages.gz metadata +WORKDIR /var/www/html/myrepo +RUN dpkg-scanpackages dists/trixie/main/binary-arm64 /dev/null | gzip -9c > dists/trixie/main/binary-arm64/Packages.gz +WORKDIR / + +# 9. Configure local APT repo +RUN echo "deb [trusted=yes arch=${ARCH}] file:/var/www/html/myrepo trixie main" \ + > /etc/apt/sources.list.d/my-mock-repo.list + +# 10. Fix home dir for arduino user (optional) +RUN usermod -s /bin/bash arduino || true +RUN mkdir -p /home/arduino && chown -R arduino:arduino /home/arduino + +# 11. Entrypoint: show upgrade availability +RUN echo '#!/bin/bash\n\ +set -e\n\ +echo "--- Updating APT ---"\n\ +apt update\n\ +echo "--- Installed version ---"\n\ +dpkg -l | grep ${APP_PACKAGE_NAME} || true\n\ +echo "--- Upgrade candidate ---"\n\ +apt-cache policy ${APP_PACKAGE_NAME}\n\ +echo "--- Available upgrades ---"\n\ +apt list --upgradable | grep ${APP_PACKAGE_NAME} || echo "No upgrade found"\n\ +echo "--- Simulating upgrade ---"\n\ +apt upgrade --simulate\n\ +' > /entrypoint.sh + +RUN chmod +x /entrypoint.sh +CMD ["/entrypoint.sh"] From 292418cdb038b1d41e6c1b4eb3f3022be1ca16e0 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 4 Nov 2025 11:21:02 +0100 Subject: [PATCH 02/17] task test --- Taskfile.yml | 3 +++ test.Dockerfile | 69 ++++++++++++++++--------------------------------- 2 files changed, 25 insertions(+), 47 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 303c792a..7376e133 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -164,6 +164,9 @@ tasks: VERSION: "0.6.3" cmds: - docker build --no-cache -t mock-apt-repo -f test.Dockerfile . + - docker run --rm -it --privileged -v /sys/fs/cgroup:/sys/fs/cgroup:ro --name apt-test-update mock-apt-repo + - docker exec -d apt-test-update /usr/bin/arduino-router + arduino-app-cli:build:local: desc: "Build the arduino-app-cli locally" diff --git a/test.Dockerfile b/test.Dockerfile index d2fa9dfb..94f8e11a 100644 --- a/test.Dockerfile +++ b/test.Dockerfile @@ -1,71 +1,46 @@ -# 1. Use Debian base FROM debian:trixie -ENV DEBIAN_FRONTEND=noninteractive +ENV container docker +STOPSIGNAL SIGRTMIN+3 +VOLUME ["/sys/fs/cgroup"] -# 2. Install necessary tools -RUN apt update && apt install -y \ - dpkg-dev \ - apt-utils \ - adduser \ +# Install systemd + dependencies +RUN apt update && apt install -y systemd systemd-sysv dbus \ + dpkg-dev apt-utils adduser gzip \ && rm -rf /var/lib/apt/lists/* -# 3. Symlink addgroup for minimal system scripts -RUN ln -s /usr/sbin/addgroup /usr/bin/addgroup || true - -# 4. Build args for parameterization +# Copy your packages and setup repo (as before) ARG OLD_PACKAGE_PATH=build/old_package ARG NEW_PACKAGE_PATH=build ARG APP_PACKAGE_NAME=arduino-app-cli ARG ROUTER_PACKAGE_NAME=arduino-router ARG ARCH=arm64 -ARG VERSION=0.6.3 -# 5. Copy packages dynamically COPY ${OLD_PACKAGE_PATH}/${APP_PACKAGE_NAME}*.deb /tmp/old_app.deb COPY ${NEW_PACKAGE_PATH}/${APP_PACKAGE_NAME}*.deb /tmp/new_app.deb COPY ${NEW_PACKAGE_PATH}/${ROUTER_PACKAGE_NAME}*.deb /tmp/new_router.deb -# 6. Install old package + router dependency -RUN apt update && apt install -y \ - /tmp/old_app.deb \ - /tmp/new_router.deb \ - && rm /tmp/old_app.deb - -# 7. Setup local APT repo with new packages -RUN mkdir -p /var/www/html/myrepo/dists/trixie/main/binary-${ARCH} - -# Rename new packages to match their real package/version/arch -RUN mv /tmp/new_app.deb /var/www/html/myrepo/dists/trixie/main/binary-${ARCH}/${APP_PACKAGE_NAME}_${VERSION}_${ARCH}.deb -RUN mv /tmp/new_router.deb /var/www/html/myrepo/dists/trixie/main/binary-${ARCH}/${ROUTER_PACKAGE_NAME}_0.6.2-1_${ARCH}.deb +RUN apt update && apt install -y /tmp/old_app.deb /tmp/new_router.deb \ + && rm /tmp/old_app.deb \ + && mkdir -p /var/www/html/myrepo/dists/trixie/main/binary-${ARCH} \ + && mv /tmp/new_app.deb /var/www/html/myrepo/dists/trixie/main/binary-${ARCH}/ \ + && mv /tmp/new_router.deb /var/www/html/myrepo/dists/trixie/main/binary-${ARCH}/ -# 8. Generate Packages.gz metadata WORKDIR /var/www/html/myrepo -RUN dpkg-scanpackages dists/trixie/main/binary-arm64 /dev/null | gzip -9c > dists/trixie/main/binary-arm64/Packages.gz +RUN dpkg-scanpackages dists/trixie/main/binary-${ARCH} /dev/null | gzip -9c > dists/trixie/main/binary-${ARCH}/Packages.gz WORKDIR / -# 9. Configure local APT repo +RUN usermod -s /bin/bash arduino || true +RUN mkdir -p /home/arduino && chown -R arduino:arduino /home/arduino + + RUN echo "deb [trusted=yes arch=${ARCH}] file:/var/www/html/myrepo trixie main" \ > /etc/apt/sources.list.d/my-mock-repo.list -# 10. Fix home dir for arduino user (optional) -RUN usermod -s /bin/bash arduino || true -RUN mkdir -p /home/arduino && chown -R arduino:arduino /home/arduino -# 11. Entrypoint: show upgrade availability -RUN echo '#!/bin/bash\n\ -set -e\n\ -echo "--- Updating APT ---"\n\ -apt update\n\ -echo "--- Installed version ---"\n\ -dpkg -l | grep ${APP_PACKAGE_NAME} || true\n\ -echo "--- Upgrade candidate ---"\n\ -apt-cache policy ${APP_PACKAGE_NAME}\n\ -echo "--- Available upgrades ---"\n\ -apt list --upgradable | grep ${APP_PACKAGE_NAME} || echo "No upgrade found"\n\ -echo "--- Simulating upgrade ---"\n\ -apt upgrade --simulate\n\ -' > /entrypoint.sh +VOLUME [ "/sys/fs/cgroup" ] + + -RUN chmod +x /entrypoint.sh -CMD ["/entrypoint.sh"] +# CMD: systemd must be PID 1 +CMD ["/sbin/init"] From 4b11bbc6f56b4d342c4d967ed3d9f5fe3f937cfb Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 5 Nov 2025 10:23:54 +0100 Subject: [PATCH 03/17] dind --- .github/workflows/test-update.yml | 124 ++++++++++++++++++++++++++++++ Taskfile.yml | 33 ++++---- test.Dockerfile | 40 ++++------ 3 files changed, 153 insertions(+), 44 deletions(-) create mode 100644 .github/workflows/test-update.yml diff --git a/.github/workflows/test-update.yml b/.github/workflows/test-update.yml new file mode 100644 index 00000000..7e34a67f --- /dev/null +++ b/.github/workflows/test-update.yml @@ -0,0 +1,124 @@ +name: Build & Update with Arduino CLI + +on: + push: + branches: + - main + - test_package_update + workflow_dispatch: + +permissions: + contents: read + +jobs: + build-and-update: + runs-on: ubuntu-22.04 + + env: + TAG_VERSION: "v0.6.7" + ARCH: amd64 + APPCLI_REPO: arduino/arduino-app-cli + ROUTER_REPO: arduino/arduino-router + + APPCLI_TAG: "" + APPCLI_REGEX: "amd64\\.deb$" + + ROUTER_TAG: "" + ROUTER_REGEX: "amd64\\.deb$" + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Build deb + run: | + go tool task build-deb VERSION=${TAG_VERSION} ARCH=${ARCH} + + - name: Fetch .debs dynamically into build/stable + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + mkdir -p build/stable + + fetch_deb () { + local repo="$1" tag="${2:-}" regex="$3" + + echo "==> Resolving release for ${repo} (tag='${tag:-}')" + if [ -n "${tag}" ]; then + url="https://api.github.com/repos/${repo}/releases/tags/${tag}" + else + url="https://api.github.com/repos/${repo}/releases/latest" + fi + + rel="$(curl -sfL -H "Authorization: token ${GH_TOKEN}" -H "Accept: application/vnd.github+json" "${url}")" + + name="$(echo "$rel" | jq -r --arg re "${regex}" '.assets[] | select(.name | test($re)) | .name' | head -n1)" + dl="$(echo "$rel" | jq -r --arg re "${regex}" '.assets[] | select(.name | test($re)) | .browser_download_url' | head -n1)" + + if [ -z "${name}" ] || [ "${name}" = "null" ] || [ -z "${dl}" ] || [ "${dl}" = "null" ]; then + echo "!! No asset found in ${repo} matching regex: ${regex}" + echo " Available assets:" + echo "$rel" | jq -r '.assets[].name' + exit 1 + fi + + echo "Found: ${name}" + echo "Downloading: ${dl}" + + curl -sfL -H "Authorization: token ${GH_TOKEN}" \ + -o "build/stable/${name}" \ + "${dl}" + + ls -lh "build/stable/${name}" + } + + fetch_deb "${APPCLI_REPO}" "${APPCLI_TAG}" "${APPCLI_REGEX}" + fetch_deb "${ROUTER_REPO}" "${ROUTER_TAG}" "${ROUTER_REGEX}" + + echo "✅ Downloaded files:" + ls -lh build/stable/ + ls -lh build/ + + - name: Build Docker image (no cache) + run: | + docker build -t mock-apt-repo -f test.Dockerfile . + + - name: Run mock-apt-repo container + run: | + docker run --rm -d \ + --privileged \ + --cgroupns=host \ + -v /sys/fs/cgroup:/sys/fs/cgroup:rw \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -e DOCKER_HOST=unix:///var/run/docker.sock \ + --name apt-test-update \ + mock-apt-repo + + - name: Run arduino-app-cli current version + run: | + docker exec --user arduino apt-test-update arduino-app-cli version + + - name: Run arduino-app-cli with auto-yes (as arduino) + run: | + mkdir -p artifacts + docker exec apt-test-update sh -lc 'su - arduino -c "yes | arduino-app-cli system update"' \ + | tee artifacts/arduino-system-update.log + + - name: Run arduino-app-cli version updated + run: | + docker exec --user arduino apt-test-update arduino-app-cli version + + - name: Restart arduino-app-cli service and verify + run: | + docker exec apt-test-update sh -lc ' + sudo systemctl status arduino-app-cli --no-pager + sudo systemctl status arduino-router --no-pager + ' + + diff --git a/Taskfile.yml b/Taskfile.yml index 7376e133..aaeb54a9 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -13,7 +13,7 @@ vars: VERSION: # if version is not passed we hack the semver by encoding the commit as pre-release sh: echo "${VERSION:-0.0.0-$(git rev-parse --short HEAD)}" NEW_PACKAGE: - sh: ls -1 ./build/arduino-app-cli_*.deb 2>/dev/null | head -n 1 + sh: ls -1 ./build/arduino-app-cli_*.deb 2>/dev/null | head -n 1 GITHUB_TOKEN_FILE: ./github_token.txt tasks: @@ -130,26 +130,26 @@ tasks: desc: "Builds the mock-repo Docker image (requires GITHUB_TOKEN_FILE)" deps: [build-deb] vars: - PKG_PATH: '{{.NEW_PACKAGE}}' + PKG_PATH: "{{.NEW_PACKAGE}}" cmds: # --- MODIFIED --- # Check for both the package and the token file - | - if [ ! -f "{{.GITHUB_TOKEN_FILE}}" ]; then - echo "Error: GitHub token file not found at {{.GITHUB_TOKEN_FILE}}" - echo "Please create this file and add your GitHub PAT to it." - exit 1 - fi + if [ ! -f "{{.GITHUB_TOKEN_FILE}}" ]; then + echo "Error: GitHub token file not found at {{.GITHUB_TOKEN_FILE}}" + echo "Please create this file and add your GitHub PAT to it." + exit 1 + fi - | - echo "Using package: {{.PKG_PATH}}" - echo "Using GitHub token from: {{.GITHUB_TOKEN_FILE}}" - - # Enable BuildKit and pass the secret - DOCKER_BUILDKIT=1 docker build \ - --secret id=github_token,src={{.GITHUB_TOKEN_FILE}} \ - --build-arg NEW_PACKAGE_PATH={{.PKG_PATH}} \ - -t newdeb \ - -f test.Dockerfile . + echo "Using package: {{.PKG_PATH}}" + echo "Using GitHub token from: {{.GITHUB_TOKEN_FILE}}" + + # Enable BuildKit and pass the secret + DOCKER_BUILDKIT=1 docker build \ + --secret id=github_token,src={{.GITHUB_TOKEN_FILE}} \ + --build-arg NEW_PACKAGE_PATH={{.PKG_PATH}} \ + -t newdeb \ + -f test.Dockerfile . status: - '[[ -f "{{.PKG_PATH}}" ]]' - '[[ -f "{{.DOCKERFILE_NAME}}" ]]' @@ -167,7 +167,6 @@ tasks: - docker run --rm -it --privileged -v /sys/fs/cgroup:/sys/fs/cgroup:ro --name apt-test-update mock-apt-repo - docker exec -d apt-test-update /usr/bin/arduino-router - arduino-app-cli:build:local: desc: "Build the arduino-app-cli locally" cmds: diff --git a/test.Dockerfile b/test.Dockerfile index 94f8e11a..e95a7689 100644 --- a/test.Dockerfile +++ b/test.Dockerfile @@ -1,30 +1,21 @@ FROM debian:trixie -ENV container docker -STOPSIGNAL SIGRTMIN+3 -VOLUME ["/sys/fs/cgroup"] - -# Install systemd + dependencies -RUN apt update && apt install -y systemd systemd-sysv dbus \ - dpkg-dev apt-utils adduser gzip \ - && rm -rf /var/lib/apt/lists/* - -# Copy your packages and setup repo (as before) -ARG OLD_PACKAGE_PATH=build/old_package -ARG NEW_PACKAGE_PATH=build -ARG APP_PACKAGE_NAME=arduino-app-cli -ARG ROUTER_PACKAGE_NAME=arduino-router +RUN apt update && \ + apt install -y systemd systemd-sysv dbus \ + sudo docker.io ca-certificates curl gnupg \ + dpkg-dev apt-utils adduser gzip && \ + rm -rf /var/lib/apt/lists/* + ARG ARCH=arm64 -COPY ${OLD_PACKAGE_PATH}/${APP_PACKAGE_NAME}*.deb /tmp/old_app.deb -COPY ${NEW_PACKAGE_PATH}/${APP_PACKAGE_NAME}*.deb /tmp/new_app.deb -COPY ${NEW_PACKAGE_PATH}/${ROUTER_PACKAGE_NAME}*.deb /tmp/new_router.deb +COPY build/stable/arduino-app-cli*.deb /tmp/stable.deb +COPY build/arduino-app-cli*.deb /tmp/unstable.deb +COPY build/stable/arduino-router*.deb /tmp/router.deb -RUN apt update && apt install -y /tmp/old_app.deb /tmp/new_router.deb \ - && rm /tmp/old_app.deb \ +RUN apt update && apt install -y /tmp/stable.deb /tmp/router.deb \ + && rm /tmp/stable.deb /tmp/router.deb \ && mkdir -p /var/www/html/myrepo/dists/trixie/main/binary-${ARCH} \ - && mv /tmp/new_app.deb /var/www/html/myrepo/dists/trixie/main/binary-${ARCH}/ \ - && mv /tmp/new_router.deb /var/www/html/myrepo/dists/trixie/main/binary-${ARCH}/ + && mv /tmp/unstable.deb /var/www/html/myrepo/dists/trixie/main/binary-${ARCH}/ WORKDIR /var/www/html/myrepo RUN dpkg-scanpackages dists/trixie/main/binary-${ARCH} /dev/null | gzip -9c > dists/trixie/main/binary-${ARCH}/Packages.gz @@ -32,15 +23,10 @@ WORKDIR / RUN usermod -s /bin/bash arduino || true RUN mkdir -p /home/arduino && chown -R arduino:arduino /home/arduino - +RUN usermod -aG docker arduino RUN echo "deb [trusted=yes arch=${ARCH}] file:/var/www/html/myrepo trixie main" \ > /etc/apt/sources.list.d/my-mock-repo.list - -VOLUME [ "/sys/fs/cgroup" ] - - - # CMD: systemd must be PID 1 CMD ["/sbin/init"] From b3abcc52aa930149612fef750870b2e6a56d64bb Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 5 Nov 2025 14:30:57 +0100 Subject: [PATCH 04/17] taskfile udpate --- .github/workflows/test-update.yml | 11 +---------- Taskfile.yml | 3 --- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/test-update.yml b/.github/workflows/test-update.yml index 7e34a67f..1682bb1d 100644 --- a/.github/workflows/test-update.yml +++ b/.github/workflows/test-update.yml @@ -112,13 +112,4 @@ jobs: - name: Run arduino-app-cli version updated run: | - docker exec --user arduino apt-test-update arduino-app-cli version - - - name: Restart arduino-app-cli service and verify - run: | - docker exec apt-test-update sh -lc ' - sudo systemctl status arduino-app-cli --no-pager - sudo systemctl status arduino-router --no-pager - ' - - + docker exec --user arduino apt-test-update arduino-app-cli version< \ No newline at end of file diff --git a/Taskfile.yml b/Taskfile.yml index aaeb54a9..baf95ac8 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -160,12 +160,9 @@ tasks: desc: Test the debian package locally deps: - build-deb - vars: - VERSION: "0.6.3" cmds: - docker build --no-cache -t mock-apt-repo -f test.Dockerfile . - docker run --rm -it --privileged -v /sys/fs/cgroup:/sys/fs/cgroup:ro --name apt-test-update mock-apt-repo - - docker exec -d apt-test-update /usr/bin/arduino-router arduino-app-cli:build:local: desc: "Build the arduino-app-cli locally" From ede3f908d750de970c9ba36f57034b92803ee7af Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Fri, 7 Nov 2025 16:25:46 +0100 Subject: [PATCH 05/17] using a go script to test update --- .github/workflows/test-update.yml | 93 +----- Taskfile.yml | 3 +- internal/testtools/deb_test.go | 290 ++++++++++++++++++ internal/testtools/package_update.go | 38 +++ .../testtools/test.Dockerfile | 2 +- 5 files changed, 336 insertions(+), 90 deletions(-) create mode 100644 internal/testtools/deb_test.go create mode 100644 internal/testtools/package_update.go rename test.Dockerfile => internal/testtools/test.Dockerfile (98%) diff --git a/.github/workflows/test-update.yml b/.github/workflows/test-update.yml index 1682bb1d..6d5b6439 100644 --- a/.github/workflows/test-update.yml +++ b/.github/workflows/test-update.yml @@ -10,22 +10,11 @@ on: permissions: contents: read + jobs: build-and-update: runs-on: ubuntu-22.04 - env: - TAG_VERSION: "v0.6.7" - ARCH: amd64 - APPCLI_REPO: arduino/arduino-app-cli - ROUTER_REPO: arduino/arduino-router - - APPCLI_TAG: "" - APPCLI_REGEX: "amd64\\.deb$" - - ROUTER_TAG: "" - ROUTER_REGEX: "amd64\\.deb$" - steps: - name: Checkout uses: actions/checkout@v4 @@ -35,81 +24,9 @@ jobs: with: go-version-file: go.mod - - name: Build deb - run: | - go tool task build-deb VERSION=${TAG_VERSION} ARCH=${ARCH} - - - name: Fetch .debs dynamically into build/stable + - name: Run dep package update test env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -euo pipefail - mkdir -p build/stable - - fetch_deb () { - local repo="$1" tag="${2:-}" regex="$3" - - echo "==> Resolving release for ${repo} (tag='${tag:-}')" - if [ -n "${tag}" ]; then - url="https://api.github.com/repos/${repo}/releases/tags/${tag}" - else - url="https://api.github.com/repos/${repo}/releases/latest" - fi - - rel="$(curl -sfL -H "Authorization: token ${GH_TOKEN}" -H "Accept: application/vnd.github+json" "${url}")" - - name="$(echo "$rel" | jq -r --arg re "${regex}" '.assets[] | select(.name | test($re)) | .name' | head -n1)" - dl="$(echo "$rel" | jq -r --arg re "${regex}" '.assets[] | select(.name | test($re)) | .browser_download_url' | head -n1)" - - if [ -z "${name}" ] || [ "${name}" = "null" ] || [ -z "${dl}" ] || [ "${dl}" = "null" ]; then - echo "!! No asset found in ${repo} matching regex: ${regex}" - echo " Available assets:" - echo "$rel" | jq -r '.assets[].name' - exit 1 - fi - - echo "Found: ${name}" - echo "Downloading: ${dl}" - - curl -sfL -H "Authorization: token ${GH_TOKEN}" \ - -o "build/stable/${name}" \ - "${dl}" - - ls -lh "build/stable/${name}" - } - - fetch_deb "${APPCLI_REPO}" "${APPCLI_TAG}" "${APPCLI_REGEX}" - fetch_deb "${ROUTER_REPO}" "${ROUTER_TAG}" "${ROUTER_REGEX}" - - echo "✅ Downloaded files:" - ls -lh build/stable/ - ls -lh build/ - - - name: Build Docker image (no cache) - run: | - docker build -t mock-apt-repo -f test.Dockerfile . - - - name: Run mock-apt-repo container - run: | - docker run --rm -d \ - --privileged \ - --cgroupns=host \ - -v /sys/fs/cgroup:/sys/fs/cgroup:rw \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -e DOCKER_HOST=unix:///var/run/docker.sock \ - --name apt-test-update \ - mock-apt-repo - - - name: Run arduino-app-cli current version - run: | - docker exec --user arduino apt-test-update arduino-app-cli version - - - name: Run arduino-app-cli with auto-yes (as arduino) - run: | - mkdir -p artifacts - docker exec apt-test-update sh -lc 'su - arduino -c "yes | arduino-app-cli system update"' \ - | tee artifacts/arduino-system-update.log - - - name: Run arduino-app-cli version updated + GH_TOKEN: ${{ secrets.ARDUINOBOT_TOKEN }} run: | - docker exec --user arduino apt-test-update arduino-app-cli version< \ No newline at end of file + cd internal/testtools + go test -v ./deb_test.go --arch amd64 \ No newline at end of file diff --git a/Taskfile.yml b/Taskfile.yml index baf95ac8..84b137c9 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -105,9 +105,10 @@ tasks: deps: - build-deb:clone-examples cmds: - - 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 . + - 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 . vars: ARCH: '{{.ARCH | default "arm64"}}' + OUTPUT: '{{.OUTPUT | default "./build"}}' build-deb:clone-examples: desc: "Clones the examples repo directly into the debian structure" diff --git a/internal/testtools/deb_test.go b/internal/testtools/deb_test.go new file mode 100644 index 00000000..56909dbd --- /dev/null +++ b/internal/testtools/deb_test.go @@ -0,0 +1,290 @@ +package testtools + +import ( + "bytes" + "flag" + "fmt" + "io" + "log" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +var arch = flag.String("arch", "amd64", "target architecture") + +func TestStableToUnstable(t *testing.T) { + fmt.Printf("***** ARCH %s ***** \n", *arch) + tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) + FetchDebPackage(t, "arduino-router", "latest", *arch) + majorTag := majorTag(t, tagAppCli) + _ = minorTag(t, tagAppCli) + + fmt.Printf("Updating from stable version %s to unstable version %s \n", tagAppCli, majorTag) + fmt.Printf("Building local deb version %s \n", majorTag) + buildDebVersion(t, majorTag, *arch) + + fmt.Println("**** BUILD docker image *****") + buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) + fmt.Println("**** RUN docker image *****") + runDockerCommand(t, "apt-test-update-image") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerSystemUpdate(t, "apt-test-update") + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerCleanUp(t, "apt-test-update") + require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") + +} + +// func TestUnstableToStable(t *testing.T) { +// tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) +// FetchDebPackage(t, "arduino-router", "latest", *arch) +// minorTag := minorTag(t, tagAppCli) +// moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) + +// fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) +// fmt.Printf("Building local deb version %s \n", minorTag) +// buildDebVersion(t, minorTag, *arch) +// moveDeb(t, "build/", "build/stable", "arduino-app-cli", tagAppCli, *arch) + +// fmt.Println("**** BUILD docker image *****") +// buildDockerImage(t, "test.Dockerfile", "test-apt-update") +// fmt.Println("**** RUN docker image *****") +// runDockerCommand(t, "test-apt-update") +// preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") +// runDockerSystemUpdate(t, "apt-test-update") +// postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") +// runDockerCleanUp(t, "apt-test-update") +// require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli) +// require.Equal(t, postUpdateVersion, "Arduino App CLI "+minorTag) + +// } + +func FetchDebPackage(t *testing.T, repo, version, arch string) string { + t.Helper() + + cmd := exec.Command( + "gh", "release", "list", + "--repo", "github.com/arduino/"+repo, + "--exclude-pre-releases", + "--limit", "1", + ) + + output, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("command failed: %v\nOutput: %s", err, output) + } + + fmt.Println(string(output)) + + fields := strings.Fields(string(output)) + if len(fields) == 0 { + log.Fatal("could not parse tag from gh release list output") + } + tag := fields[0] + tagPath := strings.TrimPrefix(tag, "v") + + debFile := fmt.Sprintf("build/stable/%s_%s-1_%s.deb", repo, tagPath, arch) + fmt.Println(debFile) + if _, err := os.Stat(debFile); err == nil { + fmt.Printf("✅ %s already exists, skipping download.\n", debFile) + return tag + } + fmt.Println("Detected tag:", tag) + cmd2 := exec.Command( + "gh", "release", "download", + tag, + "--repo", "github.com/arduino/"+repo, + "--pattern", "*", + "--dir", "./build/stable", + ) + + out, err := cmd2.CombinedOutput() + if err != nil { + log.Fatalf("download failed: %v\nOutput: %s", err, out) + } + + return tag + +} + +func buildDebVersion(t *testing.T, tagVersion, arch string) { + t.Helper() + cwd, err := os.Getwd() + if err != nil { + panic(err) + } + outputDir := filepath.Join(cwd, "build") + + cmd := exec.Command( + "go", "tool", "task", "build-deb", + fmt.Sprintf("VERSION=%s", tagVersion), + fmt.Sprintf("ARCH=%s", arch), + fmt.Sprintf("OUTPUT=%s", outputDir), + ) + + if err := cmd.Run(); err != nil { + log.Fatalf("failed to run build command: %v", err) + } +} + +func majorTag(t *testing.T, tag string) string { + t.Helper() + + parts := strings.Split(tag, ".") + last := parts[len(parts)-1] + + // Remove potential prefix 'v' from the first part, but not from the patch + lastNum, _ := strconv.Atoi(strings.TrimPrefix(last, "v")) + lastNum++ + + parts[len(parts)-1] = strconv.Itoa(lastNum) + newTag := strings.Join(parts, ".") + + return newTag +} + +func minorTag(t *testing.T, tag string) string { + t.Helper() + + parts := strings.Split(tag, ".") + last := parts[len(parts)-1] + + lastNum, _ := strconv.Atoi(strings.TrimPrefix(last, "v")) + if lastNum > 0 { + lastNum-- + } + + parts[len(parts)-1] = strconv.Itoa(lastNum) + newTag := strings.Join(parts, ".") + + if !strings.HasPrefix(newTag, "v") { + newTag = "v" + newTag + } + return newTag +} + +func buildDockerImage(t *testing.T, dockerfile, name, arch string) { + t.Helper() + + cmd := exec.Command( + "docker", "buildx", "build", + "--platform", "linux/amd64", + "-t", name, + "-f", dockerfile, + ".", + ) + // Capture both stdout and stderr + var out bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &stderr + + err := cmd.Run() + + if err != nil { + fmt.Printf("❌ Docker build failed: %v\n", err) + fmt.Printf("---- STDERR ----\n%s\n", stderr.String()) + fmt.Printf("---- STDOUT ----\n%s\n", out.String()) + return + } + + fmt.Println("✅ Docker build succeeded!") + fmt.Println(out.String()) + // out, err := cmd.CombinedOutput() + // if err != nil { + // t.Fatalf("docker build failed: %v\nOutput:\n%s", err, string(out)) + // } + +} + +func runDockerCommand(t *testing.T, containerImageName string) { + t.Helper() + + cmd := exec.Command( + "docker", "run", "--rm", "-d", + "--privileged", + "--cgroupns=host", + "-v", "/sys/fs/cgroup:/sys/fs/cgroup:rw", + "-v", "/var/run/docker.sock:/var/run/docker.sock", + "-e", "DOCKER_HOST=unix:///var/run/docker.sock", + "--name", "apt-test-update", + containerImageName, + ) + + if err := cmd.Run(); err != nil { + t.Fatalf("failed to run container: %v", err) + } + +} + +func runDockerSystemVersion(t *testing.T, containerName string) string { + t.Helper() + + cmd := exec.Command( + "docker", "exec", + "--user", "arduino", + containerName, + "arduino-app-cli", "version", + ) + + output, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("command failed: %v\nOutput: %s", err, output) + } + + return string(output) + +} + +func runDockerSystemUpdate(t *testing.T, containerName string) { + t.Helper() + var buf bytes.Buffer + + cmd := exec.Command( + "docker", "exec", + containerName, + "sh", "-lc", + `su - arduino -c "yes | arduino-app-cli system update"`, + ) + + cmd.Stdout = io.MultiWriter(os.Stdout, &buf) + + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error running system update: %v\n", err) + os.Exit(1) + } + +} + +func runDockerCleanUp(t *testing.T, containerName string) { + t.Helper() + + cleanupCmd := exec.Command("docker", "rm", "-f", containerName) + + fmt.Println("🧹 Removing Docker container " + containerName) + if err := cleanupCmd.Run(); err != nil { + fmt.Printf("⚠️ Warning: could not remove container (might not exist): %v\n", err) + } + +} + +func moveDeb(t *testing.T, startDir, targetDir, repo string, tagVersion string, arch string) { + t.Helper() + tagPath := strings.TrimPrefix(tagVersion, "v") + + debFile := fmt.Sprintf("%s/%s_%s-1_%s.deb", startDir, repo, tagPath, arch) + + moveCmd := exec.Command("cp", debFile, targetDir) + + fmt.Printf("📦 Moving %s → %s\n", debFile, targetDir) + if err := moveCmd.Run(); err != nil { + panic(fmt.Errorf("failed to move deb file: %w", err)) + } +} diff --git a/internal/testtools/package_update.go b/internal/testtools/package_update.go new file mode 100644 index 00000000..f0fd39d7 --- /dev/null +++ b/internal/testtools/package_update.go @@ -0,0 +1,38 @@ +// This file is part of arduino-app-cli. +// +// Copyright 2025 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-app-cli. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. +package testtools + +import ( + "os" + "os/exec" + "runtime" + "testing" +) + +func DockerBuild(t *testing.T) { + + if runtime.GOOS != "linux" && os.Getenv("CI") != "" { + t.Skip("Skipping tests in CI that requires docker on non-Linux systems") + } + t.Helper() + + cmd := exec.Command("docker", "build", "-t", "adbd", "-f", "test.Dockerfile", ".") + cmd.Dir = getBaseProjectPath(t) + err := cmd.Run() + if err != nil { + t.Fatalf("failed to build adb daemon: %v", err) + } + +} diff --git a/test.Dockerfile b/internal/testtools/test.Dockerfile similarity index 98% rename from test.Dockerfile rename to internal/testtools/test.Dockerfile index e95a7689..fdc585fb 100644 --- a/test.Dockerfile +++ b/internal/testtools/test.Dockerfile @@ -6,7 +6,7 @@ RUN apt update && \ dpkg-dev apt-utils adduser gzip && \ rm -rf /var/lib/apt/lists/* -ARG ARCH=arm64 +ARG ARCH=amd64 COPY build/stable/arduino-app-cli*.deb /tmp/stable.deb COPY build/arduino-app-cli*.deb /tmp/unstable.deb From 56c433877d20a262bcabee396dba6dee451bae7f Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Tue, 11 Nov 2025 11:18:54 +0100 Subject: [PATCH 06/17] no parameters --- .github/workflows/test-update.yml | 3 +-- internal/testtools/deb_test.go | 36 +++++++++++++++++++++--------- internal/testtools/test.Dockerfile | 6 ++--- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/.github/workflows/test-update.yml b/.github/workflows/test-update.yml index 6d5b6439..4f1004f2 100644 --- a/.github/workflows/test-update.yml +++ b/.github/workflows/test-update.yml @@ -28,5 +28,4 @@ jobs: env: GH_TOKEN: ${{ secrets.ARDUINOBOT_TOKEN }} run: | - cd internal/testtools - go test -v ./deb_test.go --arch amd64 \ No newline at end of file + go test -v ./internal/testtools/deb_test.go --arch arm64 \ No newline at end of file diff --git a/internal/testtools/deb_test.go b/internal/testtools/deb_test.go index 56909dbd..d3b75340 100644 --- a/internal/testtools/deb_test.go +++ b/internal/testtools/deb_test.go @@ -25,6 +25,8 @@ func TestStableToUnstable(t *testing.T) { majorTag := majorTag(t, tagAppCli) _ = minorTag(t, tagAppCli) + ls(t) + fmt.Printf("Updating from stable version %s to unstable version %s \n", tagAppCli, majorTag) fmt.Printf("Building local deb version %s \n", majorTag) buildDebVersion(t, majorTag, *arch) @@ -173,13 +175,7 @@ func minorTag(t *testing.T, tag string) string { func buildDockerImage(t *testing.T, dockerfile, name, arch string) { t.Helper() - cmd := exec.Command( - "docker", "buildx", "build", - "--platform", "linux/amd64", - "-t", name, - "-f", dockerfile, - ".", - ) + cmd := exec.Command("docker", "build", "--build-arg", "ARCH="+arch, "-t", name, "-f", dockerfile, ".") // Capture both stdout and stderr var out bytes.Buffer var stderr bytes.Buffer @@ -197,10 +193,6 @@ func buildDockerImage(t *testing.T, dockerfile, name, arch string) { fmt.Println("✅ Docker build succeeded!") fmt.Println(out.String()) - // out, err := cmd.CombinedOutput() - // if err != nil { - // t.Fatalf("docker build failed: %v\nOutput:\n%s", err, string(out)) - // } } @@ -288,3 +280,25 @@ func moveDeb(t *testing.T, startDir, targetDir, repo string, tagVersion string, panic(fmt.Errorf("failed to move deb file: %w", err)) } } + +func ls(t *testing.T) { + t.Helper() + cwd, err := os.Getwd() + if err != nil { + fmt.Println("Error getting working directory:", err) + return + } + + fmt.Println("Current directory:", cwd) + fmt.Println("Listing all files and folders recursively:") + + // Walk through all files and subdirectories + err = filepath.Walk(cwd, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + fmt.Println(path) + return nil + }) + +} diff --git a/internal/testtools/test.Dockerfile b/internal/testtools/test.Dockerfile index fdc585fb..2beb8db8 100644 --- a/internal/testtools/test.Dockerfile +++ b/internal/testtools/test.Dockerfile @@ -8,9 +8,9 @@ RUN apt update && \ ARG ARCH=amd64 -COPY build/stable/arduino-app-cli*.deb /tmp/stable.deb -COPY build/arduino-app-cli*.deb /tmp/unstable.deb -COPY build/stable/arduino-router*.deb /tmp/router.deb +COPY build/stable/arduino-app-cli*_${ARCH}.deb /tmp/stable.deb +COPY build/arduino-app-cli*_${ARCH}.deb /tmp/unstable.deb +COPY build/stable/arduino-router*_${ARCH}.deb /tmp/router.deb RUN apt update && apt install -y /tmp/stable.deb /tmp/router.deb \ && rm /tmp/stable.deb /tmp/router.deb \ From 8d5678bd397a56b07a10309938a177ac785e2b66 Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Tue, 11 Nov 2025 12:12:10 +0100 Subject: [PATCH 07/17] define arch --- .github/workflows/test-update.yml | 2 +- internal/testtools/deb_test.go | 46 +++++++++++++++---------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/test-update.yml b/.github/workflows/test-update.yml index 4f1004f2..165a7a17 100644 --- a/.github/workflows/test-update.yml +++ b/.github/workflows/test-update.yml @@ -28,4 +28,4 @@ jobs: env: GH_TOKEN: ${{ secrets.ARDUINOBOT_TOKEN }} run: | - go test -v ./internal/testtools/deb_test.go --arch arm64 \ No newline at end of file + go test -v ./internal/testtools/deb_test.go --arch amd64 \ No newline at end of file diff --git a/internal/testtools/deb_test.go b/internal/testtools/deb_test.go index d3b75340..7e66125b 100644 --- a/internal/testtools/deb_test.go +++ b/internal/testtools/deb_test.go @@ -44,29 +44,29 @@ func TestStableToUnstable(t *testing.T) { } -// func TestUnstableToStable(t *testing.T) { -// tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) -// FetchDebPackage(t, "arduino-router", "latest", *arch) -// minorTag := minorTag(t, tagAppCli) -// moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) - -// fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) -// fmt.Printf("Building local deb version %s \n", minorTag) -// buildDebVersion(t, minorTag, *arch) -// moveDeb(t, "build/", "build/stable", "arduino-app-cli", tagAppCli, *arch) - -// fmt.Println("**** BUILD docker image *****") -// buildDockerImage(t, "test.Dockerfile", "test-apt-update") -// fmt.Println("**** RUN docker image *****") -// runDockerCommand(t, "test-apt-update") -// preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") -// runDockerSystemUpdate(t, "apt-test-update") -// postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") -// runDockerCleanUp(t, "apt-test-update") -// require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli) -// require.Equal(t, postUpdateVersion, "Arduino App CLI "+minorTag) - -// } +func TestUnstableToStable(t *testing.T) { + tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) + FetchDebPackage(t, "arduino-router", "latest", *arch) + minorTag := minorTag(t, tagAppCli) + moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) + + fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) + fmt.Printf("Building local deb version %s \n", minorTag) + buildDebVersion(t, minorTag, *arch) + moveDeb(t, "build/", "build/stable", "arduino-app-cli", tagAppCli, *arch) + + fmt.Println("**** BUILD docker image *****") + buildDockerImage(t, "test.Dockerfile", "test-apt-update", *arch) + fmt.Println("**** RUN docker image *****") + runDockerCommand(t, "test-apt-update") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerSystemUpdate(t, "apt-test-update") + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerCleanUp(t, "apt-test-update") + require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+minorTag+"\n") + +} func FetchDebPackage(t *testing.T, repo, version, arch string) string { t.Helper() From 24bce6a97b6338385147c3e2d94fe2e86d8b7815 Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Tue, 11 Nov 2025 12:32:28 +0100 Subject: [PATCH 08/17] unstable test --- internal/testtools/deb_test.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/internal/testtools/deb_test.go b/internal/testtools/deb_test.go index 7e66125b..bb1784db 100644 --- a/internal/testtools/deb_test.go +++ b/internal/testtools/deb_test.go @@ -25,12 +25,11 @@ func TestStableToUnstable(t *testing.T) { majorTag := majorTag(t, tagAppCli) _ = minorTag(t, tagAppCli) - ls(t) - fmt.Printf("Updating from stable version %s to unstable version %s \n", tagAppCli, majorTag) fmt.Printf("Building local deb version %s \n", majorTag) buildDebVersion(t, majorTag, *arch) - + fmt.Printf("Check folder structure and deb downloaded\n") + ls(t) fmt.Println("**** BUILD docker image *****") buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) fmt.Println("**** RUN docker image *****") @@ -56,9 +55,9 @@ func TestUnstableToStable(t *testing.T) { moveDeb(t, "build/", "build/stable", "arduino-app-cli", tagAppCli, *arch) fmt.Println("**** BUILD docker image *****") - buildDockerImage(t, "test.Dockerfile", "test-apt-update", *arch) + buildDockerImage(t, "test.Dockerfile", "test-apt-update-unstable", *arch) fmt.Println("**** RUN docker image *****") - runDockerCommand(t, "test-apt-update") + runDockerCommand(t, "test-apt-update-unstable") preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") runDockerSystemUpdate(t, "apt-test-update") postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") @@ -142,7 +141,6 @@ func majorTag(t *testing.T, tag string) string { parts := strings.Split(tag, ".") last := parts[len(parts)-1] - // Remove potential prefix 'v' from the first part, but not from the patch lastNum, _ := strconv.Atoi(strings.TrimPrefix(last, "v")) lastNum++ From b2ed9682ecd9c2230c455ab603407e40dec8108f Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Tue, 11 Nov 2025 14:24:06 +0100 Subject: [PATCH 09/17] delete major --- internal/testtools/deb_test.go | 57 ++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/internal/testtools/deb_test.go b/internal/testtools/deb_test.go index bb1784db..0b2f52db 100644 --- a/internal/testtools/deb_test.go +++ b/internal/testtools/deb_test.go @@ -33,7 +33,7 @@ func TestStableToUnstable(t *testing.T) { fmt.Println("**** BUILD docker image *****") buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) fmt.Println("**** RUN docker image *****") - runDockerCommand(t, "apt-test-update-image") + runDockerContainer(t, "apt-test-update", "apt-test-update-image") preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") runDockerSystemUpdate(t, "apt-test-update") postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") @@ -52,16 +52,19 @@ func TestUnstableToStable(t *testing.T) { fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) fmt.Printf("Building local deb version %s \n", minorTag) buildDebVersion(t, minorTag, *arch) - moveDeb(t, "build/", "build/stable", "arduino-app-cli", tagAppCli, *arch) + moveDeb(t, "build/", "build/stable", "arduino-app-cli", minorTag, *arch) + + fmt.Printf("Check folder structure and deb downloaded\n") + ls(t) fmt.Println("**** BUILD docker image *****") - buildDockerImage(t, "test.Dockerfile", "test-apt-update-unstable", *arch) + buildDockerImage(t, "test.Dockerfile", "test-apt-update-unstable-image", *arch) fmt.Println("**** RUN docker image *****") - runDockerCommand(t, "test-apt-update-unstable") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - runDockerSystemUpdate(t, "apt-test-update") - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - runDockerCleanUp(t, "apt-test-update") + runDockerContainer(t, "test-apt-update-unstable-image", "apt-test-update-unstable") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") + runDockerSystemUpdate(t, "apt-test-update-unstable") + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") + runDockerCleanUp(t, "apt-test-update-unstable") require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") require.Equal(t, postUpdateVersion, "Arduino App CLI "+minorTag+"\n") @@ -194,7 +197,7 @@ func buildDockerImage(t *testing.T, dockerfile, name, arch string) { } -func runDockerCommand(t *testing.T, containerImageName string) { +func runDockerContainer(t *testing.T, containerName string, containerImageName string) { t.Helper() cmd := exec.Command( @@ -204,7 +207,7 @@ func runDockerCommand(t *testing.T, containerImageName string) { "-v", "/sys/fs/cgroup:/sys/fs/cgroup:rw", "-v", "/var/run/docker.sock:/var/run/docker.sock", "-e", "DOCKER_HOST=unix:///var/run/docker.sock", - "--name", "apt-test-update", + "--name", containerName, containerImageName, ) @@ -253,6 +256,25 @@ func runDockerSystemUpdate(t *testing.T, containerName string) { } +func runDockerDaemon(t *testing.T, containerName string) string { + t.Helper() + + cmd := exec.Command( + "docker", "exec", + "--user", "arduino", + containerName, + "arduino-app-cli", "daemon", + ) + + output, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("command failed: %v\nOutput: %s", err, output) + } + + return string(output) + +} + func runDockerCleanUp(t *testing.T, containerName string) { t.Helper() @@ -277,6 +299,8 @@ func moveDeb(t *testing.T, startDir, targetDir, repo string, tagVersion string, if err := moveCmd.Run(); err != nil { panic(fmt.Errorf("failed to move deb file: %w", err)) } + + rm(t, debFile) } func ls(t *testing.T) { @@ -300,3 +324,16 @@ func ls(t *testing.T) { }) } + +func rm(t *testing.T, pathFile string) { + t.Helper() + removeCmd := exec.Command("rm", pathFile) + + err := removeCmd.Run() + if err != nil { + log.Fatalf("Failed to remove file: %v", err) + } + + fmt.Printf("📦 Removed %s\n", pathFile) + +} From a6de7b33a27851f3c29772b0dfe5b3aab9368509 Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Tue, 11 Nov 2025 14:55:46 +0100 Subject: [PATCH 10/17] Client test --- internal/testtools/deb_test.go | 124 +++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 7 deletions(-) diff --git a/internal/testtools/deb_test.go b/internal/testtools/deb_test.go index 0b2f52db..35a8ed81 100644 --- a/internal/testtools/deb_test.go +++ b/internal/testtools/deb_test.go @@ -1,11 +1,15 @@ package testtools import ( + "bufio" "bytes" + "context" "flag" "fmt" "io" + "iter" "log" + "net/http" "os" "os/exec" "path/filepath" @@ -34,12 +38,39 @@ func TestStableToUnstable(t *testing.T) { buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) fmt.Println("**** RUN docker image *****") runDockerContainer(t, "apt-test-update", "apt-test-update-image") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - runDockerSystemUpdate(t, "apt-test-update") - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + // preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + // runDockerSystemUpdate(t, "apt-test-update") + // postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + // //runDockerCleanUp(t, "apt-test-update") + // require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + // require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") +} + +func TestClientUpdate(t *testing.T) { + + fmt.Printf("Check folder structure and deb downloaded\n") + ls(t) + // fmt.Println("**** BUILD docker image *****") + // buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) + // fmt.Println("**** RUN docker image *****") + // runDockerContainer(t, "apt-test-update", "apt-test-update-image") + //Start the daemon + runDockerDaemon(t, "apt-test-update") + //PUT on the /v1/updates/apply + status := putUpdateRequest(t, "http://localhost:8080/v1/system/update/apply") + fmt.Printf("Response status: %s\n", status) + //ClientSSE + + itr := NewSSEClient(context.Background(), "GET", "http://localhost:8080/v1/system/update/apply") + + for event, err := range itr { + if err != nil { + log.Fatalf("Error receiving SSE event: %v", err) + } + fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) + } + runDockerCleanUp(t, "apt-test-update") - require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") } @@ -60,7 +91,7 @@ func TestUnstableToStable(t *testing.T) { fmt.Println("**** BUILD docker image *****") buildDockerImage(t, "test.Dockerfile", "test-apt-update-unstable-image", *arch) fmt.Println("**** RUN docker image *****") - runDockerContainer(t, "test-apt-update-unstable-image", "apt-test-update-unstable") + runDockerContainer(t, "test-apt-update-unstable", "test-apt-update-unstable-image") preUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") runDockerSystemUpdate(t, "apt-test-update-unstable") postUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") @@ -261,11 +292,11 @@ func runDockerDaemon(t *testing.T, containerName string) string { cmd := exec.Command( "docker", "exec", + "-d", // detached mode "--user", "arduino", containerName, "arduino-app-cli", "daemon", ) - output, err := cmd.CombinedOutput() if err != nil { log.Fatalf("command failed: %v\nOutput: %s", err, output) @@ -337,3 +368,82 @@ func rm(t *testing.T, pathFile string) { fmt.Printf("📦 Removed %s\n", pathFile) } + +func putUpdateRequest(t *testing.T, url string) string { + + t.Helper() + + // Create PUT request + req, err := http.NewRequest(http.MethodPut, url, nil) + if err != nil { + log.Fatalf("Error creating request: %v", err) + } + + // Optional: add headers if your API needs them + req.Header.Set("Content-Type", "application/json") + + // Send the request + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Fatalf("Error making request: %v", err) + } + defer resp.Body.Close() + + // Check status code + return resp.Status +} +func NewSSEClient(ctx context.Context, method, url string) iter.Seq2[Event, error] { + return func(yield func(Event, error) bool) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + _ = yield(Event{}, err) + return + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + _ = yield(Event{}, err) + return + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + _ = yield(Event{}, fmt.Errorf("got response status code %d", resp.StatusCode)) + return + } + + reader := bufio.NewReader(resp.Body) + + evt := Event{} + for { + line, err := reader.ReadString('\n') + if err != nil { + _ = yield(Event{}, err) + return + } + switch { + case strings.HasPrefix(line, "data:"): + evt.Data = []byte(strings.TrimSpace(strings.TrimPrefix(line, "data:"))) + case strings.HasPrefix(line, "event:"): + evt.Event = strings.TrimSpace(strings.TrimPrefix(line, "event:")) + case strings.HasPrefix(line, "id:"): + evt.ID = strings.TrimSpace(strings.TrimPrefix(line, "id:")) + case strings.HasPrefix(line, "\n"): + if !yield(evt, nil) { + return + } + evt = Event{} + default: + _ = yield(Event{}, fmt.Errorf("unknown line: '%s'", line)) + return + } + } + } +} + +type Event struct { + ID string + Event string + Data []byte // json +} From 7719bf3969ba51fd9516a9d061945a818397fba4 Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Tue, 11 Nov 2025 17:43:02 +0100 Subject: [PATCH 11/17] Client test --- internal/testtools/deb_test.go | 51 ++++++++++++++++-------------- internal/testtools/test.Dockerfile | 1 + 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/internal/testtools/deb_test.go b/internal/testtools/deb_test.go index 35a8ed81..735e3323 100644 --- a/internal/testtools/deb_test.go +++ b/internal/testtools/deb_test.go @@ -16,6 +16,7 @@ import ( "strconv" "strings" "testing" + "time" "github.com/stretchr/testify/require" ) @@ -38,36 +39,37 @@ func TestStableToUnstable(t *testing.T) { buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) fmt.Println("**** RUN docker image *****") runDockerContainer(t, "apt-test-update", "apt-test-update-image") - // preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - // runDockerSystemUpdate(t, "apt-test-update") - // postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - // //runDockerCleanUp(t, "apt-test-update") - // require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - // require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerSystemUpdate(t, "apt-test-update") + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerCleanUp(t, "apt-test-update") + require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") } func TestClientUpdate(t *testing.T) { - fmt.Printf("Check folder structure and deb downloaded\n") - ls(t) - // fmt.Println("**** BUILD docker image *****") - // buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) - // fmt.Println("**** RUN docker image *****") - // runDockerContainer(t, "apt-test-update", "apt-test-update-image") + fmt.Println("**** BUILD docker image *****") + buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) + fmt.Println("**** RUN docker image *****") + runDockerContainer(t, "apt-test-update", "apt-test-update-image") //Start the daemon runDockerDaemon(t, "apt-test-update") + time.Sleep(5 * time.Second) //wait for the daemon to be fully started //PUT on the /v1/updates/apply - status := putUpdateRequest(t, "http://localhost:8080/v1/system/update/apply") + status := putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") fmt.Printf("Response status: %s\n", status) - //ClientSSE - itr := NewSSEClient(context.Background(), "GET", "http://localhost:8080/v1/system/update/apply") + itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") for event, err := range itr { if err != nil { log.Fatalf("Error receiving SSE event: %v", err) } fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) + if string(event.Data) == "Download complete" { + break + } } runDockerCleanUp(t, "apt-test-update") @@ -233,8 +235,10 @@ func runDockerContainer(t *testing.T, containerName string, containerImageName s cmd := exec.Command( "docker", "run", "--rm", "-d", + "-p", "8800:8800", "--privileged", "--cgroupns=host", + "--network", "host", "-v", "/sys/fs/cgroup:/sys/fs/cgroup:rw", "-v", "/var/run/docker.sock:/var/run/docker.sock", "-e", "DOCKER_HOST=unix:///var/run/docker.sock", @@ -287,22 +291,22 @@ func runDockerSystemUpdate(t *testing.T, containerName string) { } -func runDockerDaemon(t *testing.T, containerName string) string { +func runDockerDaemon(t *testing.T, containerName string) { t.Helper() cmd := exec.Command( "docker", "exec", - "-d", // detached mode + "-d", "--user", "arduino", containerName, - "arduino-app-cli", "daemon", + "systemctl", "start", "arduino-app-cli", ) output, err := cmd.CombinedOutput() if err != nil { - log.Fatalf("command failed: %v\nOutput: %s", err, output) + log.Fatalf("command failed: %v\n Output: %s", err, output) } - return string(output) + fmt.Printf("Daemon started: %s\n", output) } @@ -373,23 +377,22 @@ func putUpdateRequest(t *testing.T, url string) string { t.Helper() - // Create PUT request req, err := http.NewRequest(http.MethodPut, url, nil) if err != nil { log.Fatalf("Error creating request: %v", err) } - // Optional: add headers if your API needs them req.Header.Set("Content-Type", "application/json") - // Send the request client := &http.Client{} resp, err := client.Do(req) if err != nil { - log.Fatalf("Error making request: %v", err) + log.Fatalf("Error sending request: %v", err) } defer resp.Body.Close() + fmt.Printf("Response status: %s\n", resp.Status) + // Check status code return resp.Status } diff --git a/internal/testtools/test.Dockerfile b/internal/testtools/test.Dockerfile index 2beb8db8..578e71c0 100644 --- a/internal/testtools/test.Dockerfile +++ b/internal/testtools/test.Dockerfile @@ -28,5 +28,6 @@ RUN usermod -aG docker arduino RUN echo "deb [trusted=yes arch=${ARCH}] file:/var/www/html/myrepo trixie main" \ > /etc/apt/sources.list.d/my-mock-repo.list +EXPOSE 8800 # CMD: systemd must be PID 1 CMD ["/sbin/init"] From c504ca63f1166766f6f645df78bcc5bce23e3704 Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Wed, 12 Nov 2025 10:08:55 +0100 Subject: [PATCH 12/17] using a helper file --- .github/workflows/test-update.yml | 2 +- .../testtools/test_deb_update/deb_test.go | 101 ++++++++++++++++++ .../helpers.go} | 86 --------------- .../{ => test_deb_update}/test.Dockerfile | 0 4 files changed, 102 insertions(+), 87 deletions(-) create mode 100644 internal/testtools/test_deb_update/deb_test.go rename internal/testtools/{deb_test.go => test_deb_update/helpers.go} (69%) rename internal/testtools/{ => test_deb_update}/test.Dockerfile (100%) diff --git a/.github/workflows/test-update.yml b/.github/workflows/test-update.yml index 165a7a17..ae90f7a9 100644 --- a/.github/workflows/test-update.yml +++ b/.github/workflows/test-update.yml @@ -28,4 +28,4 @@ jobs: env: GH_TOKEN: ${{ secrets.ARDUINOBOT_TOKEN }} run: | - go test -v ./internal/testtools/deb_test.go --arch amd64 \ No newline at end of file + go test -v ./internal/testtools/test_deb_update -- --arch amd64 \ No newline at end of file diff --git a/internal/testtools/test_deb_update/deb_test.go b/internal/testtools/test_deb_update/deb_test.go new file mode 100644 index 00000000..2906d906 --- /dev/null +++ b/internal/testtools/test_deb_update/deb_test.go @@ -0,0 +1,101 @@ +package testtools + +import ( + "context" + "flag" + "fmt" + "log" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +var arch = flag.String("arch", "amd64", "target architecture") + +func TestStableToUnstable(t *testing.T) { + fmt.Printf("***** ARCH %s ***** \n", *arch) + tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) + FetchDebPackage(t, "arduino-router", "latest", *arch) + majorTag := majorTag(t, tagAppCli) + _ = minorTag(t, tagAppCli) + + fmt.Printf("Updating from stable version %s to unstable version %s \n", tagAppCli, majorTag) + fmt.Printf("Building local deb version %s \n", majorTag) + buildDebVersion(t, majorTag, *arch) + // fmt.Printf("Check folder structure and deb downloaded\n") + // ls(t) + fmt.Println("**** BUILD docker image *****") + buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) + fmt.Println("**** RUN docker image *****") + runDockerContainer(t, "apt-test-update", "apt-test-update-image") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerSystemUpdate(t, "apt-test-update") + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerCleanUp(t, "apt-test-update") + require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") +} + +func TestClientUpdate(t *testing.T) { + + fmt.Printf("***** ARCH %s ***** \n", *arch) + tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) + FetchDebPackage(t, "arduino-router", "latest", *arch) + majorTag := majorTag(t, tagAppCli) + + fmt.Println("**** RUN docker image *****") + runDockerContainer(t, "apt-test-update", "apt-test-update-image") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + + runDockerDaemon(t, "apt-test-update") + time.Sleep(5 * time.Second) //wait for the daemon to be fully started + status := putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") + fmt.Printf("Response status: %s\n", status) + + itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") + + for event, err := range itr { + if err != nil { + log.Printf("Error receiving SSE event: %v", err) + } + fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) + if string(event.Data) == "Download complete" { + fmt.Println("✅ Download complete — exiting successfully.") + } + } + + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + + require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") + runDockerCleanUp(t, "apt-test-update") + +} + +func TestUnstableToStable(t *testing.T) { + tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) + FetchDebPackage(t, "arduino-router", "latest", *arch) + minorTag := minorTag(t, tagAppCli) + moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) + + fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) + fmt.Printf("Building local deb version %s \n", minorTag) + buildDebVersion(t, minorTag, *arch) + moveDeb(t, "build/", "build/stable", "arduino-app-cli", minorTag, *arch) + + fmt.Printf("Check folder structure and deb downloaded\n") + ls(t) + + fmt.Println("**** BUILD docker image *****") + buildDockerImage(t, "test.Dockerfile", "test-apt-update-unstable-image", *arch) + fmt.Println("**** RUN docker image *****") + runDockerContainer(t, "apt-test-update-unstable", "test-apt-update-unstable-image") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") + runDockerSystemUpdate(t, "apt-test-update-unstable") + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") + runDockerCleanUp(t, "apt-test-update-unstable") + require.Equal(t, preUpdateVersion, "Arduino App CLI "+minorTag+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + +} diff --git a/internal/testtools/deb_test.go b/internal/testtools/test_deb_update/helpers.go similarity index 69% rename from internal/testtools/deb_test.go rename to internal/testtools/test_deb_update/helpers.go index 735e3323..58b71452 100644 --- a/internal/testtools/deb_test.go +++ b/internal/testtools/test_deb_update/helpers.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "context" - "flag" "fmt" "io" "iter" @@ -16,93 +15,8 @@ import ( "strconv" "strings" "testing" - "time" - - "github.com/stretchr/testify/require" ) -var arch = flag.String("arch", "amd64", "target architecture") - -func TestStableToUnstable(t *testing.T) { - fmt.Printf("***** ARCH %s ***** \n", *arch) - tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) - FetchDebPackage(t, "arduino-router", "latest", *arch) - majorTag := majorTag(t, tagAppCli) - _ = minorTag(t, tagAppCli) - - fmt.Printf("Updating from stable version %s to unstable version %s \n", tagAppCli, majorTag) - fmt.Printf("Building local deb version %s \n", majorTag) - buildDebVersion(t, majorTag, *arch) - fmt.Printf("Check folder structure and deb downloaded\n") - ls(t) - fmt.Println("**** BUILD docker image *****") - buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) - fmt.Println("**** RUN docker image *****") - runDockerContainer(t, "apt-test-update", "apt-test-update-image") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - runDockerSystemUpdate(t, "apt-test-update") - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - runDockerCleanUp(t, "apt-test-update") - require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") -} - -func TestClientUpdate(t *testing.T) { - - fmt.Println("**** BUILD docker image *****") - buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) - fmt.Println("**** RUN docker image *****") - runDockerContainer(t, "apt-test-update", "apt-test-update-image") - //Start the daemon - runDockerDaemon(t, "apt-test-update") - time.Sleep(5 * time.Second) //wait for the daemon to be fully started - //PUT on the /v1/updates/apply - status := putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") - fmt.Printf("Response status: %s\n", status) - - itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") - - for event, err := range itr { - if err != nil { - log.Fatalf("Error receiving SSE event: %v", err) - } - fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) - if string(event.Data) == "Download complete" { - break - } - } - - runDockerCleanUp(t, "apt-test-update") - -} - -func TestUnstableToStable(t *testing.T) { - tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) - FetchDebPackage(t, "arduino-router", "latest", *arch) - minorTag := minorTag(t, tagAppCli) - moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) - - fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) - fmt.Printf("Building local deb version %s \n", minorTag) - buildDebVersion(t, minorTag, *arch) - moveDeb(t, "build/", "build/stable", "arduino-app-cli", minorTag, *arch) - - fmt.Printf("Check folder structure and deb downloaded\n") - ls(t) - - fmt.Println("**** BUILD docker image *****") - buildDockerImage(t, "test.Dockerfile", "test-apt-update-unstable-image", *arch) - fmt.Println("**** RUN docker image *****") - runDockerContainer(t, "test-apt-update-unstable", "test-apt-update-unstable-image") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") - runDockerSystemUpdate(t, "apt-test-update-unstable") - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") - runDockerCleanUp(t, "apt-test-update-unstable") - require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+minorTag+"\n") - -} - func FetchDebPackage(t *testing.T, repo, version, arch string) string { t.Helper() diff --git a/internal/testtools/test.Dockerfile b/internal/testtools/test_deb_update/test.Dockerfile similarity index 100% rename from internal/testtools/test.Dockerfile rename to internal/testtools/test_deb_update/test.Dockerfile From 37b71dcebcb2fa0042e5836eb011b0a987659457 Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Wed, 12 Nov 2025 12:11:36 +0100 Subject: [PATCH 13/17] using run --- .../testtools/test_deb_update/deb_test.go | 164 +++++++++++------- internal/testtools/test_deb_update/helpers.go | 20 +++ 2 files changed, 124 insertions(+), 60 deletions(-) diff --git a/internal/testtools/test_deb_update/deb_test.go b/internal/testtools/test_deb_update/deb_test.go index 2906d906..05465f69 100644 --- a/internal/testtools/test_deb_update/deb_test.go +++ b/internal/testtools/test_deb_update/deb_test.go @@ -25,77 +25,121 @@ func TestStableToUnstable(t *testing.T) { buildDebVersion(t, majorTag, *arch) // fmt.Printf("Check folder structure and deb downloaded\n") // ls(t) - fmt.Println("**** BUILD docker image *****") - buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) - fmt.Println("**** RUN docker image *****") - runDockerContainer(t, "apt-test-update", "apt-test-update-image") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - runDockerSystemUpdate(t, "apt-test-update") - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - runDockerCleanUp(t, "apt-test-update") - require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") + // fmt.Println("**** BUILD docker image *****") + // buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) + // fmt.Println("**** RUN docker image *****") + // runDockerContainer(t, "apt-test-update", "apt-test-update-image") + // preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + // runDockerSystemUpdate(t, "apt-test-update") + // postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + // runDockerCleanUp(t, "apt-test-update") + // require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + // require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") } -func TestClientUpdate(t *testing.T) { +// func TestClientUpdateStU(t *testing.T) { - fmt.Printf("***** ARCH %s ***** \n", *arch) - tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) - FetchDebPackage(t, "arduino-router", "latest", *arch) - majorTag := majorTag(t, tagAppCli) +// fmt.Printf("***** ARCH %s ***** \n", *arch) +// tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) +// FetchDebPackage(t, "arduino-router", "latest", *arch) +// majorTag := majorTag(t, tagAppCli) - fmt.Println("**** RUN docker image *****") - runDockerContainer(t, "apt-test-update", "apt-test-update-image") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") +// fmt.Println("**** RUN docker image *****") +// runDockerContainer(t, "apt-test-update", "apt-test-update-image") +// preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - runDockerDaemon(t, "apt-test-update") - time.Sleep(5 * time.Second) //wait for the daemon to be fully started - status := putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") - fmt.Printf("Response status: %s\n", status) +// runDockerDaemon(t, "apt-test-update") +// WaitForPort(t, "127.0.0.1", 8800, 5*time.Second) - itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") +// status := putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") +// fmt.Printf("Response status: %s\n", status) - for event, err := range itr { - if err != nil { - log.Printf("Error receiving SSE event: %v", err) - } - fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) - if string(event.Data) == "Download complete" { - fmt.Println("✅ Download complete — exiting successfully.") - } - } +// itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") +// for event, err := range itr { +// if err != nil { +// log.Printf("Error receiving SSE event: %v", err) +// } +// fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) +// if string(event.Data) == "Download complete" { +// fmt.Println("✅ Download complete — exiting successfully.") +// } +// } - require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") - runDockerCleanUp(t, "apt-test-update") +// postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") -} +// require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") +// require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") +// runDockerCleanUp(t, "apt-test-update") + +// } func TestUnstableToStable(t *testing.T) { - tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) - FetchDebPackage(t, "arduino-router", "latest", *arch) - minorTag := minorTag(t, tagAppCli) - moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) - - fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) - fmt.Printf("Building local deb version %s \n", minorTag) - buildDebVersion(t, minorTag, *arch) - moveDeb(t, "build/", "build/stable", "arduino-app-cli", minorTag, *arch) - - fmt.Printf("Check folder structure and deb downloaded\n") - ls(t) - - fmt.Println("**** BUILD docker image *****") - buildDockerImage(t, "test.Dockerfile", "test-apt-update-unstable-image", *arch) - fmt.Println("**** RUN docker image *****") - runDockerContainer(t, "apt-test-update-unstable", "test-apt-update-unstable-image") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") - runDockerSystemUpdate(t, "apt-test-update-unstable") - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") - runDockerCleanUp(t, "apt-test-update-unstable") - require.Equal(t, preUpdateVersion, "Arduino App CLI "+minorTag+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + + t.Run("CLI Update Testing", func(t *testing.T) { + tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) + FetchDebPackage(t, "arduino-router", "latest", *arch) + minorTag := minorTag(t, tagAppCli) + //Move the stable package to the build (unstable) folder + moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) + fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) + fmt.Printf("Building local deb version %s \n", minorTag) + //Build unstable with a minor tag w.r.t stable + buildDebVersion(t, minorTag, *arch) + //Move the unstable package to the stable folder + moveDeb(t, "build/", "build/stable", "arduino-app-cli", minorTag, *arch) + fmt.Printf("Check folder structure and deb downloaded\n") + ls(t) + fmt.Println("**** BUILD docker image *****") + buildDockerImage(t, "test.Dockerfile", "test-apt-update-unstable-image", *arch) + fmt.Println("**** RUN docker image *****") + runDockerContainer(t, "apt-test-update-unstable", "test-apt-update-unstable-image") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") + runDockerSystemUpdate(t, "apt-test-update-unstable") + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") + runDockerCleanUp(t, "apt-test-update-unstable") + require.Equal(t, preUpdateVersion, "Arduino App CLI "+minorTag+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + }) + + t.Run("Client Daemon Request Testing", func(t *testing.T) { + tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) + FetchDebPackage(t, "arduino-router", "latest", *arch) + minorTag := minorTag(t, tagAppCli) + //Move the stable package to the build (unstable) folder + moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) + fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) + fmt.Printf("Building local deb version %s \n", minorTag) + //Build unstable with a minor tag w.r.t stable + buildDebVersion(t, minorTag, *arch) + //Move the unstable package to the stable folder + moveDeb(t, "build/", "build/stable", "arduino-app-cli", minorTag, *arch) + fmt.Printf("Check folder structure and deb downloaded\n") + fmt.Println("**** RUN docker image *****") + runDockerContainer(t, "apt-test-update-unstable", "test-apt-update-unstable-image") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") + runDockerDaemon(t, "apt-test-update-unstable ") + WaitForPort(t, "127.0.0.1", 8800, 5*time.Second) + status := putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") + fmt.Printf("Response status: %s\n", status) + + itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") + + for event, err := range itr { + if err != nil { + log.Printf("Error receiving SSE event: %v", err) + } + fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) + if string(event.Data) == "Download complete" { + fmt.Println("✅ Download complete — exiting successfully.") + } + } + + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerCleanUp(t, "apt-test-update-unstable") + require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+minorTag+"\n") + + }) } diff --git a/internal/testtools/test_deb_update/helpers.go b/internal/testtools/test_deb_update/helpers.go index 58b71452..2400f3a7 100644 --- a/internal/testtools/test_deb_update/helpers.go +++ b/internal/testtools/test_deb_update/helpers.go @@ -8,6 +8,7 @@ import ( "io" "iter" "log" + "net" "net/http" "os" "os/exec" @@ -15,6 +16,7 @@ import ( "strconv" "strings" "testing" + "time" ) func FetchDebPackage(t *testing.T, repo, version, arch string) string { @@ -364,3 +366,21 @@ type Event struct { Event string Data []byte // json } + +// WaitForPort waits until a TCP port is open or fails after timeout. +func WaitForPort(t *testing.T, host string, port int, timeout time.Duration) { + t.Helper() + addr := fmt.Sprintf("%s:%d", host, port) + + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + conn, err := net.DialTimeout("tcp", addr, 500*time.Millisecond) + if err == nil { + _ = conn.Close() + t.Logf("Server is up on %s", addr) + return + } + time.Sleep(200 * time.Millisecond) + } + t.Fatalf("Server at %s did not start within %v", addr, timeout) +} From 380c48c031af7caff72b7876b62e8c67ae38b7d4 Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Wed, 12 Nov 2025 15:29:56 +0100 Subject: [PATCH 14/17] path in the building --- .../testtools/test_deb_update/deb_test.go | 154 +++++++++--------- internal/testtools/test_deb_update/helpers.go | 22 +-- 2 files changed, 84 insertions(+), 92 deletions(-) diff --git a/internal/testtools/test_deb_update/deb_test.go b/internal/testtools/test_deb_update/deb_test.go index 05465f69..3879c3ec 100644 --- a/internal/testtools/test_deb_update/deb_test.go +++ b/internal/testtools/test_deb_update/deb_test.go @@ -15,83 +15,87 @@ var arch = flag.String("arch", "amd64", "target architecture") func TestStableToUnstable(t *testing.T) { fmt.Printf("***** ARCH %s ***** \n", *arch) - tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) - FetchDebPackage(t, "arduino-router", "latest", *arch) + tagAppCli := FetchDebPackage(t, "build/stable", "arduino-app-cli", "latest", *arch) + FetchDebPackage(t, "build/stable", "arduino-router", "latest", *arch) majorTag := majorTag(t, tagAppCli) _ = minorTag(t, tagAppCli) - fmt.Printf("Updating from stable version %s to unstable version %s \n", tagAppCli, majorTag) fmt.Printf("Building local deb version %s \n", majorTag) - buildDebVersion(t, majorTag, *arch) - // fmt.Printf("Check folder structure and deb downloaded\n") - // ls(t) - // fmt.Println("**** BUILD docker image *****") - // buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) - // fmt.Println("**** RUN docker image *****") - // runDockerContainer(t, "apt-test-update", "apt-test-update-image") - // preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - // runDockerSystemUpdate(t, "apt-test-update") - // postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - // runDockerCleanUp(t, "apt-test-update") - // require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - // require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") + buildDebVersion(t, "build", majorTag, *arch) + fmt.Printf("Check folder structure and deb downloaded\n") + ls(t) + fmt.Println("**** BUILD docker image *****") + buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) + fmt.Println("**** RUN docker image *****") + runDockerContainer(t, "apt-test-update", "apt-test-update-image") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerSystemUpdate(t, "apt-test-update") + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerCleanUp(t, "apt-test-update") + require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") } -// func TestClientUpdateStU(t *testing.T) { +func TestClientUpdateStU(t *testing.T) { -// fmt.Printf("***** ARCH %s ***** \n", *arch) -// tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) -// FetchDebPackage(t, "arduino-router", "latest", *arch) -// majorTag := majorTag(t, tagAppCli) + fmt.Printf("***** ARCH %s ***** \n", *arch) + tagAppCli := FetchDebPackage(t, "build/stable", "arduino-app-cli", "latest", *arch) + FetchDebPackage(t, "build/stable", "arduino-router", "latest", *arch) + majorTag := majorTag(t, tagAppCli) -// fmt.Println("**** RUN docker image *****") -// runDockerContainer(t, "apt-test-update", "apt-test-update-image") -// preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + fmt.Println("**** RUN docker image *****") + runDockerContainer(t, "apt-test-update", "apt-test-update-image") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") -// runDockerDaemon(t, "apt-test-update") -// WaitForPort(t, "127.0.0.1", 8800, 5*time.Second) + status := runDockerDaemon(t, "apt-test-update") + WaitForPort(t, "127.0.0.1", "8800", 5*time.Second) -// status := putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") -// fmt.Printf("Response status: %s\n", status) + status = putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") + if status != "202 Accepted" { + log.Fatalf("Error putting update request: %s", status) + } -// itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") + itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") -// for event, err := range itr { -// if err != nil { -// log.Printf("Error receiving SSE event: %v", err) -// } -// fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) -// if string(event.Data) == "Download complete" { -// fmt.Println("✅ Download complete — exiting successfully.") -// } -// } + for event, err := range itr { + if err != nil { + log.Printf("Error receiving SSE event: %v", err) + } + fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) + if string(event.Data) == "Download complete" { + fmt.Println("✅ Download complete — exiting successfully.") + break + } + } -// postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") -// require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") -// require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") -// runDockerCleanUp(t, "apt-test-update") + require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") + runDockerCleanUp(t, "apt-test-update") -// } +} func TestUnstableToStable(t *testing.T) { + fmt.Printf("***** ARCH %s ***** \n", *arch) + tagAppCli := FetchDebPackage(t, "build", "arduino-app-cli", "latest", *arch) + FetchDebPackage(t, "build/stable", "arduino-router", "latest", *arch) + minorTag := minorTag(t, tagAppCli) + //Move the stable package to the build (unstable) folder + //moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) + fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) + fmt.Printf("Building local deb version %s \n", minorTag) + //Build unstable with a minor tag w.r.t stable + buildDebVersion(t, "build/stable", minorTag, *arch) + //Move the unstable package to the stable folder + //moveDeb(t, "build/", "build/stable", "arduino-app-cli", minorTag, *arch) + //fmt.Printf("Check folder structure and deb downloaded\n") + //ls(t) + fmt.Println("**** BUILD docker image *****") + buildDockerImage(t, "test.Dockerfile", "test-apt-update-unstable-image", *arch) t.Run("CLI Update Testing", func(t *testing.T) { - tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) - FetchDebPackage(t, "arduino-router", "latest", *arch) - minorTag := minorTag(t, tagAppCli) - //Move the stable package to the build (unstable) folder - moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) - fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) - fmt.Printf("Building local deb version %s \n", minorTag) - //Build unstable with a minor tag w.r.t stable - buildDebVersion(t, minorTag, *arch) - //Move the unstable package to the stable folder - moveDeb(t, "build/", "build/stable", "arduino-app-cli", minorTag, *arch) - fmt.Printf("Check folder structure and deb downloaded\n") - ls(t) - fmt.Println("**** BUILD docker image *****") - buildDockerImage(t, "test.Dockerfile", "test-apt-update-unstable-image", *arch) + fmt.Println("**** RUN docker image *****") runDockerContainer(t, "apt-test-update-unstable", "test-apt-update-unstable-image") preUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") @@ -103,25 +107,16 @@ func TestUnstableToStable(t *testing.T) { }) t.Run("Client Daemon Request Testing", func(t *testing.T) { - tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) - FetchDebPackage(t, "arduino-router", "latest", *arch) - minorTag := minorTag(t, tagAppCli) - //Move the stable package to the build (unstable) folder - moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) - fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) - fmt.Printf("Building local deb version %s \n", minorTag) - //Build unstable with a minor tag w.r.t stable - buildDebVersion(t, minorTag, *arch) - //Move the unstable package to the stable folder - moveDeb(t, "build/", "build/stable", "arduino-app-cli", minorTag, *arch) - fmt.Printf("Check folder structure and deb downloaded\n") fmt.Println("**** RUN docker image *****") runDockerContainer(t, "apt-test-update-unstable", "test-apt-update-unstable-image") preUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") - runDockerDaemon(t, "apt-test-update-unstable ") - WaitForPort(t, "127.0.0.1", 8800, 5*time.Second) - status := putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") - fmt.Printf("Response status: %s\n", status) + status := runDockerDaemon(t, "apt-test-update-unstable") + WaitForPort(t, "127.0.0.1", "8800", 5*time.Second) + + status = putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") + if status != "202 Accepted" { + log.Fatalf("Error putting update request: %s", status) + } itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") @@ -130,15 +125,16 @@ func TestUnstableToStable(t *testing.T) { log.Printf("Error receiving SSE event: %v", err) } fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) - if string(event.Data) == "Download complete" { - fmt.Println("✅ Download complete — exiting successfully.") + if string(event.Data) == "Upgrade completed successfully" { + fmt.Println("✅ exiting successfully.") + break } } - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") runDockerCleanUp(t, "apt-test-update-unstable") - require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+minorTag+"\n") + require.Equal(t, preUpdateVersion, "Arduino App CLI "+minorTag+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") }) diff --git a/internal/testtools/test_deb_update/helpers.go b/internal/testtools/test_deb_update/helpers.go index 2400f3a7..39ec4a37 100644 --- a/internal/testtools/test_deb_update/helpers.go +++ b/internal/testtools/test_deb_update/helpers.go @@ -19,7 +19,7 @@ import ( "time" ) -func FetchDebPackage(t *testing.T, repo, version, arch string) string { +func FetchDebPackage(t *testing.T, path, repo, version, arch string) string { t.Helper() cmd := exec.Command( @@ -43,7 +43,7 @@ func FetchDebPackage(t *testing.T, repo, version, arch string) string { tag := fields[0] tagPath := strings.TrimPrefix(tag, "v") - debFile := fmt.Sprintf("build/stable/%s_%s-1_%s.deb", repo, tagPath, arch) + debFile := fmt.Sprintf("%s/%s_%s-1_%s.deb", path, repo, tagPath, arch) fmt.Println(debFile) if _, err := os.Stat(debFile); err == nil { fmt.Printf("✅ %s already exists, skipping download.\n", debFile) @@ -55,7 +55,7 @@ func FetchDebPackage(t *testing.T, repo, version, arch string) string { tag, "--repo", "github.com/arduino/"+repo, "--pattern", "*", - "--dir", "./build/stable", + "--dir", path, ) out, err := cmd2.CombinedOutput() @@ -67,13 +67,13 @@ func FetchDebPackage(t *testing.T, repo, version, arch string) string { } -func buildDebVersion(t *testing.T, tagVersion, arch string) { +func buildDebVersion(t *testing.T, storePath, tagVersion, arch string) { t.Helper() cwd, err := os.Getwd() if err != nil { panic(err) } - outputDir := filepath.Join(cwd, "build") + outputDir := filepath.Join(cwd, storePath) cmd := exec.Command( "go", "tool", "task", "build-deb", @@ -207,7 +207,7 @@ func runDockerSystemUpdate(t *testing.T, containerName string) { } -func runDockerDaemon(t *testing.T, containerName string) { +func runDockerDaemon(t *testing.T, containerName string) string { t.Helper() cmd := exec.Command( @@ -222,8 +222,7 @@ func runDockerDaemon(t *testing.T, containerName string) { log.Fatalf("command failed: %v\n Output: %s", err, output) } - fmt.Printf("Daemon started: %s\n", output) - + return string(output) } func runDockerCleanUp(t *testing.T, containerName string) { @@ -307,9 +306,6 @@ func putUpdateRequest(t *testing.T, url string) string { } defer resp.Body.Close() - fmt.Printf("Response status: %s\n", resp.Status) - - // Check status code return resp.Status } func NewSSEClient(ctx context.Context, method, url string) iter.Seq2[Event, error] { @@ -368,9 +364,9 @@ type Event struct { } // WaitForPort waits until a TCP port is open or fails after timeout. -func WaitForPort(t *testing.T, host string, port int, timeout time.Duration) { +func WaitForPort(t *testing.T, host string, port string, timeout time.Duration) { t.Helper() - addr := fmt.Sprintf("%s:%d", host, port) + addr := fmt.Sprintf("%s:%s", host, port) deadline := time.Now().Add(timeout) for time.Now().Before(deadline) { From fb890be9bc00e2018a8dc9038975a43c063d82e0 Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Wed, 12 Nov 2025 15:42:09 +0100 Subject: [PATCH 15/17] test rebase --- .github/workflows/test-update.yml | 1 + .../testtools/test_deb_update/deb_test.go | 82 +++++++++---------- internal/testtools/test_deb_update/helpers.go | 24 ++++++ 3 files changed, 65 insertions(+), 42 deletions(-) diff --git a/.github/workflows/test-update.yml b/.github/workflows/test-update.yml index ae90f7a9..0bb9b04a 100644 --- a/.github/workflows/test-update.yml +++ b/.github/workflows/test-update.yml @@ -5,6 +5,7 @@ on: branches: - main - test_package_update + - test_package_update_rebase workflow_dispatch: permissions: diff --git a/internal/testtools/test_deb_update/deb_test.go b/internal/testtools/test_deb_update/deb_test.go index 3879c3ec..9746fd9d 100644 --- a/internal/testtools/test_deb_update/deb_test.go +++ b/internal/testtools/test_deb_update/deb_test.go @@ -19,61 +19,59 @@ func TestStableToUnstable(t *testing.T) { FetchDebPackage(t, "build/stable", "arduino-router", "latest", *arch) majorTag := majorTag(t, tagAppCli) _ = minorTag(t, tagAppCli) + fmt.Printf("Updating from stable version %s to unstable version %s \n", tagAppCli, majorTag) fmt.Printf("Building local deb version %s \n", majorTag) buildDebVersion(t, "build", majorTag, *arch) - fmt.Printf("Check folder structure and deb downloaded\n") - ls(t) - fmt.Println("**** BUILD docker image *****") - buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) - fmt.Println("**** RUN docker image *****") - runDockerContainer(t, "apt-test-update", "apt-test-update-image") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - runDockerSystemUpdate(t, "apt-test-update") - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - runDockerCleanUp(t, "apt-test-update") - require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") -} -func TestClientUpdateStU(t *testing.T) { + t.Run("CLI Update Testing", func(t *testing.T) { - fmt.Printf("***** ARCH %s ***** \n", *arch) - tagAppCli := FetchDebPackage(t, "build/stable", "arduino-app-cli", "latest", *arch) - FetchDebPackage(t, "build/stable", "arduino-router", "latest", *arch) - majorTag := majorTag(t, tagAppCli) + fmt.Printf("Check folder structure and deb downloaded\n") + ls(t) + fmt.Println("**** BUILD docker image *****") + buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) + fmt.Println("**** RUN docker image *****") + runDockerContainer(t, "apt-test-update", "apt-test-update-image") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerSystemUpdate(t, "apt-test-update") + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + runDockerCleanUp(t, "apt-test-update") + require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") + }) - fmt.Println("**** RUN docker image *****") - runDockerContainer(t, "apt-test-update", "apt-test-update-image") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + t.Run("Client Daemon Request Testing", func(t *testing.T) { + runDockerContainer(t, "apt-test-update", "apt-test-update-image") + preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - status := runDockerDaemon(t, "apt-test-update") - WaitForPort(t, "127.0.0.1", "8800", 5*time.Second) + status := runDockerDaemon(t, "apt-test-update") + WaitForPort(t, "127.0.0.1", "8800", 5*time.Second) - status = putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") - if status != "202 Accepted" { - log.Fatalf("Error putting update request: %s", status) - } + status = putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") + if status != "202 Accepted" { + log.Fatalf("Error putting update request: %s", status) + } - itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") + itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") - for event, err := range itr { - if err != nil { - log.Printf("Error receiving SSE event: %v", err) - } - fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) - if string(event.Data) == "Download complete" { - fmt.Println("✅ Download complete — exiting successfully.") - break + for event, err := range itr { + if err != nil { + log.Printf("Error receiving SSE event: %v", err) + } + fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) + if string(event.Data) == "Download complete" { + fmt.Println("✅ Download complete — exiting successfully.") + break + } } - } - - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") - runDockerCleanUp(t, "apt-test-update") + postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") + require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") + require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") + runDockerCleanUp(t, "apt-test-update") + }) + rmrf(t, "build") } func TestUnstableToStable(t *testing.T) { diff --git a/internal/testtools/test_deb_update/helpers.go b/internal/testtools/test_deb_update/helpers.go index 39ec4a37..3cf76e46 100644 --- a/internal/testtools/test_deb_update/helpers.go +++ b/internal/testtools/test_deb_update/helpers.go @@ -308,6 +308,30 @@ func putUpdateRequest(t *testing.T, url string) string { return resp.Status } + +func rmrf(t *testing.T, pathFile string) { + t.Helper() + // Check if the folder exists + if _, err := os.Stat(pathFile); os.IsNotExist(err) { + fmt.Println("No build directory found.") + return + } + + // Run the Linux command to remove it + cmd := exec.Command("rm", "-rf", pathFile) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + fmt.Println("Removing build directory...") + + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error removing build folder: %v\n", err) + os.Exit(1) + } + + fmt.Println("Build directory removed successfully.") +} + func NewSSEClient(ctx context.Context, method, url string) iter.Seq2[Event, error] { return func(yield func(Event, error) bool) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) From d359c2f43326856a818a455e8cc9cd9d12b971fb Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Wed, 12 Nov 2025 18:20:46 +0100 Subject: [PATCH 16/17] this is sedia refactor --- .github/workflows/test-update.yml | 5 +- internal/e2e/updatetest/deb_test.go | 122 +++++++++++ .../updatetest}/helpers.go | 197 ++++++------------ .../updatetest}/test.Dockerfile | 0 .../testtools/test_deb_update/deb_test.go | 139 ------------ 5 files changed, 186 insertions(+), 277 deletions(-) create mode 100644 internal/e2e/updatetest/deb_test.go rename internal/{testtools/test_deb_update => e2e/updatetest}/helpers.go (57%) rename internal/{testtools/test_deb_update => e2e/updatetest}/test.Dockerfile (100%) delete mode 100644 internal/testtools/test_deb_update/deb_test.go diff --git a/.github/workflows/test-update.yml b/.github/workflows/test-update.yml index 0bb9b04a..9d452fbf 100644 --- a/.github/workflows/test-update.yml +++ b/.github/workflows/test-update.yml @@ -11,7 +11,6 @@ on: permissions: contents: read - jobs: build-and-update: runs-on: ubuntu-22.04 @@ -27,6 +26,6 @@ jobs: - name: Run dep package update test env: - GH_TOKEN: ${{ secrets.ARDUINOBOT_TOKEN }} + GH_TOKEN: ${{ secrets.ARDUINOBOT_TOKEN }} run: | - go test -v ./internal/testtools/test_deb_update -- --arch amd64 \ No newline at end of file + go test -count=1 --timeout 30m -v ./internal/e2e/updatetest -- --arch amd64 diff --git a/internal/e2e/updatetest/deb_test.go b/internal/e2e/updatetest/deb_test.go new file mode 100644 index 00000000..2e3cd461 --- /dev/null +++ b/internal/e2e/updatetest/deb_test.go @@ -0,0 +1,122 @@ +package updatetest + +import ( + "flag" + "fmt" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +var arch = flag.String("arch", "amd64", "target architecture") + +const dockerFile = "test.Dockerfile" +const daemonHost = "127.0.0.1:8800" + +func TestUpdatePackage(t *testing.T) { + fmt.Printf("***** ARCH %s ***** \n", *arch) + + t.Run("Stable To Current", func(t *testing.T) { + t.Cleanup(func() { os.RemoveAll("build") }) + + tagAppCli := fetchDebPackageLatest(t, "build/stable", "arduino-app-cli") + fetchDebPackageLatest(t, "build/stable", "arduino-router") + majorTag := genMajorTag(t, tagAppCli) + + fmt.Printf("Updating from stable version %s to unstable version %s \n", tagAppCli, majorTag) + fmt.Printf("Building local deb version %s \n", majorTag) + buildDebVersion(t, "build", majorTag, *arch) + + const dockerImageName = "apt-test-update-image" + fmt.Println("**** BUILD docker image *****") + buildDockerImage(t, dockerFile, dockerImageName, *arch) + //TODO: t cleanup remove docker image + + t.Run("CLI Command", func(t *testing.T) { + const containerName = "apt-test-update" + t.Cleanup(func() { stopDockerContainer(t, containerName) }) + + fmt.Println("**** RUN docker image *****") + startDockerContainer(t, containerName, dockerImageName) + waitForPort(t, daemonHost, 5*time.Second) + + preUpdateVersion := getAppCliVersion(t, containerName) + require.Equal(t, "v"+preUpdateVersion, tagAppCli) + runSystemUpdate(t, containerName) + postUpdateVersion := getAppCliVersion(t, containerName) + require.Equal(t, "v"+postUpdateVersion, majorTag) + }) + + t.Run("HTTP Request", func(t *testing.T) { + const containerName = "apt-test-update-http" + t.Cleanup(func() { stopDockerContainer(t, containerName) }) + + startDockerContainer(t, containerName, dockerImageName) + waitForPort(t, daemonHost, 5*time.Second) + + preUpdateVersion := getAppCliVersion(t, containerName) + require.Equal(t, "v"+preUpdateVersion, tagAppCli) + + putUpdateRequest(t, daemonHost) + waitForUpgrade(t, daemonHost) + + postUpdateVersion := getAppCliVersion(t, containerName) + require.Equal(t, "v"+postUpdateVersion, majorTag) + }) + + }) + + t.Run("CurrentToStable", func(t *testing.T) { + t.Cleanup(func() { os.RemoveAll("build") }) + + tagAppCli := fetchDebPackageLatest(t, "build", "arduino-app-cli") + fetchDebPackageLatest(t, "build/stable", "arduino-router") + minorTag := genMinorTag(t, tagAppCli) + + fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) + fmt.Printf("Building local deb version %s \n", minorTag) + buildDebVersion(t, "build/stable", minorTag, *arch) + + fmt.Println("**** BUILD docker image *****") + const dockerImageName = "test-apt-update-unstable-image" + + buildDockerImage(t, dockerFile, dockerImageName, *arch) + //TODO: t cleanup remove docker image + + t.Run("CLI Command", func(t *testing.T) { + const containerName = "apt-test-update-unstable" + t.Cleanup(func() { stopDockerContainer(t, containerName) }) + + fmt.Println("**** RUN docker image *****") + startDockerContainer(t, containerName, dockerImageName) + waitForPort(t, daemonHost, 5*time.Second) + + preUpdateVersion := getAppCliVersion(t, containerName) + require.Equal(t, "v"+preUpdateVersion, minorTag) + runSystemUpdate(t, containerName) + postUpdateVersion := getAppCliVersion(t, containerName) + require.Equal(t, "v"+postUpdateVersion, tagAppCli) + }) + + t.Run("HTTP Request", func(t *testing.T) { + const containerName = "apt-test-update--unstable-http" + t.Cleanup(func() { stopDockerContainer(t, containerName) }) + + startDockerContainer(t, containerName, dockerImageName) + waitForPort(t, daemonHost, 5*time.Second) + + preUpdateVersion := getAppCliVersion(t, containerName) + require.Equal(t, "v"+preUpdateVersion, minorTag) + + putUpdateRequest(t, daemonHost) + waitForUpgrade(t, daemonHost) + + postUpdateVersion := getAppCliVersion(t, containerName) + require.Equal(t, "v"+postUpdateVersion, tagAppCli) + }) + + }) + +} diff --git a/internal/testtools/test_deb_update/helpers.go b/internal/e2e/updatetest/helpers.go similarity index 57% rename from internal/testtools/test_deb_update/helpers.go rename to internal/e2e/updatetest/helpers.go index 3cf76e46..9b39fd3e 100644 --- a/internal/testtools/test_deb_update/helpers.go +++ b/internal/e2e/updatetest/helpers.go @@ -1,11 +1,11 @@ -package testtools +package updatetest import ( "bufio" "bytes" "context" + "encoding/json" "fmt" - "io" "iter" "log" "net" @@ -17,14 +17,17 @@ import ( "strings" "testing" "time" + + "github.com/stretchr/testify/require" ) -func FetchDebPackage(t *testing.T, path, repo, version, arch string) string { +func fetchDebPackageLatest(t *testing.T, path, repo string) string { t.Helper() + repo = fmt.Sprintf("github.com/arduino/%s", repo) cmd := exec.Command( "gh", "release", "list", - "--repo", "github.com/arduino/"+repo, + "--repo", repo, "--exclude-pre-releases", "--limit", "1", ) @@ -41,20 +44,13 @@ func FetchDebPackage(t *testing.T, path, repo, version, arch string) string { log.Fatal("could not parse tag from gh release list output") } tag := fields[0] - tagPath := strings.TrimPrefix(tag, "v") - debFile := fmt.Sprintf("%s/%s_%s-1_%s.deb", path, repo, tagPath, arch) - fmt.Println(debFile) - if _, err := os.Stat(debFile); err == nil { - fmt.Printf("✅ %s already exists, skipping download.\n", debFile) - return tag - } fmt.Println("Detected tag:", tag) cmd2 := exec.Command( "gh", "release", "download", tag, - "--repo", "github.com/arduino/"+repo, - "--pattern", "*", + "--repo", repo, + "--pattern", "*.deb", "--dir", path, ) @@ -75,11 +71,15 @@ func buildDebVersion(t *testing.T, storePath, tagVersion, arch string) { } outputDir := filepath.Join(cwd, storePath) + tagVersion = fmt.Sprintf("VERSION=%s", tagVersion) + arch = fmt.Sprintf("ARCH=%s", arch) + outputDir = fmt.Sprintf("OUTPUT=%s", outputDir) + cmd := exec.Command( "go", "tool", "task", "build-deb", - fmt.Sprintf("VERSION=%s", tagVersion), - fmt.Sprintf("ARCH=%s", arch), - fmt.Sprintf("OUTPUT=%s", outputDir), + tagVersion, + arch, + outputDir, ) if err := cmd.Run(); err != nil { @@ -87,7 +87,7 @@ func buildDebVersion(t *testing.T, storePath, tagVersion, arch string) { } } -func majorTag(t *testing.T, tag string) string { +func genMajorTag(t *testing.T, tag string) string { t.Helper() parts := strings.Split(tag, ".") @@ -102,7 +102,7 @@ func majorTag(t *testing.T, tag string) string { return newTag } -func minorTag(t *testing.T, tag string) string { +func genMinorTag(t *testing.T, tag string) string { t.Helper() parts := strings.Split(tag, ".") @@ -125,7 +125,9 @@ func minorTag(t *testing.T, tag string) string { func buildDockerImage(t *testing.T, dockerfile, name, arch string) { t.Helper() - cmd := exec.Command("docker", "build", "--build-arg", "ARCH="+arch, "-t", name, "-f", dockerfile, ".") + arch = fmt.Sprintf("ARCH=%s", arch) + + cmd := exec.Command("docker", "build", "--build-arg", arch, "-t", name, "-f", dockerfile, ".") // Capture both stdout and stderr var out bytes.Buffer var stderr bytes.Buffer @@ -133,7 +135,6 @@ func buildDockerImage(t *testing.T, dockerfile, name, arch string) { cmd.Stderr = &stderr err := cmd.Run() - if err != nil { fmt.Printf("❌ Docker build failed: %v\n", err) fmt.Printf("---- STDERR ----\n%s\n", stderr.String()) @@ -142,11 +143,9 @@ func buildDockerImage(t *testing.T, dockerfile, name, arch string) { } fmt.Println("✅ Docker build succeeded!") - fmt.Println(out.String()) - } -func runDockerContainer(t *testing.T, containerName string, containerImageName string) { +func startDockerContainer(t *testing.T, containerName string, containerImageName string) { t.Helper() cmd := exec.Command( @@ -168,64 +167,48 @@ func runDockerContainer(t *testing.T, containerName string, containerImageName s } -func runDockerSystemVersion(t *testing.T, containerName string) string { +func getAppCliVersion(t *testing.T, containerName string) string { t.Helper() cmd := exec.Command( "docker", "exec", "--user", "arduino", containerName, - "arduino-app-cli", "version", + "arduino-app-cli", "version", "--format", "json", ) - output, err := cmd.CombinedOutput() if err != nil { log.Fatalf("command failed: %v\nOutput: %s", err, output) } - return string(output) - -} - -func runDockerSystemUpdate(t *testing.T, containerName string) { - t.Helper() - var buf bytes.Buffer - - cmd := exec.Command( - "docker", "exec", - containerName, - "sh", "-lc", - `su - arduino -c "yes | arduino-app-cli system update"`, - ) - - cmd.Stdout = io.MultiWriter(os.Stdout, &buf) - - if err := cmd.Run(); err != nil { - fmt.Fprintf(os.Stderr, "Error running system update: %v\n", err) - os.Exit(1) + var version struct { + Version string `json:"version"` + DaemonVersion string `json:"daemon_version"` } + err = json.Unmarshal(output, &version) + require.NoError(t, err) + //TODO to enable after 0.6.7 + //require.Equal(t, version.Version, version.DaemonVersion, "client and daemon versions should match") + require.NotEmpty(t, version.Version) + return version.Version } -func runDockerDaemon(t *testing.T, containerName string) string { +func runSystemUpdate(t *testing.T, containerName string) { t.Helper() cmd := exec.Command( "docker", "exec", - "-d", "--user", "arduino", containerName, - "systemctl", "start", "arduino-app-cli", + "arduino-app-cli", "system", "update", "--yes", ) output, err := cmd.CombinedOutput() - if err != nil { - log.Fatalf("command failed: %v\n Output: %s", err, output) - } - - return string(output) + require.NoError(t, err, "system update failed: %s", output) + t.Logf("system update output: %s", output) } -func runDockerCleanUp(t *testing.T, containerName string) { +func stopDockerContainer(t *testing.T, containerName string) { t.Helper() cleanupCmd := exec.Command("docker", "rm", "-f", containerName) @@ -237,60 +220,11 @@ func runDockerCleanUp(t *testing.T, containerName string) { } -func moveDeb(t *testing.T, startDir, targetDir, repo string, tagVersion string, arch string) { - t.Helper() - tagPath := strings.TrimPrefix(tagVersion, "v") - - debFile := fmt.Sprintf("%s/%s_%s-1_%s.deb", startDir, repo, tagPath, arch) +func putUpdateRequest(t *testing.T, host string) { - moveCmd := exec.Command("cp", debFile, targetDir) - - fmt.Printf("📦 Moving %s → %s\n", debFile, targetDir) - if err := moveCmd.Run(); err != nil { - panic(fmt.Errorf("failed to move deb file: %w", err)) - } - - rm(t, debFile) -} - -func ls(t *testing.T) { t.Helper() - cwd, err := os.Getwd() - if err != nil { - fmt.Println("Error getting working directory:", err) - return - } - fmt.Println("Current directory:", cwd) - fmt.Println("Listing all files and folders recursively:") - - // Walk through all files and subdirectories - err = filepath.Walk(cwd, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - fmt.Println(path) - return nil - }) - -} - -func rm(t *testing.T, pathFile string) { - t.Helper() - removeCmd := exec.Command("rm", pathFile) - - err := removeCmd.Run() - if err != nil { - log.Fatalf("Failed to remove file: %v", err) - } - - fmt.Printf("📦 Removed %s\n", pathFile) - -} - -func putUpdateRequest(t *testing.T, url string) string { - - t.Helper() + url := fmt.Sprintf("http://%s/v1/system/update/apply", host) req, err := http.NewRequest(http.MethodPut, url, nil) if err != nil { @@ -306,30 +240,8 @@ func putUpdateRequest(t *testing.T, url string) string { } defer resp.Body.Close() - return resp.Status -} + require.Equal(t, 202, resp.StatusCode) -func rmrf(t *testing.T, pathFile string) { - t.Helper() - // Check if the folder exists - if _, err := os.Stat(pathFile); os.IsNotExist(err) { - fmt.Println("No build directory found.") - return - } - - // Run the Linux command to remove it - cmd := exec.Command("rm", "-rf", pathFile) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - fmt.Println("Removing build directory...") - - if err := cmd.Run(); err != nil { - fmt.Fprintf(os.Stderr, "Error removing build folder: %v\n", err) - os.Exit(1) - } - - fmt.Println("Build directory removed successfully.") } func NewSSEClient(ctx context.Context, method, url string) iter.Seq2[Event, error] { @@ -387,20 +299,35 @@ type Event struct { Data []byte // json } -// WaitForPort waits until a TCP port is open or fails after timeout. -func WaitForPort(t *testing.T, host string, port string, timeout time.Duration) { +// waitForPort waits until a TCP port is open or fails after timeout. +func waitForPort(t *testing.T, host string, timeout time.Duration) { t.Helper() - addr := fmt.Sprintf("%s:%s", host, port) deadline := time.Now().Add(timeout) for time.Now().Before(deadline) { - conn, err := net.DialTimeout("tcp", addr, 500*time.Millisecond) + conn, err := net.DialTimeout("tcp", host, 500*time.Millisecond) if err == nil { _ = conn.Close() - t.Logf("Server is up on %s", addr) + t.Logf("Server is up on %s", host) return } time.Sleep(200 * time.Millisecond) } - t.Fatalf("Server at %s did not start within %v", addr, timeout) + t.Fatalf("Server at %s did not start within %v", host, timeout) +} + +func waitForUpgrade(t *testing.T, host string) { + t.Helper() + + url := fmt.Sprintf("http://%s/v1/system/update/events", host) + + itr := NewSSEClient(t.Context(), "GET", url) + for event, err := range itr { + require.NoError(t, err) + t.Logf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) + if event.Event == "restarting" { + break + } + } + } diff --git a/internal/testtools/test_deb_update/test.Dockerfile b/internal/e2e/updatetest/test.Dockerfile similarity index 100% rename from internal/testtools/test_deb_update/test.Dockerfile rename to internal/e2e/updatetest/test.Dockerfile diff --git a/internal/testtools/test_deb_update/deb_test.go b/internal/testtools/test_deb_update/deb_test.go deleted file mode 100644 index 9746fd9d..00000000 --- a/internal/testtools/test_deb_update/deb_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package testtools - -import ( - "context" - "flag" - "fmt" - "log" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -var arch = flag.String("arch", "amd64", "target architecture") - -func TestStableToUnstable(t *testing.T) { - fmt.Printf("***** ARCH %s ***** \n", *arch) - tagAppCli := FetchDebPackage(t, "build/stable", "arduino-app-cli", "latest", *arch) - FetchDebPackage(t, "build/stable", "arduino-router", "latest", *arch) - majorTag := majorTag(t, tagAppCli) - _ = minorTag(t, tagAppCli) - - fmt.Printf("Updating from stable version %s to unstable version %s \n", tagAppCli, majorTag) - fmt.Printf("Building local deb version %s \n", majorTag) - buildDebVersion(t, "build", majorTag, *arch) - - t.Run("CLI Update Testing", func(t *testing.T) { - - fmt.Printf("Check folder structure and deb downloaded\n") - ls(t) - fmt.Println("**** BUILD docker image *****") - buildDockerImage(t, "test.Dockerfile", "apt-test-update-image", *arch) - fmt.Println("**** RUN docker image *****") - runDockerContainer(t, "apt-test-update", "apt-test-update-image") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - runDockerSystemUpdate(t, "apt-test-update") - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - runDockerCleanUp(t, "apt-test-update") - require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") - }) - - t.Run("Client Daemon Request Testing", func(t *testing.T) { - runDockerContainer(t, "apt-test-update", "apt-test-update-image") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - - status := runDockerDaemon(t, "apt-test-update") - WaitForPort(t, "127.0.0.1", "8800", 5*time.Second) - - status = putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") - if status != "202 Accepted" { - log.Fatalf("Error putting update request: %s", status) - } - - itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") - - for event, err := range itr { - if err != nil { - log.Printf("Error receiving SSE event: %v", err) - } - fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) - if string(event.Data) == "Download complete" { - fmt.Println("✅ Download complete — exiting successfully.") - break - } - } - - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") - - require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+majorTag+"\n") - runDockerCleanUp(t, "apt-test-update") - }) - rmrf(t, "build") -} - -func TestUnstableToStable(t *testing.T) { - - fmt.Printf("***** ARCH %s ***** \n", *arch) - tagAppCli := FetchDebPackage(t, "build", "arduino-app-cli", "latest", *arch) - FetchDebPackage(t, "build/stable", "arduino-router", "latest", *arch) - minorTag := minorTag(t, tagAppCli) - //Move the stable package to the build (unstable) folder - //moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) - fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) - fmt.Printf("Building local deb version %s \n", minorTag) - //Build unstable with a minor tag w.r.t stable - buildDebVersion(t, "build/stable", minorTag, *arch) - //Move the unstable package to the stable folder - //moveDeb(t, "build/", "build/stable", "arduino-app-cli", minorTag, *arch) - //fmt.Printf("Check folder structure and deb downloaded\n") - //ls(t) - fmt.Println("**** BUILD docker image *****") - buildDockerImage(t, "test.Dockerfile", "test-apt-update-unstable-image", *arch) - t.Run("CLI Update Testing", func(t *testing.T) { - - fmt.Println("**** RUN docker image *****") - runDockerContainer(t, "apt-test-update-unstable", "test-apt-update-unstable-image") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") - runDockerSystemUpdate(t, "apt-test-update-unstable") - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") - runDockerCleanUp(t, "apt-test-update-unstable") - require.Equal(t, preUpdateVersion, "Arduino App CLI "+minorTag+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - }) - - t.Run("Client Daemon Request Testing", func(t *testing.T) { - fmt.Println("**** RUN docker image *****") - runDockerContainer(t, "apt-test-update-unstable", "test-apt-update-unstable-image") - preUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") - status := runDockerDaemon(t, "apt-test-update-unstable") - WaitForPort(t, "127.0.0.1", "8800", 5*time.Second) - - status = putUpdateRequest(t, "http://127.0.0.1:8800/v1/system/update/apply") - if status != "202 Accepted" { - log.Fatalf("Error putting update request: %s", status) - } - - itr := NewSSEClient(context.Background(), "GET", "http://localhost:8800/v1/system/update/events") - - for event, err := range itr { - if err != nil { - log.Printf("Error receiving SSE event: %v", err) - } - fmt.Printf("Received event: ID=%s, Event=%s, Data=%s\n", event.ID, event.Event, string(event.Data)) - if string(event.Data) == "Upgrade completed successfully" { - fmt.Println("✅ exiting successfully.") - break - } - } - - postUpdateVersion := runDockerSystemVersion(t, "apt-test-update-unstable") - runDockerCleanUp(t, "apt-test-update-unstable") - require.Equal(t, preUpdateVersion, "Arduino App CLI "+minorTag+"\n") - require.Equal(t, postUpdateVersion, "Arduino App CLI "+tagAppCli+"\n") - - }) - -} From 7442c09e754b62aed7a23396e6531acdd61ff910 Mon Sep 17 00:00:00 2001 From: Giulio Pilotto Date: Thu, 13 Nov 2025 10:23:59 +0100 Subject: [PATCH 17/17] exclude updatetest folder --- Taskfile.yml | 2 +- internal/e2e/updatetest/helpers.go | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 84b137c9..33f7ea0c 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -55,7 +55,7 @@ tasks: cmds: - go build ./cmd/arduino-app-cli # needed for e2e tests - task: generate - - go test ./internal/... ./cmd/... -v -race {{ .CLI_ARGS }} + - go test $(go list ./internal/... ./cmd/... | grep -v internal/e2e/updatetest) -v -race {{ .CLI_ARGS }} # exclude deb package update tests test:pkg: desc: Run only tests in the pkg directory diff --git a/internal/e2e/updatetest/helpers.go b/internal/e2e/updatetest/helpers.go index 9b39fd3e..410a9999 100644 --- a/internal/e2e/updatetest/helpers.go +++ b/internal/e2e/updatetest/helpers.go @@ -187,8 +187,8 @@ func getAppCliVersion(t *testing.T, containerName string) string { } err = json.Unmarshal(output, &version) require.NoError(t, err) - //TODO to enable after 0.6.7 - //require.Equal(t, version.Version, version.DaemonVersion, "client and daemon versions should match") + // TODO to enable after 0.6.7 + // require.Equal(t, version.Version, version.DaemonVersion, "client and daemon versions should match") require.NotEmpty(t, version.Version) return version.Version @@ -299,10 +299,8 @@ type Event struct { Data []byte // json } -// waitForPort waits until a TCP port is open or fails after timeout. -func waitForPort(t *testing.T, host string, timeout time.Duration) { +func waitForPort(t *testing.T, host string, timeout time.Duration) { // nolint:unparam t.Helper() - deadline := time.Now().Add(timeout) for time.Now().Before(deadline) { conn, err := net.DialTimeout("tcp", host, 500*time.Millisecond)