Repository navigation
ci(gha): GHA-01 split build/report, zizmor check - #3753
Conversation
Replace thollander/actions-comment-pull-request with a small actions/github-script that finds the PR comment by a hidden marker (also taking over comments posted by that action before) and updates it, or creates one. The final comment also says when the worker run failed or produced no tarballs, instead of leaving "Preparing ..." in place. Values are passed via env, not expanded into script code. Replace the curl-made check run, whose details_url is ignored for GITHUB_TOKEN and whose name got an unpredictable prefix, with a commit status "GHA-01: Tarballs / download link": its target_url is honored, so "Details" leads to the artifact right away. This needs "statuses: write" instead of "checks: write". The link is also written to the job summary of the run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
Compute the artifact name in a shell step instead of with bluwy/substitute-string-action: PR-<number>, else the branch or tag name, with characters that are not valid in artifact names replaced by "_" (a superset of the " " and "/" handled before, so usual names do not change). The values come in via env, since a PR branch name is author-controlled; it was expanded into a shell command line before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
Instead of dispatching the "Worker" workflow as a separate run with benc-uk/workflow-dispatch, and keeping a job polling for its status all the time it runs (doubling the runner time used), call it as a reusable workflow (it already declared workflow_call), so it runs as part of this workflow run. GHA-01 now has three jobs, each with only the permissions it needs: prepare (artifact name, PR comment; pull-requests: write), build (the worker touching untrusted code; contents: read) and report (artifact lookup and cleanup, PR comment, summary, commit status; actions/pull-requests/statuses: write). The report job also runs when the worker failed, and the artifact is looked up in this run only. The workflow no longer needs "actions: write" to start runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
Delete outdated AppVeyorBot comments on PRs in the "prepare" job of GHA-01 with a few lines of actions/github-script, and remove the "GHA-00: Pre-clean" workflow, which did only that, using a third-party action (maheshrayas/action-pr-comment-delete) with the repository hard-coded, and broader permissions than needed (contents: write, checks: write) in a pull_request_target workflow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
The worker restored and saved its ccache with actions/cache on every run, including builds of PR code. Under pull_request_target those run in the base (master) branch cache scope, so a PR could plant a poisoned cache that later master branch builds restore and use. Split it into actions/cache/restore (always) and actions/cache/save, which only runs for pushes to master and the scheduled runs (reusing the key computed when restoring, as its .timestamp-init file is gone by then). PR builds still benefit from the master branch cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
Move the inline actions/github-script code of GHA-01 into two plain JavaScript modules, which the steps load with require(): - pr-comments.js: generic helpers to create or update one comment of a workflow on the PR (found by a hidden marker), and to delete PR comments of another account (used for old AppVeyor CI comments); - dist-tarballs-report.js: tarball artifact lookup and clean-up, and reporting the link as PR comment, job summary and commit status. The PR comment logic is no longer repeated, and the code can be read, linted and type-checked as normal files: they have "// @ts-check" with JSDoc types, and pass tsc --strict (see the comment at the top of pr-comments.js for how to check them). No build step is needed. The jobs with write permissions get the modules with a sparse checkout of .github/scripts from their default ref, i.e. the trusted workflow commit (also for pull_request_target events), never the PR source. Old artifacts are now deleted with awaited API calls, listing all pages of artifacts with that name, instead of the first page only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
Update comments of the GHA-01 workflow which no longer matched it: - pull_request_target runs the workflow (and its .github/scripts/) from the default branch, not the PR target branch (GitHub changed that in late 2025), and the permissions are now per job; - the old artifacts clean-up code is in deleteOldArtifacts() now; - the workflow also runs weekly and manually; - note again what the "Preparing ..." PR comment step is about. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
The PyNUTClient publishing workflow does not use git credentials after its checkout, so do not persist them in the working copy (zizmor "artipacked" finding). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
Only propose GitHub Actions versions published at least a week ago, so that a compromised release is more likely to be found and pulled before it is picked up, since actions are referenced by tags (zizmor "dependabot-cooldown" finding). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
Add a "GHA-06: zizmor" workflow, which runs the zizmor static analysis of GitHub Actions workflows (a pinned version from PyPI, no third-party action) for pushes to master and PRs changing .github/ files. It shows findings as annotations, and fails for new ones of at least "medium" severity. It runs on pull_request with a read-only token. The deliberate choices of this repository are recorded, with reasons, in .github/zizmor.yml: actions are referenced by tags (updated by Dependabot), and GHA-01 uses pull_request_target on purpose. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
Actions from GitHub's own organizations (actions/*, github/*) may stay referenced by tags (their major version tags move to new releases by design, so this trusts those organizations, which run the workflows anyway). Pin all other actions to a full commit SHA, with the version in a comment (which Dependabot updates too), as their tags or branches can be moved to other code: - pypa/gh-action-pypi-publish @release/v1 (a branch) -> v1.14.2 Also use actions/checkout@v7 instead of @master (a moving branch) in the PyNUTClient workflow, like elsewhere. The zizmor check now enforces this policy (ref-pin for actions/* and github/*, hash-pin for all others) via .github/zizmor.yml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
|
A ZIP file with standard source tarball and another tarball with pre-built docs for commit 7f4d96f is temporarily available: NUT-tarballs-PR-3753.zip. |
Under pull_request_target, the PR build job ran in the base branch cache scope: not saving the ccache there was not enough, as PR code can get the job's Actions runtime token and write cache entries for later master builds (e.g. a ccache.conf with a prefix_command). Split GHA-01 into a build workflow on pull_request (read-only token, the PR's own cache scope, also for forks), and a "GHA-01: Tarballs Report" workflow on workflow_run (default branch code only, with the write permissions) for the PR comment, commit status and artifact clean-up. It uses the build run only as data: the PR is looked up by head owner/branch and must match the run's head commit (needed anyway, as workflow_run lists no PRs from forks), and the artifact name is computed there. The worker puts the link in its job summary. This also drops pull_request_target, which GitHub blocks by default on public repositories from November 2, 2026. Use of coding helper tools and AI disclosed: Claude Code was used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
- Artifact name step no longer reads the PR branch name. - Old artifacts are not deleted for master or tags. - A manual worker run on master with an untrusted ref would use the master cache scope. - Non-GitHub actions are SHA-pinned now, only GitHub's own use tags. Use of coding helper tools and AI disclosed: Claude Code was used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
Checks to run before pushing changes to .github/ (zizmor, actionlint, tsc --strict for the helper scripts, and why there is no package.json), and the rules behind the current setup: untrusted input only as data, least privilege, avoid pull_request_target, trusted cache saves only, and no new third-party actions without a clear need (SHA-pinned). Use of coding helper tools and AI disclosed: Claude Code was used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: nmbro <nmbro@users.noreply.github.com>
|
✅ Build nut 2.8.5.5665-master completed (commit 5a095a7702 by @nmbro)
|
|
❌ Build nut 2.8.5.5666-master failed (commit 8212a2a4ca by @nmbro) |
|
❌ Build nut 2.8.5.5666-master failed (commit 8212a2a4ca by @nmbro) |
|
✅ Build nut 2.8.5.5667-master completed (commit 939962965a by @nmbro)
|
|
✅ Build nut 2.8.5.5667-master completed (commit 939962965a by @nmbro) |
…tools#3754, networkupstools#3756] Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
jimklimov
left a comment
There was a problem hiding this comment.
LGTM, posted a couple of comments/questions but they can be followed-up later and do not block a merge.
| # The ccache is restored here, and saved (see below) only by master | ||
| # branch pushes and scheduled runs. PR builds run on pull_request, in | ||
| # their own cache scope, so they can not plant a poisoned cache for | ||
| # later master branch builds. Note that a manual (workflow_dispatch) | ||
| # run uses the cache scope of the branch it is started on: do not | ||
| # start it on master with an untrusted "ref" input. |
There was a problem hiding this comment.
Cross-linking with PR #3754 discussion: with all of these merged - would each PR running "in its own cache scope" start with a blank cache or inherit the master-branch cache as a starting point? Or does it always restore the latest master branch and not benefit from cached bits from earlier run of this PR?
| if [ -n "${PR_NUMBER}" ] ; then | ||
| NAME="PR-${PR_NUMBER}" | ||
| else | ||
| NAME="${REF_NAME}" |
There was a problem hiding this comment.
Is this protected from white-spaces or other "funny characters" in the NAME string (e.g. something encoded into GitHub branch name trying to escape shell quoting - backticks etc.)? This here is directly shell variable expansion (not the YAML parser substitution of some sort), right? That should be safe, e.g.:
$ (REF='`ls`' ; echo "REF=${REF}")
REF=`ls`
There was a problem hiding this comment.
Yes, it's safe. The ${{ }} values only get into the script as env vars, and the shell doesn't re-parse a variable's value, so backticks or $(...) in a ref name stay literal. This is the pattern GitHub recommends against script injection: https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
The tr -c on the next line also replaces anything outside A-Za-z0-9._+- (newlines included), so the name can't add extra lines to $GITHUB_OUTPUT.
Hardens GHA-01 (dist tarballs) beyond the GHSA-w6wj-3r73-fxmh fix:
pull_request, report onworkflow_run. PR code (forks too) now builds with a read-only token in the PR's own cache scope. Underpull_request_targetit ran in master's cache scope, where not saving the ccache did not prevent cache poisoning. Comment, commit status and artifact clean-up move to a new "GHA-01: Tarballs Report" workflow, which runs only default-branch code and treats the build run as data. Also dropspull_request_target, blocked by default on public repositories from Nov 2, 2026.actions/github-script(one comment, updated in place), commit status instead of a check run (its "Details" opens the artifact), worker as a reusable workflow (no polling job),00-preclean.ymlfolded in..github/changes; deliberate exceptions in.github/zizmor.yml. Non-GitHub actions pinned to SHAs; Dependabot 7-day cooldown..github/CI.md: checks to run and rules for CI changes (linked fromAGENTS.md).Not yet tested end-to-end:
workflow_runonly runs from the default branch, so the report workflow needs a fork with this branch as default.Use of coding helper tools and AI disclosed: Claude Code was used for development assistance.
🤖 Generated with Claude Code