From bd757915cb757d5576236a02fbb1bf05d3f94eb4 Mon Sep 17 00:00:00 2001 From: ttbombadil Date: Sun, 4 Oct 2026 16:26:17 +0200 Subject: [PATCH] build(toolchain): pin the tested AVR core --- .github/workflows/ci.yml | 13 ++++--- Dockerfile | 4 +- docs/MIXED_CAPACITY_TESTING.md | 10 +++-- scripts/arduino-avr-core-version | 2 + scripts/check-arduino-avr-core-version.sh | 14 +++++++ scripts/install-arduino-avr-core.sh | 12 ++++++ scripts/run-mixed-capacity-test.sh | 1 + tests/server/arduino-core-version.test.ts | 46 +++++++++++++++++++++++ 8 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 scripts/arduino-avr-core-version create mode 100644 scripts/check-arduino-avr-core-version.sh create mode 100644 scripts/install-arduino-avr-core.sh create mode 100644 tests/server/arduino-core-version.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49e904e2a..b082c3735 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,15 +117,15 @@ jobs: ~/.cache/ms-playwright server/arduino-cache storage/binaries - key: ${{ runner.os }}-e2e-turbo-arduino-cli-1.5.1-${{ hashFiles('package-lock.json', 'server/services/arduino-compiler.ts') }} + key: ${{ runner.os }}-e2e-turbo-arduino-cli-1.5.1-${{ hashFiles('scripts/arduino-avr-core-version') }}-${{ hashFiles('package-lock.json', 'server/services/arduino-compiler.ts') }} restore-keys: | ${{ runner.os }}-e2e-turbo- - - name: Install Arduino CLI 1.5.1 & Core + - name: Install pinned Arduino CLI and AVR core run: | BINDIR=/usr/local/bin sh scripts/install-arduino-cli.sh arduino-cli version | grep -F "Version: 1.5.1 " - arduino-cli core list | grep "arduino:avr" || (arduino-cli core update-index && arduino-cli core install arduino:avr) + sh scripts/install-arduino-avr-core.sh - name: Install Playwright Browsers run: npx playwright install --with-deps chromium @@ -176,13 +176,13 @@ jobs: with: path: | ~/.arduino15 - key: ${{ runner.os }}-arduino-toolchain-cli-1.5.1-${{ hashFiles('package-lock.json') }} + key: ${{ runner.os }}-arduino-toolchain-cli-1.5.1-${{ hashFiles('scripts/arduino-avr-core-version') }}-${{ hashFiles('package-lock.json') }} - - name: Install Arduino CLI 1.5.1 & Core + - name: Install pinned Arduino CLI and AVR core run: | BINDIR=/usr/local/bin sh scripts/install-arduino-cli.sh arduino-cli version | grep -F "Version: 1.5.1 " - arduino-cli core list | grep "arduino:avr" || (arduino-cli core update-index && arduino-cli core install arduino:avr) + sh scripts/install-arduino-avr-core.sh - name: Run toolchain integration tests env: @@ -240,6 +240,7 @@ jobs: - name: Verify Arduino CLI in Production Image run: | docker run --rm --entrypoint arduino-cli unosim:ci version | grep -F "Version: 1.5.1 " + docker run --rm --entrypoint sh unosim:ci -c 'sh /usr/local/bin/check-arduino-avr-core-version.sh' - name: Run Docker sandbox tests run: npm run test:docker diff --git a/Dockerfile b/Dockerfile index e1d662ff6..e7ba5ad2f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,6 +24,7 @@ ENV ARDUINO_CACHE_DIR=/app/server/arduino-cache # 1. Copy Docker CLI binary from official image (no apt repo setup needed) COPY --from=docker-cli /usr/local/bin/docker /usr/local/bin/docker COPY scripts/install-arduino-cli.sh /usr/local/bin/install-arduino-cli.sh +COPY scripts/arduino-avr-core-version scripts/check-arduino-avr-core-version.sh scripts/install-arduino-avr-core.sh /usr/local/bin/ # 2. Install system tools and Arduino CLI RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -37,8 +38,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && mkdir -p /home/node/.arduino15 \ && chown -R node:node /home/node/.arduino15 \ && su node -c 'HOME=/home/node arduino-cli config init' \ - && su node -c 'HOME=/home/node arduino-cli core update-index' \ - && su node -c 'HOME=/home/node arduino-cli core install arduino:avr' \ + && su node -c 'HOME=/home/node sh /usr/local/bin/install-arduino-avr-core.sh' \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /app/server/arduino-cache /app/storage/binaries /app/temp diff --git a/docs/MIXED_CAPACITY_TESTING.md b/docs/MIXED_CAPACITY_TESTING.md index 7ac3c861f..ed339b5f5 100644 --- a/docs/MIXED_CAPACITY_TESTING.md +++ b/docs/MIXED_CAPACITY_TESTING.md @@ -17,10 +17,12 @@ limits or judge a run against assumed thresholds. npm run build:sandbox ``` -- The host must have Arduino CLI 1.5.1 and the `arduino:avr` core installed. - The runner checks the CLI version and includes both versions in its report. - Install the pinned CLI with `sh scripts/install-arduino-cli.sh`; install the - core with `arduino-cli core install arduino:avr` if it is missing. +- The host must have Arduino CLI 1.5.1 and `arduino:avr` 1.8.8 installed. + The runner checks both versions and includes them in its report. Install the + CLI with `sh scripts/install-arduino-cli.sh` and the core with + `sh scripts/install-arduino-avr-core.sh`. The pinned core version has one + update point in `scripts/arduino-avr-core-version`; change it only after the + Docker image, CI toolchain, and integration-toolchain gates pass. - The harness starts and stops its own test-mode backend and only labels and cleans containers owned by its run ID. It disables rate limiting for the diff --git a/scripts/arduino-avr-core-version b/scripts/arduino-avr-core-version new file mode 100644 index 000000000..88c6a8b7f --- /dev/null +++ b/scripts/arduino-avr-core-version @@ -0,0 +1,2 @@ +# Update this value only after the Docker, CI, and integration-toolchain gates pass. +ARDUINO_AVR_CORE_VERSION=1.8.8 diff --git a/scripts/check-arduino-avr-core-version.sh b/scripts/check-arduino-avr-core-version.sh new file mode 100644 index 000000000..838de777a --- /dev/null +++ b/scripts/check-arduino-avr-core-version.sh @@ -0,0 +1,14 @@ +#!/bin/sh +set -eu + +SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +. "$SCRIPT_DIR/arduino-avr-core-version" + +INSTALLED_VERSIONS="$(arduino-cli core list | awk '$1 == "arduino:avr" { print $2 }')" +if ! printf '%s\n' "$INSTALLED_VERSIONS" | grep -Fxq "$ARDUINO_AVR_CORE_VERSION"; then + FOUND_VERSION="${INSTALLED_VERSIONS:-none}" + echo "Expected arduino:avr ${ARDUINO_AVR_CORE_VERSION}; found ${FOUND_VERSION}" >&2 + exit 1 +fi + +echo "Verified arduino:avr ${ARDUINO_AVR_CORE_VERSION}" diff --git a/scripts/install-arduino-avr-core.sh b/scripts/install-arduino-avr-core.sh new file mode 100644 index 000000000..b38927f04 --- /dev/null +++ b/scripts/install-arduino-avr-core.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -eu + +SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +. "$SCRIPT_DIR/arduino-avr-core-version" + +if ! sh "$SCRIPT_DIR/check-arduino-avr-core-version.sh" >/dev/null 2>&1; then + arduino-cli core update-index + arduino-cli core install "arduino:avr@${ARDUINO_AVR_CORE_VERSION}" +fi + +sh "$SCRIPT_DIR/check-arduino-avr-core-version.sh" diff --git a/scripts/run-mixed-capacity-test.sh b/scripts/run-mixed-capacity-test.sh index c5f671169..88d124e17 100755 --- a/scripts/run-mixed-capacity-test.sh +++ b/scripts/run-mixed-capacity-test.sh @@ -79,6 +79,7 @@ docker image inspect unosim-sandbox:latest >/dev/null 2>&1 || { echo "Required i [[ -x ./node_modules/.bin/tsx ]] || { echo "Dependencies are missing; run npm install first" >&2; exit 1; } ARDUINO_CLI_VERSION="$(arduino-cli version 2>/dev/null || true)" printf '%s\n' "${ARDUINO_CLI_VERSION}" | grep -Fq "Version: 1.5.1 " || { echo "Arduino CLI 1.5.1 is required on the host" >&2; exit 1; } +sh scripts/check-arduino-avr-core-version.sh ARDUINO_AVR_CORE_VERSION="$(arduino-cli core list | awk '$1 == "arduino:avr" {print $2}')" [[ -n "${ARDUINO_AVR_CORE_VERSION}" ]] || { echo "The arduino:avr core must be installed on the host" >&2; exit 1; } diff --git a/tests/server/arduino-core-version.test.ts b/tests/server/arduino-core-version.test.ts new file mode 100644 index 000000000..6dba9de33 --- /dev/null +++ b/tests/server/arduino-core-version.test.ts @@ -0,0 +1,46 @@ +import { readFileSync } from "node:fs"; +import { mkdtemp, chmod, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; +import { afterEach, describe, expect, it } from "vitest"; + +const execFileAsync = promisify(execFile); +const CHECKER = resolve("scripts/check-arduino-avr-core-version.sh"); +const VERSION_FILE = resolve("scripts/arduino-avr-core-version"); +const EXPECTED_CORE_VERSION = readFileSync(VERSION_FILE, "utf8") + .match(/^ARDUINO_AVR_CORE_VERSION=(\S+)$/m)?.[1]; +if (!EXPECTED_CORE_VERSION) throw new Error("Pinned AVR core version is missing"); +let tempDir: string | undefined; + +afterEach(async () => { + if (tempDir) await rm(tempDir, { recursive: true, force: true }); + tempDir = undefined; +}); + +async function runChecker(installedVersion: string): Promise { + tempDir = await mkdtemp(join(tmpdir(), "unosim-core-version-")); + const fakeCli = join(tempDir, "arduino-cli"); + await writeFile( + fakeCli, + `#!/bin/sh\nprintf 'ID Installed Latest Name\\narduino:avr ${installedVersion} ${installedVersion} Arduino AVR Boards\\n'\n`, + ); + await chmod(fakeCli, 0o755); + await execFileAsync("sh", [CHECKER], { + env: { ...process.env, PATH: `${tempDir}:/usr/bin:/bin` }, + }); +} + +describe("Arduino AVR core version canary", () => { + it("accepts the tested pinned core version", async () => { + await expect(runChecker(EXPECTED_CORE_VERSION)).resolves.toBeUndefined(); + }); + + it("rejects a different installed core version", async () => { + const mismatchVersion = EXPECTED_CORE_VERSION === "1.8.7" ? "1.8.6" : "1.8.7"; + await expect(runChecker(mismatchVersion)).rejects.toThrow( + new RegExp(`expected.*${EXPECTED_CORE_VERSION}.*found.*${mismatchVersion}`, "i"), + ); + }); +});