Skip to content

build: pin jactionlint, shellcheck and oapi-codegen in mise - #549

Merged
wdower merged 8 commits into
mainfrom
ci/08rub-mise-workflow-tools
Nov 13, 2026
Merged

wdower merged 8 commits into
mainfrom
ci/08rub-mise-workflow-tools

Conversation

@aaronlippold

@aaronlippold aaronlippold commented Nov 10, 2026 •

Copy link
Copy Markdown
Member

What

The workflow linter, shellcheck and oapi-codegen move into mise.toml, with checksums in mise.lock, so local runs and CI use the same releases, as #544 did for Go, golangci-lint, gosec and govulncheck. actionlint is replaced by jactionlint at its correctness profile.

Why

Tool Before (CI) Before (local)
actionlint 1.7.12, curl + sha256 in ci.yml env whatever is on PATH (pnpm lint:workflows)
shellcheck not pinned: the runner image's copy Homebrew's
oapi-codegen go install @v2.8.0 from ci.yml env whatever is on PATH (pnpm run matrix), hint hard-coded in two places

actionlint has had no release, commit or merged pull request since 2026-04-19, and its $/ support (rhysd/actionlint#711) is still open. jactionlint (MIT, v2.5.0 released 2026-11-05) is a fork that reads the same config, runs shellcheck the same way, accepts $/, and its correctness profile covers actionlint's checks.

How

  • mise.toml: "aqua:jdx/jactionlint" = "2.5.0", shellcheck = "0.11.0", "go:github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen" = "2.8.0". mise lock added their entries and changed nothing else. jactionlint and shellcheck have SHA-256 for all seven platforms; jactionlint's are backed by GitHub attestations, while shellcheck publishes no checksums or signatures, so its SHA-256 is the one mise recorded. oapi-codegen goes through go install, verified by the Go checksum database, like govulncheck.
  • ci.yml: scan-workflows installs jactionlint and shellcheck through jdx/mise-action (same pin and mise version as check-go) and runs jactionlint; command -v shellcheck still fails the step if shellcheck is missing. check-openapi-matrix installs Go and oapi-codegen through mise-action instead of go install, and no longer asks the shared setup for a second Go. ACTIONLINT_VERSION, ACTIONLINT_SHA256 and OAPI_CODEGEN_VERSION are removed; the oapi-codegen "measurement subject" note moves to mise.toml.
  • .github/jactionlint.yaml selects profile: correctness. .github/actionlint.yaml is removed: it held only the ignore patterns for $/, and it said to delete it once a pinned linter accepts $/. jactionlint now checks $/ targets and their inputs.
  • setup-npm/action.yml: the one new correctness finding (shellcheck SC1003 on tr '\\' '/') is fixed with ${modules//\\//}, which converts the same Windows path without the pipe.
  • hdf-openapi/test/matrix.test.ts parses ci.yml (with the yaml package hdf-openapi already depends on) and checks the check-openapi-matrix job itself: mise-action installs the generator mise.toml pins before the matrix step, no other step mentions oapi-codegen, no if: or continue-on-error on the job or any step up to the matrix, and gate-hdf-scans needs the job, runs if: always() and has a step whose condition is exactly the OR of failure, cancelled and skipped and which exits 1. It requires mise.lock to lock the pinned version, and the pin to equal the baseline's recorded version. It splits mise.toml and mise.lock on CRLF too, for Windows checkouts. On main the test only searched ci.yml for text, and its "CI runs the matrix" substring also matched a comment, so it passed with the step removed.
  • pnpm lint:workflows runs jactionlint; the matrix script's hint, hdf-openapi's README, CLAUDE.md and the developer guide point at mise install and running under mise exec -- (or with mise activated), as the developer guide's toolchain section does.

Verification

  • TDD: the matrix.test.ts change failed first ("mise.toml must pin the generator version"), then passed after the change.
  • The CI-wiring test fails for each of 14 changes applied to a copy of the files and restored: matrix step removed, generator installed outside mise, pin differing from the baseline, matrix step continue-on-error, install step if: false, an extra step fetching the generator, the job dropped from the gate, the install after the matrix, the gate without always(), its rejection step removed, the skipped case dropped, || changed to &&, the step continue-on-error, and exit 1 removed. It passes with mise.toml, mise.lock and ci.yml in CRLF, and fails when mise.lock locks a different version.
  • Independent reviews (verdicts read from each reviewer's own log): Gemini gemini-4-argon-high PASS at round 1; Codex gpt-6.1-sol PASS at round 3. Findings fixed on the way: the Windows CRLF failure (confirmed by CI), textual CI checks, the lock not checked, the gate's rejection not checked, comments overstating lock checksums and what mise install puts on PATH; CodeQL's incomplete-escaping alert on built regexes; a mise.lock I had committed at 2.8.2 (mise rewrote it during a local check).
  • Local, after mise install: jactionlint 2.5.0, shellcheck 0.11.0 and oapi-codegen v2.8.0 resolve to mise's installs; pnpm --filter @mitre/hdf-openapi run matrix matches the baseline; matrix.test.ts 38/38 with both generators re-measured; zizmor 1.30.0 no findings.
  • pnpm check in the pre-commit hook.

Before merge

#546 is merged and this branch is updated from main (merge commit, no rebase), so jactionlint's only finding, the deprecated app-id input in snapshot.yml, is gone: pnpm lint:workflows exits 0 and zizmor reports no findings on the merged tree. The merge was clean; the developer guide's toolchain section and the ci.yml header, which #547 also edited, read consistently.

jactionlint's default profile (security and policy rules) reports more findings on main; the release-workflow ones are fixed in #547 and the rest are tracked separately before that profile is turned on.

Three tools ran locally at whatever version was on PATH while CI pinned its
own: actionlint (ci.yml env, sha256), shellcheck (not pinned; the runner
image's copy) and oapi-codegen (ci.yml env, plus the matrix script's install
hint and hdf-openapi's README). They are now in mise.toml, with checksums in
mise.lock written by `mise lock`, and CI installs them through jdx/mise-action:
scan-workflows installs jactionlint and shellcheck, check-openapi-matrix
installs Go and oapi-codegen 2.8.0 (the baseline's version). The ci.yml env
pins and the curl and go install steps are gone.

actionlint is replaced by jactionlint 2.5.0 at the correctness profile
(.github/jactionlint.yaml): actionlint has had no release, commit or merged
pull request since 2026-04-19, and jactionlint accepts the `$/` form, so
.github/actionlint.yaml and its ignore patterns are removed and `$/` targets
are checked. Its one new correctness finding, SC1003 on setup-npm's
`tr '\\' '/'`, is fixed with a parameter expansion. The deprecated app-id
input it reports in snapshot.yml is fixed by the client-id change on main.

matrix.test.ts reads the generator pin from mise.toml and requires ci.yml to
install it through mise. Its "CI runs the matrix" check now requires the
step's run: line: the substring it looked for also matched a comment, so it
passed with the step removed.

`pnpm lint:workflows` runs jactionlint; the install hints, hdf-openapi's
README, CLAUDE.md and the developer guide point at `mise install`.

Authored by: Aaron Lippold<lippold@gmail.com>
Comment thread hdf-openapi/test/matrix.test.ts Fixed
Comment thread hdf-openapi/test/matrix.test.ts Fixed
@aaronlippold
aaronlippold requested a review from wdower November 10, 2026 13:20
…t regexes

mise.lock recorded oapi-codegen 2.8.2 while mise.toml pins 2.8.0, so CI's
`mise install --locked` refused it ("2.8.0 is not in the lockfile"). mise
rewrites the lock whenever it resolves tools, and a local check that briefly
set mise.toml to 2.8.2 left the lock there. `mise lock` from the real
mise.toml restores 2.8.0; nothing else in the lock changes.

matrix.test.ts built regular expressions from the tool name and escaped only
dots (CodeQL js/incomplete-sanitization). It now finds the mise.toml pin line
and the install_args line with string comparisons.

Authored by: Aaron Lippold<lippold@gmail.com>
…outs

Review findings on this branch:

- matrix.test.ts failed on windows-latest: a CRLF checkout of mise.toml
  left "\r" on the pin line, so the version never matched. It now splits on
  CRLF as well, and passes with mise.toml, mise.lock and ci.yml in CRLF.
- It checked ci.yml as text, so a matrix step with continue-on-error, a
  skipped install step, an install after the matrix, or a second way of
  fetching the generator still passed. It now parses ci.yml with yaml and
  checks the check-openapi-matrix job itself: mise-action installs the pinned
  generator before the matrix step, no other step mentions oapi-codegen, no
  if: or continue-on-error on the job or any step up to the matrix, and the
  scan gate needs the job.
- It now also requires mise.lock to lock the version mise.toml pins, which
  CI's `mise install --locked` enforces.
- ci.yml's comment no longer says govulncheck and oapi-codegen have
  checksums in mise.lock: they are built with go install and verified by the
  Go checksum database.
- `mise install` does not put tools on PATH unless mise is activated, so the
  matrix hint, hdf-openapi's README, the lint:workflows hint and CLAUDE.md
  say to run them under `mise exec --`, as the developer guide does.

Authored by: Aaron Lippold<lippold@gmail.com>
The CI-wiring test checked that gate-hdf-scans needs check-openapi-matrix,
but not that the gate fails when that job fails. It now also requires the
gate to run `if: always()` and to have a step, not marked continue-on-error,
whose condition covers failed, cancelled and skipped upstream results and
which exits 1. Removing `always()`, removing that step, dropping the skipped
case, marking it continue-on-error or removing its `exit 1` each fails the
test.

Authored by: Aaron Lippold<lippold@gmail.com>
The test checked that the gate's rejection condition named failure,
cancelled and skipped, so replacing `||` with `&&` still passed while the
gate would then let a single failed upstream job through. It now compares
the whitespace-normalized condition with the OR of the three results.

Authored by: Aaron Lippold<lippold@gmail.com>
@aaronlippold
aaronlippold marked this pull request as ready for review November 10, 2026 16:18
aaronlippold and others added 3 commits November 10, 2026 13:42
The assertions about how CI measures the OpenAPI matrix lived in
hdf-openapi's own test, where they describe the workflow rather than the
package and run only when that package's suite runs. They move to a guard
script beside the other check-* guards, with its own test, wired into the
check target and into a CI job so a pull request runs it.

Every wiring assertion the branch already had is preserved, each still
failing on its own when its condition is broken. The real measurement
against the installed generator stays in the package test, where the
baseline it compares against lives.

Bead: hdf-libs-4w640
Signed-off-by: Will <will@dower.dev>

@wdower wdower left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the matrix wiring assertions out of hdf-openapi/test/matrix.test.ts into scripts/check-openapi-matrix-wiring.mjs because they read repo infrastructure — ci.yml, mise.toml, mise.lock, the root package.json — rather than any package code, which is what the other scripts/check-* guards do; the real measurement against the installed generator stays in the package, where the baseline lives.

@wdower
wdower merged commit 46cb3b0 into main Nov 13, 2026
24 checks passed
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