Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/test-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Run dep package update test
env:
GH_TOKEN: ${{ secrets.ARDUINOBOT_TOKEN }}
run: |
go test -v ./internal/testtools/test_deb_update -- --arch amd64
44 changes: 43 additions & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -102,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"
Expand All @@ -123,6 +127,44 @@ 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
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

arduino-app-cli:build:local:
desc: "Build the arduino-app-cli locally"
cmds:
Expand Down
38 changes: 38 additions & 0 deletions internal/testtools/package_update.go
Original file line number Diff line number Diff line change
@@ -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 [email protected].
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)
}

}
141 changes: 141 additions & 0 deletions internal/testtools/test_deb_update/deb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
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)
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) {

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")

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")

}

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")

})

}
Loading
Loading