Skip to content

Harden GitHub Actions workflows - #2262

Open
rly wants to merge 6 commits into
devfrom
harden-github-actions
Open

Harden GitHub Actions workflows#2262
rly wants to merge 6 commits into
devfrom
harden-github-actions

Conversation

@rly

@rly rly commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Motivation

Ports the GitHub Actions hardening from hdmf-dev/hdmf#1518, plus a few CI fixes found along the way.

Least-privilege permissions, SHA-pinned actions, persist-credentials: false, a setup-python-tox composite action, Dependabot for actions with a 7-day cooldown, and a zizmor audit of .github/. Actions publishing immutable releases stay on version tags for readability, with the exception recorded in .github/zizmor.yml.

Three points that are not self-evident from the diff:

  • uses: $/... is GitHub's self-repository syntax, GA since 2026-07-30 and requiring runner ≥ 2.336.0 (hosted runners are on 2.337.0). It resolves to the running commit with no checkout. actionlint 1.7.12 predates it and reports it as malformed; we do not run actionlint in CI.
  • pip caching is off for the two jobs whose distributions are published (deploy_release.yml, and the upload-wheels leg feeding the rolling latest pre-release), so a poisoned cache cannot reach a consumer. zizmor flags this pattern as cache-poisoning; the two workflows that publish nothing carry a documented ignore.
  • Timeouts are set at roughly 3× the maximum runtime measured over the last three successful runs of each workflow.

Also deletes generate_test_files.yml, which is unused. src/pynwb/testing/make_test_files.py still generates the back-compat files when run under an old PyNWB.

How to test the behavior?

uvx zizmor@1.30.0 --persona regular .github/

Passes with no findings, offline and online. The rest is exercised by this PR's own CI.

Checklist

  • Did you update CHANGELOG.md with your changes?
  • Have you checked our Contributing document?
  • Have you ensured the PR clearly describes the problem and the solution?
  • Is your contribution compliant with our coding style? This can be checked running ruff check . && codespell from the source directory.
  • Have you checked to ensure that there aren't other open Pull Requests for the same change?
  • Have you included the relevant issue number using "Fix #XXX" notation where XXX is the issue number? By including "Fix #XXX" you allow GitHub to close issue #XXX when the PR is merged.

🤖 Generated with Claude Code

rly and others added 3 commits September 2, 2026 15:20
Add least-privilege `permissions: contents: read` to every workflow, with
elevated scopes declared only on the jobs that need them.

Pin every action to a full commit SHA with a version comment. Five actions
publish GitHub immutable releases, whose tags cannot be reassigned; they are
pinned to a version tag and exempted in .github/zizmor.yml.

Set `persist-credentials: false` on every checkout. No workflow pushes with
the checkout credentials.

Add .github/actions/setup-python-tox, a composite action that sets up Python
with pip caching and installs tox, referenced with the self-repository `$/`
syntax so it resolves to the running commit without a checkout.

Add .github/workflows/zizmor.yml, which audits .github/ on changes to it, and
.github/dependabot.yml, which tracks action updates weekly with a 7-day
cooldown after publication.

In run_coverage.yml, rename the codecov-action `file` input to `files`;
`file` was removed in codecov-action v5 and was being ignored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Move `concurrency` from the workflow level down to each job, keyed on the
matrix entry, so a superseding run cancels each matrix leg individually.
Tag-triggered `deploy_release.yml` and issue-triggered `project_action.yml`
stay ungrouped, since neither should be cancelled by a later run.

generate_test_files.yml named its job `${{ matrix.os }}`, a key its matrix
does not define, so every job rendered with a blank name. Use `matrix.name`.

run_dandi_read_tests.yml sets up Python with `actions/setup-python` and
installs into it with pip, so its conda reporting step described the runner's
preinstalled conda rather than the environment under test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Give every job a `timeout-minutes` backstop in place of the 6-hour default,
sized at roughly three times the maximum runtime measured over recent
successful runs.

Cache pip downloads on the workflows that were still downloading everything
each run. Caching is off for the two jobs whose distributions are published,
so a release and the rolling "latest" pre-release are built from a cold
cache; `setup-python-tox` takes a `pip-cache` input to select this.

Skip the Codecov upload on a pull request from a fork. `CODECOV_TOKEN` is not
exposed there, and `fail_ci_if_error: true` turned that into a failure on
every external contributor's pull request. The tests themselves still run.

Delete the "Generate test files" workflow. Running
src/pynwb/testing/make_test_files.py under an old PyNWB generates the
back-compat files when they are needed.

Shallow-clone NWB Inspector, keep its tox pin consistent with run_all_tests,
and expire the `distributions` artifact after a day, since the deploy-dev job
consumes it within the same run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.17%. Comparing base (8676056) to head (ec168e5).

Additional details and impacted files
@@           Coverage Diff           @@
##              dev    #2262   +/-   ##
=======================================
  Coverage   96.17%   96.17%           
=======================================
  Files          30       30           
  Lines        3003     3003           
  Branches      437      437           
=======================================
  Hits         2888     2888           
  Misses         65       65           
  Partials       50       50           
Flag Coverage Δ
integration 75.02% <ø> (ø)
unit 86.41% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rly
rly marked this pull request as ready for review September 2, 2026 22:52
rly and others added 2 commits September 2, 2026 15:56
These are all internal CI changes; the detail belongs in the pull request.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
zizmor-action carries the zizmor version it runs, pinned by container image
digest, so Dependabot keeps zizmor current by bumping the action. A version
inside a `run:` step is invisible to Dependabot and has to be bumped by hand.

The action publishes immutable releases, so it takes a version tag under the
policy in .github/zizmor.yml.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Run Codespell on pull requests and on manual dispatch. The branch filters
were redundant with the pull request trigger, and the push trigger re-ran the
check on dev after it had already passed on the pull request.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant