Skip to content

ci: fix workflow bugs, bump actions, tighten permissions - #259

Merged
zfields merged 7 commits into
masterfrom
rob-ci-improvements
Oct 19, 2026
Merged

zfields merged 7 commits into
masterfrom
rob-ci-improvements

Conversation

@rdlauer

@rdlauer rdlauer commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes several CI bugs, moves every action to its latest major version, and adds least-privilege permissions, concurrency, timeouts, and Dependabot. What the tests check is unchanged. CI stays on GitHub-hosted runners (see the new ARCHITECTURE.md note on why Namespace wasn't adopted).

Note

This branch includes the two commits from #257 (@zakoverflow's Bats pin), because it bumps the same action again. If #257 merges first, rebasing this branch drops them. If this merges first, #257 can be closed.

Bug fixes

  • The architecture-docs check never enforced its first pattern. ! grep doesn't trigger errexit, and the loop only returned the last iteration's status. It now checks grep's status explicitly and fails on a match or on a missing path.
  • force_build dispatched from any branch published ghcr.io/blues/note_c_ci:latest. Publishing is now limited to master and waits for every job that uses the image. Before, it only waited for run_unit_tests.
  • Image-consuming jobs used if: always(). That ignores cancellation, and when the image build failed they silently tested against the old :latest. They now run only when the image build succeeded or was skipped.
  • The md5srv "Re-run Tests" step could never work. It called $HOME/.local/bin/bats, but bats-action installs to $HOME/.local/share/bats/bin and adds that to PATH. It now calls bats, and only retries when the test step itself failed.
  • The HIL proxy-route check [ -n $route_uid ] was always true, and neither route check caught jq's "null".
  • Coveralls was pinned to its moving master branch. It's now @v2 with file:.
  • Dependabot PRs don't get Actions secrets, so the hardware job is now skipped for them. The md5srv tests still run. To check a Dependabot bump to a HIL action, run the workflow manually (workflow_dispatch) on its branch.
  • The bats library cache never restored. bats-action installs bats-support and bats-assert under /usr/lib by default, which the unprivileged cache restore can't write to. Every run logged "Failed to restore … tar exit code 2" and downloaded the libraries again. They now install under runner.temp, as bats-action's README recommends. I also deleted three 18-month-old master cache entries that reported a hit but couldn't be restored; nothing in the diff relies on that.

Hygiene

  • ci.yml concurrency: superseded PR runs are cancelled. Master runs get a per-SHA group, so a pending master run is never cancelled.
  • Linux jobs are pinned to ubuntu-24.04. ubuntu-latest starts moving to Ubuntu 26 on 2026-10-19, and the jobs that run directly on the runner depend on its packages: HIL's apt packages and ngrok's buster repo, Python 3.11, doxygen, and the integration test's compiler. ubuntu-latest still resolves to 24.04 today, so nothing changes yet. macOS stays on macos-latest.
  • Removed paths-filter's base: 'master'. It's ignored on PR events, and v4 now warns about that. On push and dispatch an unset base already falls back to the default branch.
  • timeout-minutes on every job, instead of the 6-hour default.
  • Least-privilege permissions: contents: read by default, pull-requests: read for paths-filter, and packages: write only for publish_ci_image.
  • Removed the unused buildx setup from the load-ci-image composite action. Removed the unused ghcr login and the redundant load: true from the image build.
  • Added .github/dependabot.yml: GitHub Actions, weekly, with minor and patch bumps grouped. It covers the composite action too.

Action versions

Action Before After
actions/checkout v3, v4 v7
actions/setup-python v3, v4 v7 ('3.x' for docs keeps today's resolution; HIL stays on 3.11)
actions/upload-artifact v4 v7
actions/download-artifact v4 v8
dorny/paths-filter v2 v4
docker/login-action, docker/setup-buildx-action v2 v4
docker/build-push-action v4 v7
coverallsapp/github-action master v2
bats-core/bats-action 3.0.0 4.0.0 (needs github-token, which is now passed)
tailscale/github-action v3 v4
aws-actions/configure-aws-credentials v4 v6
mikepenz/action-junit-report v3 v6
peaceiris/actions-gh-pages v3 v4

Test plan

  • actionlint: no errors. Only existing shellcheck style notes remain.
  • Tested the new architecture-docs check locally with bash -e: passes on the current tree, fails on a match in either pattern, fails on a missing path.
  • CI pipeline green on this PR. Coveralls is unchanged at 93.408%, and publish_ci_image is correctly skipped.
  • HIL green on this PR: md5srv, plus the hardware job on Tailscale v4, AWS v6, and junit-report v6
  • bats cache: dispatched MD5 Server Tests twice on this branch. The first run missed and saved; the second restored all three entries with no warning.
  • Annotations: the Node 20 deprecation warnings, the Ubuntu 26 notice, and the paths-filter warning are gone
  • Dispatched CI on this branch with force_build=true (run). The image built from today's debian:stable-slim, all six image-consuming jobs loaded it and passed, publish_ci_image was skipped because the run wasn't on master, and the 396 MB handoff artifact expires after 7 days. The new build-push version also runs BuildKit's Dockerfile checks, which flag FROM --platform=linux/amd64 (FromPlatformFlagConstDisallowed). That's a warning only, and the constant platform looks deliberate since the image installs i386 multilib packages, so I left it.
  • After merge: the first master run uploads coverage and publishes the docs site, with no image publish unless the Dockerfile changed

Docs

  • ARCHITECTURE.md: new Continuous Integration subsection under Testing Strategy.
  • docs/architecture/architecture.html: unchanged. It's the component graph and has no CI content.
  • source-research/: unaffected. Workflows are excluded, and no SDK symbols, dependencies, or APIs changed.

Not changed

notecard-binary-tests.yml ignores any firmware_s3_uri value other than nightly, even though the input's description says it takes an S3 URI. The HIL owner needs to decide whether to implement it or reword the description.

🤖 Generated with Claude Code

zakoverflow and others added 5 commits October 19, 2026 15:13
- The md5srv-test job (the first job of the nightly card.binary HIL
  workflow) failed in "Setup Bats and bats libs" with
  "curl: (22) The requested URL returned error: 403". The card.binary
  hardware job needs md5srv-test, so it was skipped.
- The workflow does not pass bats-version to bats-core/bats-action, so
  the action looks up the latest Bats release through
  api.github.com/repos/bats-core/bats-core/releases/latest. The
  BATS_VERSION env var in this workflow is not read by the action.
- bats-action 3.0.0 makes that lookup without a token. Unauthenticated
  API calls share a 60 requests/hour limit per source IP, and GitHub
  answers with 403 once the limit is used up. curl -f hides the response
  body, so the log can't confirm the reason, but this is the likely one.
- bats-action 3.0.1 sends the job's GITHUB_TOKEN with the lookup and the
  tarball download. That is the upstream fix for this lookup. Bump to
  3.0.1. No other change: same inputs, same Bats version resolution, same
  libraries.
- Checked locally: actionlint reports no issues in md5srv-tests.yml, and
  the 3.0.1 install logic resolves and installs Bats v1.14.0, after which
  the md5srv bats suite passes (14 tests, 0 failures).
- Pass bats-version: 1.14.0 to bats-core/bats-action. With an explicit
  version the action skips its api.github.com releases/latest lookup, so
  the install no longer depends on GitHub API rate limits at all.
- 1.14.0 is the version the nightly has been installing as "latest", so
  this does not change which Bats the tests run on.
- Remove the BATS_VERSION: 1.10.0 env var. The action never read it, and
  its value did not match the Bats version actually in use.
- Keep bats-action 3.0.1. Its token on the tarball download stays as
  defense in depth.
Bug fixes:
- check_architecture_docs: `! grep` skips errexit, so only the last
  pattern was ever enforced. Check grep's status explicitly and fail on a
  match or on a missing path.
- publish_ci_image: a force_build dispatch from any branch pushed
  ghcr.io/blues/note_c_ci:latest. Gate on master and require every
  image-consuming job to pass, not just run_unit_tests.
- Image-consuming jobs used `if: always()`, which runs even after
  cancellation and tests against the stale :latest image when the image
  build fails. Run only when the image build succeeded or was skipped.
- md5srv "Re-run Tests" invoked $HOME/.local/bin/bats, which bats-action
  never installs, so the retry could not run. Use bats from PATH and only
  retry when the test step itself failed.
- HIL route checks: `[ -n $route_uid ]` was always true and neither check
  caught jq's "null". Quote and reject "null".
- Coveralls was pinned to the moving `master` branch; use v2 with `file`.

Hygiene:
- ci.yml concurrency: cancel superseded PR runs; master runs get a
  per-SHA group so a pending run is never cancelled.
- timeout-minutes on every job; least-privilege permissions.
- Drop the unused buildx setup from the load-ci-image composite action,
  and the unused ghcr login and redundant `load: true` from the image build.
- Add dependabot for GitHub Actions (workflows and composite actions).

Action bumps: checkout v7, setup-python v7, upload-artifact v7,
download-artifact v8, paths-filter v4, docker login/setup-buildx v4,
build-push v7, bats-action 4.0.0 (with github-token), tailscale v4,
configure-aws-credentials v6, action-junit-report v6, actions-gh-pages v4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dependabot PR runs get no Actions secrets (only Dependabot secrets, and
none are configured), so the Tailscale, Notehub and ngrok steps would
fail. The secret-free md5srv tests still run. Re-runs keep github.actor,
so they stay skipped; a maintainer can validate HIL changes on a
Dependabot branch with workflow_dispatch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a Continuous Integration subsection under Testing Strategy covering
what runs where, the CI image publication policy, when the hardware
tests run or are skipped, and why CI stays on GitHub-hosted runners.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rdlauer rdlauer mentioned this pull request Oct 19, 2026
5 tasks done
rdlauer and others added 2 commits October 19, 2026 15:57
…base

ubuntu-latest starts moving to Ubuntu 26 on 2026-10-19. The host-run
jobs (HIL apt packages including ngrok's buster repo, Python 3.11, the
docs build's doxygen, the integration test's compiler) are exposed to
that change, so pin every Linux job to ubuntu-24.04 (the image
ubuntu-latest resolves to today) and move to 26.04 deliberately later.
macOS stays on macos-latest.

paths-filter ignores `base` on pull_request events and v4 warns about
it. On push and dispatch an unset base falls back to the default branch
(master), so removing it changes nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bats-action defaults bats-support/bats-assert to /usr/lib, and the cache
restore runs tar as the unprivileged runner user, so every run logged
"Failed to restore ... tar exit code 2" and re-downloaded the libraries.
bats-action's README documents this and recommends a path under $HOME.
runner.temp qualifies and sits outside the workspace, so the later
checkout isn't affected by the root-owned install.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zfields
zfields merged commit 2d22844 into master Oct 19, 2026
31 checks passed
@zfields
zfields deleted the rob-ci-improvements branch October 19, 2026 21:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants