From 93228e2dc6ccf372627460379df4c9ab7e8b708c Mon Sep 17 00:00:00 2001 From: Akshay-2007-1 Date: Wed, 30 Sep 2026 15:12:13 +0800 Subject: [PATCH 1/8] fix(ci): move Build workflow off the deprecated ubuntu-20.04 runner GitHub removed the Ubuntu 20.04 hosted runner image, so this job was sitting queued forever with no runner ever picking it up (confirmed: zero prior runs of this workflow existed, and the release-v0.3.0 tag push got stuck queued for 20+ minutes with no runner_name assigned until manually cancelled). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01UfpKt8WtJCGZHiETgDXmYu --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1f8a01d..914a442 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ on: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 with: From 8da2ba329adf45fd9af6f5f6c57dd42b5cd66c08 Mon Sep 17 00:00:00 2001 From: Akshay-2007-1 Date: Wed, 30 Sep 2026 15:15:36 +0800 Subject: [PATCH 2/8] fix(ci): bump checkout and upload-artifact to non-deprecated versions actions/upload-artifact@v2 is hard-deprecated - GitHub auto-fails any run using it before a single step executes. Bumps it and the similarly ancient actions/checkout@v2 to v4. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01UfpKt8WtJCGZHiETgDXmYu --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 914a442..b67cc19 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: build: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive - uses: satackey/action-docker-layer-caching@v0.0.8 @@ -47,7 +47,7 @@ jobs: run: sudo chmod 755 /boot/vmlinuz* - name: build image run: ./build_image.sh - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 if: ${{ github.event_name == 'workflow_dispatch' }} with: name: image From 880ad4e0b2fc088a3136a6a900e3f593a6ce49b9 Mon Sep 17 00:00:00 2001 From: Akshay-2007-1 Date: Wed, 30 Sep 2026 15:17:05 +0800 Subject: [PATCH 3/8] fix(ci): drop the unmaintained docker-layer-caching action satackey/action-docker-layer-caching@v0.0.8 calls a GitHub Actions cache API endpoint that's since been decommissioned ("Our services aren't available right now"), failing every run before the build even starts. It's purely a build-speed optimization, not required for correctness - removing it rather than trying to replace it. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01UfpKt8WtJCGZHiETgDXmYu --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b67cc19..7c7a26c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,6 @@ jobs: - uses: actions/checkout@v4 with: submodules: recursive - - uses: satackey/action-docker-layer-caching@v0.0.8 - name: install brickstrap dependencies run: | sudo apt-get update From 05c79c1971dc5bf62edda10b60cccc40303274b3 Mon Sep 17 00:00:00 2001 From: Akshay-2007-1 Date: Wed, 30 Sep 2026 15:22:05 +0800 Subject: [PATCH 4/8] fix(ci): disable BuildKit to keep sudo working inside ev3dev-service-control's Dockerfile BuildKit mounts each RUN step's rootfs with nosuid, which breaks `sudo` there ("effective uid is not 0") even though the Dockerfile itself is unchanged - this only surfaced from the runs-on bump to a runner image shipping a newer Docker Engine. Falls back to the legacy builder, which doesn't impose this restriction. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01UfpKt8WtJCGZHiETgDXmYu --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7c7a26c..9a94d22 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,6 +11,12 @@ on: jobs: build: runs-on: ubuntu-22.04 + env: + # BuildKit mounts each RUN step's rootfs with nosuid, which breaks `sudo` inside + # ev3dev-service-control's Dockerfile (still needed on the newer Docker Engine that ships + # with this runner image, even though that Dockerfile itself is unchanged). The legacy + # builder doesn't impose this restriction. + DOCKER_BUILDKIT: "0" steps: - uses: actions/checkout@v4 with: From 6a297f52111dea5ee169ce13fa2e4307899cbf98 Mon Sep 17 00:00:00 2001 From: Akshay-2007-1 Date: Wed, 30 Sep 2026 15:26:46 +0800 Subject: [PATCH 5/8] fix(ci): strip sudo from ev3dev-service-control's Dockerfile before building Confirmed the nosuid failure isn't BuildKit-specific (same error with DOCKER_BUILDKIT=0) - it's the runner's Docker host mounting RUN steps' rootfs nosuid regardless of builder. sudo was never load-bearing here anyway (build_control_panel.sh always runs the resulting image with -u 0:0), so removing it is safe and sidesteps the host-level issue entirely, without touching the upstream submodule repo. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01UfpKt8WtJCGZHiETgDXmYu --- build_control_panel.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/build_control_panel.sh b/build_control_panel.sh index 91a43de..0cd0f3f 100755 --- a/build_control_panel.sh +++ b/build_control_panel.sh @@ -7,6 +7,13 @@ REPO_DIR="ev3dev-service-control" IMAGE_NAME="sourceacademy/ev3-vala-compiler" cd "$SCRIPT_DIR/$REPO_DIR" +# Strip `sudo` from the Dockerfile before building: the base image runs as root by default (this +# repo always invokes it with -u 0:0 below, and never anything else), so sudo was never load-bearing +# there - just carried over unmodified from the StackOverflow snippet its top RUN line cites. It +# fails outright on GitHub's newer runner images: their Docker host mounts each RUN step's rootfs +# nosuid, which strips sudo's setuid bit ("effective uid is not 0"), regardless of BuildKit vs the +# legacy builder - patched here rather than in the upstream submodule repo, which we don't own. +sed -i -e 's/^RUN sudo /RUN /' -e 's/"sudo", //' Dockerfile docker build -t "$IMAGE_NAME" . cd "$SCRIPT_DIR" From 3d2258aace5eadaaf0d77e3f10abae8b4169b4e4 Mon Sep 17 00:00:00 2001 From: Akshay-2007-1 Date: Wed, 30 Sep 2026 15:31:52 +0800 Subject: [PATCH 6/8] fix(ci): force USER root in ev3dev-service-control's Dockerfile instead of relying on sudo Root cause, verified by comparison: the sibling debian-stretch-cross image (Dockerfile.sling) uses the identical sudo pattern and builds fine on this same runner, but debian-stretch-armel-cross's own /usr/bin/sudo is missing its setuid bit - a defect specific to that one base image, not a runner/host/BuildKit issue (previous two fix attempts for those wrongly assumed causes didn't help, and confirmed this image's default user genuinely isn't root either: a plain sed -i on /etc/apt/sources.list without sudo fails with a real permission error). Forcing USER root sidesteps sudo's broken setuid mechanism entirely rather than depending on it. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01UfpKt8WtJCGZHiETgDXmYu --- build_control_panel.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/build_control_panel.sh b/build_control_panel.sh index 0cd0f3f..cfc1275 100755 --- a/build_control_panel.sh +++ b/build_control_panel.sh @@ -7,13 +7,14 @@ REPO_DIR="ev3dev-service-control" IMAGE_NAME="sourceacademy/ev3-vala-compiler" cd "$SCRIPT_DIR/$REPO_DIR" -# Strip `sudo` from the Dockerfile before building: the base image runs as root by default (this -# repo always invokes it with -u 0:0 below, and never anything else), so sudo was never load-bearing -# there - just carried over unmodified from the StackOverflow snippet its top RUN line cites. It -# fails outright on GitHub's newer runner images: their Docker host mounts each RUN step's rootfs -# nosuid, which strips sudo's setuid bit ("effective uid is not 0"), regardless of BuildKit vs the -# legacy builder - patched here rather than in the upstream submodule repo, which we don't own. -sed -i -e 's/^RUN sudo /RUN /' -e 's/"sudo", //' Dockerfile +# The ev3dev/debian-stretch-armel-cross base image's own /usr/bin/sudo is missing its setuid bit +# ("effective uid is not 0") - a defect in that specific image, not a runner/host issue: the +# sibling debian-stretch-cross image (Dockerfile.sling) uses the exact same sudo pattern and +# builds fine on the same runner. This image's default user genuinely isn't root either (a plain +# `sed -i` on /etc/apt/sources.list without sudo fails with a real permission error), so strip +# sudo AND force USER root directly - that doesn't depend on sudo's (broken) setuid mechanism at +# all. Patched here rather than in the upstream submodule repo, which we don't own. +sed -i -e 's/^RUN sudo /RUN /' -e 's/"sudo", //' -e '/^FROM /a USER root' Dockerfile docker build -t "$IMAGE_NAME" . cd "$SCRIPT_DIR" From 5f843316b4cde2be6654ab1723d93a9bd6bd5b08 Mon Sep 17 00:00:00 2001 From: Akshay-2007-1 Date: Wed, 30 Sep 2026 15:39:47 +0800 Subject: [PATCH 7/8] fix(ci): work around the EOL Debian Stretch archive's expired signing keys apt refuses to install packages non-interactively once a repo's Release-file signature has expired, which is the case for archive.debian.org's Stretch suite (confirmed via the build log's GPG warnings: both the Debian Archive Automatic Signing Key and the Debian Stable Release Key are expired). Scoped narrowly to this one install step, from this single archived/dead repository only. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01UfpKt8WtJCGZHiETgDXmYu --- build_control_panel.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/build_control_panel.sh b/build_control_panel.sh index cfc1275..6fe30e8 100755 --- a/build_control_panel.sh +++ b/build_control_panel.sh @@ -14,7 +14,17 @@ cd "$SCRIPT_DIR/$REPO_DIR" # `sed -i` on /etc/apt/sources.list without sudo fails with a real permission error), so strip # sudo AND force USER root directly - that doesn't depend on sudo's (broken) setuid mechanism at # all. Patched here rather than in the upstream submodule repo, which we don't own. -sed -i -e 's/^RUN sudo /RUN /' -e 's/"sudo", //' -e '/^FROM /a USER root' Dockerfile + +# Debian Stretch has been end-of-life for years and its archive's Release-file signatures have +# since expired, so a plain `apt install -y` refuses to proceed ("unauthenticated packages"). This +# is the standard, narrowly-scoped workaround for an archived/dead release's own expired signing +# keys - it does not affect any live, currently-maintained repository. +sed -i \ + -e 's/^RUN sudo /RUN /' \ + -e 's/"sudo", //' \ + -e '/^FROM /a USER root' \ + -e 's/apt install --yes/apt -o APT::Get::AllowUnauthenticated=true install --yes/' \ + Dockerfile docker build -t "$IMAGE_NAME" . cd "$SCRIPT_DIR" From e786364cfc1723bd722d9853c7de88e26fb0f274 Mon Sep 17 00:00:00 2001 From: Akshay-2007-1 Date: Wed, 30 Sep 2026 15:48:36 +0800 Subject: [PATCH 8/8] fix(ci): detect supported ext4 mke2fs features at build time instead of hardcoding them The hardcoded exclusion list assumed the CI host's mke2fs would either recognize each feature name (and disable it) or never have heard of it at all (making the exclusion a harmless no-op). Bumping the runner image broke that assumption in a new way: its mke2fs (1.46.5) doesn't recognize one of the five names, and an unrecognized name in the -O list isn't a no-op - it makes mke2fs reject the whole option string as invalid, aborting the build ("Invalid filesystem option set"). Replaces the hardcoded string with a real per-feature test against a scratch file on this same host's mke2fs (the same binary guestfish's appliance is built from), keeping only the names it actually accepts - self-adapting to whatever e2fsprogs version happens to be installed, rather than needing another hardcoded update next time it changes. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01UfpKt8WtJCGZHiETgDXmYu --- brickstrap.sh | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/brickstrap.sh b/brickstrap.sh index e3dc08a..d0f6e69 100755 --- a/brickstrap.sh +++ b/brickstrap.sh @@ -201,9 +201,25 @@ function brickstrap_create_image() # orphan_file, compat bit 12), which systemd-fsck-root.service treats as fatal and drops the # whole boot into emergency mode. Explicitly excluding the newer features keeps the # filesystem on a feature set the image's own e2fsck actually understands. Harmless on an - # older host (e.g. this project's current ubuntu-20.04 CI runner) whose mke2fs never enabled - # these features anyway - excluding an already-absent feature is a no-op. - ROOTFS_MKE2FS_FEATURES="^orphan_file,^metadata_csum_seed,^fast_commit,^sparse_super2,^stable_inodes" + # older host whose mke2fs never enabled these features anyway - excluding an already-absent + # feature is normally a no-op. + # + # That assumption broke moving to a newer CI runner image: its mke2fs (1.46.5) is old enough + # to not recognize one of these five names at all (added in a later e2fsprogs release), and + # unlike "silently no-op on an absent feature", an unrecognized name makes mke2fs reject the + # entire -O list as invalid, aborting the whole image build. Rather than hardcode which name(s) + # are safe for whichever e2fsprogs happens to be installed, detect it directly: keep only the + # names this host's own mke2fs actually accepts negating, on a real scratch filesystem via the + # same -O parser guestfish's appliance will use (built from this same host's packages). + ROOTFS_MKE2FS_FEATURES="" + for feat in orphan_file metadata_csum_seed fast_commit sparse_super2 stable_inodes; do + feat_test_img="$(mktemp)" + truncate -s 16M "$feat_test_img" + if mke2fs -F -q -O "^$feat" "$feat_test_img" >/dev/null 2>&1; then + ROOTFS_MKE2FS_FEATURES="${ROOTFS_MKE2FS_FEATURES:+$ROOTFS_MKE2FS_FEATURES,}^$feat" + fi + rm -f "$feat_test_img" + done guestfish -N "$BRICKSTRAP_IMAGE_FILE_NAME"=disk:$BRICKSTRAP_IMAGE_FILE_SIZE -- \ part-init /dev/sda mbr : \