Reusable workflows->composite actions - #89
Merged
Merged
Conversation
- Drop the GitHub App token everywhere: automation runs on the scoped `GITHUB_TOKEN` with job-level `permissions` blocks and fail-closed workflow defaults. repo-sync falls back to opening an issue asking for a manual sync when a push touches workflow files, which the token cannot write. - Replace JasonEtco/create-an-issue and peter-evans/create-issue-from-file with a first-party `create-issue` action: stdlib Python around the gh CLI that dedupes by title and updates the existing open issue. - Convert reusable workflows to composite actions: repo-sync, rust-version-check, unused-deps, links-check, typos, lints, msrv, wasm, codecov, gpu-ci (cuda and opencl merged behind a `gpu-framework` input), and gpu-bench. Keep docs and licenses-audits as reusable workflows since they take no configuration. Remove check-lurk-compiles and bench-pr-comment, superseded in ix. - Reference sibling actions and same-repo actions with `$/` self-repository refs, deleting all ci-workflows self-checkouts along with their default-branch version skew; actionlint gets an ignore for its `$/` false positive (rhysd/actionlint#711), and zizmor's self-repository audit endorses the syntax. - Standardize on actions-rust-lang/setup-rust-toolchain: Rust CI actions assume the caller provisions the toolchain and cache (or a wrapper like ix's), while release-pr and typos provision their own since Rust is incidental there. ci-env is gone; the toolchain action covers its env vars. - gpu-bench reports regressions via a step output because composite steps silently ignore `continue-on-error`; also fix its NUM_VCPUS typo and release-pr's unguarded `cd`s. Callers migrate from `uses: .../.github/workflows/<x>.yml@main` to job steps calling `.../.github/actions/<x>@<ref>`; each action description documents its required permissions and setup assumptions.
The github-actions ecosystem's "/" directory only covers `.github/workflows/`, so the third-party actions that moved into composite actions would no longer receive update PRs without listing their directories explicitly.
- create-issue runs end-to-end against a stub `gh` on PATH that records its calls: the create + label path, the update-existing-by-title path, and rejection when both `body` and `body-file` are set, asserted against the recorded call log - lints, wasm, and unused-deps run on a generated fixture crate through the documented caller pattern (checkout, then setup-rust-toolchain, then the action) - rust-version-check runs twice against the stub `gh`: an up-to-date pin must not open an issue, and an outdated pin (installed so `rustup show` reports it as active) must open exactly one with the parsed version in the body
actionlint can't parse composite actions (rhysd/actionlint#46), so their bash `run:` blocks otherwise go unchecked — the class of bug that produced the NUM_VCPUS typo and release-pr's unguarded `cd`s. A Python script beside the action extracts each block via `yq -o=json`, masks `${{ }}` expressions the way actionlint does, and batches everything through one shellcheck invocation. Both tools are preinstalled on GitHub-hosted runners.
The workarounds covered `actions/create-github-app-token`'s `app-id` to `client-id` rename, and no workflow calls that action any more.
Every `uses:` now carries a full commit SHA with a version comment, which is the form Dependabot reads to offer updates. `taiki-e/install-action`'s per-tool tags (`@nextest`, `@cargo-udeps`, `@cargo-llvm-cov`) move and cannot be pinned, so those call sites switch to the action's `tool:` input against a pinnable release.
pinact fails the lint job when an action is not pinned to a full commit SHA. `verify` additionally rejects a pinned SHA whose version comment names a different release — the form an attacker would use to make a malicious commit look like an innocuous tag. The comments pinact maintains are also what Dependabot reads, so pinned actions still receive update PRs. `fix: "false"` implies `skip_push`, so the check runs no git commands and needs no write permissions. Wired into lint-workflows, so callers pick it up when they bump their pin.
Dependabot documents `directories` globbing with `*`, not `**`, and every composite action is one level under `.github/actions/`. An unsupported pattern would match nothing and silently stop update PRs.
samuelburnham
marked this pull request as ready for review
September 30, 2026 16:17
samuelburnham
added a commit
to argumentcomputer/multi-stark
that referenced
this pull request
Oct 1, 2026
ci-workflows removed its ci-env composite action (argumentcomputer/ci-workflows#89), so both jobs that checked it out failed at that step. The toolchain action's v2 defaults cover what ci-env set: incremental off, colored output, short backtraces, and warnings denied through cargo's build.warnings instead of RUSTFLAGS. With RUSTFLAGS no longer exported, the .cargo/config.toml codegen flags now apply to the ubuntu-latest jobs as they already did to cuda-compile. Pin every action to a full commit SHA with pinact, replacing the moving `install-action@nextest` alias with the release pin plus `tool: nextest`. Add the ci-workflows lint-workflows step (actionlint, shellcheck, pinact verify, zizmor) to the lints job ahead of the toolchain setup. ci-workflows publishes no tags, so .github/pinact.yaml exempts its commit-pinned actions from version-comment verification. Also set persist-credentials: false on every checkout, pass the cargo-deny Rust version as a step output instead of through GITHUB_ENV, use github.token in the Nix workflow, and give Dependabot a 3-day cooldown.
samuelburnham
added a commit
to argumentcomputer/multi-stark
that referenced
this pull request
Oct 1, 2026
ci-workflows removed its ci-env composite action (argumentcomputer/ci-workflows#89), so both jobs that checked it out failed at that step. The toolchain action's v2 defaults cover what ci-env set: incremental off, colored output, short backtraces, and warnings denied through cargo's build.warnings instead of RUSTFLAGS. With RUSTFLAGS no longer exported, the .cargo/config.toml codegen flags now apply to the ubuntu-latest jobs as they already did to cuda-compile. Pin every action to a full commit SHA with pinact, replacing the moving `install-action@nextest` alias with the release pin plus `tool: nextest`. Add the ci-workflows lint-workflows step (actionlint, shellcheck, pinact verify, zizmor) to the lints job ahead of the toolchain setup. ci-workflows publishes no tags, so .github/pinact.yaml exempts its commit-pinned actions from version-comment verification. Also set persist-credentials: false on every checkout, pass the cargo-deny Rust version as a step output instead of through GITHUB_ENV, use github.token in the Nix workflow, and raise Dependabot's cooldown from the 3-day default to the 7 days zizmor's dependabot-cooldown audit requires.
samuelburnham
added a commit
to argumentcomputer/multi-stark
that referenced
this pull request
Oct 1, 2026
ci-workflows removed its ci-env composite action (argumentcomputer/ci-workflows#89), so both jobs that checked it out failed at that step. The toolchain action's v2 defaults cover what ci-env set: incremental off, colored output, short backtraces, and warnings denied through cargo's build.warnings instead of RUSTFLAGS. With RUSTFLAGS no longer exported, the .cargo/config.toml codegen flags now apply to the ubuntu-latest jobs as they already did to cuda-compile. Pin every action to a full commit SHA with pinact, replacing the moving `install-action@nextest` alias with the release pin plus `tool: nextest`. Add the ci-workflows lint-workflows step (actionlint, shellcheck, pinact verify, zizmor) to the lints job ahead of the toolchain setup. ci-workflows publishes no tags, so .github/pinact.yaml exempts its commit-pinned actions from version-comment verification. Also set persist-credentials: false on every checkout, pass the cargo-deny Rust version as a step output instead of through GITHUB_ENV, use github.token in the Nix workflow, and raise Dependabot's cooldown from the 3-day default to the 7 days zizmor's dependabot-cooldown audit requires.
samuelburnham
added a commit
that referenced
this pull request
Oct 1, 2026
… rust-version-check The toolchain parse piped `rustup show` through `grep rustc`, but rustup 1.28 reworked that output and no longer prints the rustc line, so the grep found nothing and pipefail failed the step. `rustc --version` under the `rust-toolchain.toml` override reports the pinned version directly. Nightlies were dated by the commit date rustc and `rustup check` report, which is the day before the nightly cut from it, so a dated pin read as one day older than it was and the "latest" date was one day behind the real latest. A dated pin now supplies its own date, and the latest date comes from the nightly channel manifest, the file the fenix hash is taken from. Only stable needs the channel installed for `rustup check`. rust-version-check is removed: rust-version covers the same check and can open the pull request itself. Its only remaining callers use the reusable workflow form that #89 already removed.
samuelburnham
added a commit
that referenced
this pull request
Oct 2, 2026
) * rust-version: update flake.nix and open the PR; fix version checks rust-version now detects the release channel from the pinned toolchain instead of taking it as an input, so a nightly pin can never be rewritten to a stable release or the other way round; beta pins are rejected. With `update-flake` it also rewrites the `sha256` of the fenix `fromToolchainFile` call in flake.nix. fenix fetches the release's channel manifest with `pkgs.fetchurl` at evaluation time, so the hash is that manifest's, computed with curl and openssl rather than Nix. Flake inputs are deliberately left alone. With `pr` it opens the pull request itself through create-pull-request, adding only the files it edited, so callers no longer wire that up. The PR uses GITHUB_TOKEN by default, so a maintainer approves its CI run. Also installs the channel before `rustup check` and anchors the channel line it parses, so a repository that pins a version or date no longer reads as up to date, and drops the stale cleanup step from the pre-`$/` checkout convention. rust-version-check compared versions as strings, which orders 1.100 before 1.99 and would have stopped reporting once the minor version reached three digits; it now sorts them as versions and anchors the same parse. The lint-workflows shellcheck helper also covers a repository that is itself an action, i.e. has action.yml at its root, and skips manifests without `runs.steps`. The test workflow exercises rust-version on an up-to-date pin, an outdated stable pin with a flake, an outdated nightly pin, and a beta pin. * Update Rust flake inputs with the toolchain * rust-version: read versions from rustc and the nightly manifest; drop rust-version-check The toolchain parse piped `rustup show` through `grep rustc`, but rustup 1.28 reworked that output and no longer prints the rustc line, so the grep found nothing and pipefail failed the step. `rustc --version` under the `rust-toolchain.toml` override reports the pinned version directly. Nightlies were dated by the commit date rustc and `rustup check` report, which is the day before the nightly cut from it, so a dated pin read as one day older than it was and the "latest" date was one day behind the real latest. A dated pin now supplies its own date, and the latest date comes from the nightly channel manifest, the file the fenix hash is taken from. Only stable needs the channel installed for `rustup check`. rust-version-check is removed: rust-version covers the same check and can open the pull request itself. Its only remaining callers use the reusable workflow form that #89 already removed. * rust-version: open the flake.nix sed range at the call's brace The range opened at any `fromToolchainFile` mention and closed at the next `}`. In a flake that also calls lean4-nix's `fromToolchainFile` on a `${system}` line, that brace closed the range before the fenix block, so its `sha256` was never rewritten and the action failed on ix, lean-ffi and Blake3.lean. The range now opens at `fromToolchainFile {` and closes at the `sha256` line itself.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Converts the reusable workflows in this repo to composite actions, drops the org GitHub App token in favour of the scoped
GITHUB_TOKEN, and hardens the lint job so the actions can't regress silently.Breaking change for callers
Every reusable workflow except
docs.ymlandlicenses-audits.ymlis gone. Callers move from a job-levelto job steps: checkout,
actions-rust-lang/setup-rust-toolchainwhere Rust is involved, thenEach action's
descriptiondocuments the permissions the calling job needs and what it assumes is already set up.check-lurk-compilesandbench-pr-commentare removed outright; ix superseded both.What changed
Composite actions.
repo-sync,rust-version-check,unused-deps,links-check,typos,lints,msrv,wasm,codecov,gpu-ci(CUDA and OpenCL merged behind agpu-frameworkinput) andgpu-bench. Sibling actions are referenced with$/self-repository refs, which resolve to this repo at the running commit, so the self-checkouts and their default-branch skew are gone.gpu-benchreports regressions through a step output because composite steps ignorecontinue-on-error.No more App token. Automation runs on
GITHUB_TOKENwith job-levelpermissionsand fail-closed workflow defaults.repo-syncopens an issue asking for a manual sync when a push touches workflow files, which that token cannot write.JasonEtco/create-an-issueandpeter-evans/create-issue-from-fileare replaced by a first-partycreate-issueaction: stdlib Python around theghCLI that dedupes by title and updates the existing open issue.Toolchain setup. Standardised on
actions-rust-lang/setup-rust-toolchain. Rust CI actions assume the caller provisions the toolchain and cache;release-prandtyposprovision their own.ci-envis removed since the toolchain action sets the same variables.Supply chain. Every third-party action is pinned to a full commit SHA with a version comment.
taiki-e/install-action's per-tool tags move and can't be pinned, so those sites use itstool:input against a pinnable release.lint-workflowsnow runs pinact withverify, which fails the job on an unpinned action or on a SHA whose version comment names a different release. Dependabot covers.github/actions/*as well as workflows, since the github-actions ecosystem's/directory only means.github/workflows/.Lint coverage for composite actions. actionlint can't parse composite actions, so a script beside
lint-workflowsextracts every bashrun:block viayq, masks${{ }}expressions the way actionlint does, and runs them through shellcheck. This is the class of bug behind theNUM_VCPUStypo and the unguardedcds inrelease-pr, both fixed here.Smoke tests. A new
test.ymlexercisescreate-issueagainst a stubghthat records its calls (create, update-by-title, and invalid-input paths), runslints,wasmandunused-depson a generated fixture crate through the documented caller pattern, and runsrust-version-checkwith an up-to-date pin and an outdated pin.Fixes surfaced by the smoke tests
-ignorepattern for$/was being split on whitespace by the wrapper action; it now contains no literal spaces.cargo clippy -Dwarningswas missing the--separator, onmaintoo.rust-version-checkparsed the toolchain fromrustup show, which stopped printing arustcline in rustup 1.28; it now readsrustc --version..github/zizmor.ymlthat disabledunpinned-usesis removed now that everything is pinned.Not in this PR
@mainwill break on merge and need their own migration PRs.msrv,codecov, the GPU actions,typos,links-check,repo-sync,release-prandtag-releasehave no smoke coverage yet.rust-version-checkassumes the pinned toolchain is installed; rustup 1.28+ no longer installs it on demand.