From b5fda8046fbec9ec2911830b654a02fe6a6d5294 Mon Sep 17 00:00:00 2001 From: Alberto Donato Date: Mon, 19 Oct 2026 11:16:26 +0200 Subject: [PATCH 1/4] feat: add run-script reusable workflow ### what Add a run-script reusable workflow, encapsulating common set up for a mise-based repository where the workflow ends up running one or more commands (e.g lint, test, ...) This includes - optional runner hardening via step-security (if the env var is passed) - optional pre-commit cache save/restore - mise and tools installation ### why Reduce repeated setup in many github actions ### testing ### docs udpated here --- .github/workflows/ci.yml | 49 ++++++++------------- .github/workflows/run-script.yml | 75 ++++++++++++++++++++++++++++++++ README.md | 23 +++++++++- 3 files changed, 114 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/run-script.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1ec714..1cb0e64 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,41 +12,26 @@ permissions: {} jobs: lint: - runs-on: ubuntu-latest permissions: contents: read - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Set tool versions - uses: wistia/parse-tool-versions@32f568a4ffd4bfa7720ebf93f171597d1ebc979a # v2.1.1 - with: - postfix: _TOOL_VERSION - - - uses: j178/prek-action@4e14d07f9231acabce116ccfca13b13dd9755ece # v3.0.0 - with: - # Without a pin this installs whatever prek released most recently, - # so a hostile release reaches CI with no change here. The version - # comes from .tool-versions, so CI and a local run agree on it. - prek-version: ${{ env.PREK_TOOL_VERSION }} - env: - # zizmor's five online audits skip silently without a token, and its - # summary line reads the same either way, so CI would pass while the - # supply-chain audits never ran. - GH_TOKEN: ${{ github.token }} + uses: ./.github/workflows/run-script.yml + secrets: + STEP_SECURITY_API_KEY: ${{ secrets.STEP_SECURITY_API_KEY }} + with: + pre-commit-cache: true + # zizmor's online audits skip silently without a token, and its summary + # line reads the same either way, so CI would pass while the supply-chain + # audits never ran. + github-token: true + run: | + just lint test: - runs-on: ubuntu-latest permissions: contents: read - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Run the tests - run: python3 -m unittest discover -s validate-min-terraform -p 'test_*.py' + uses: ./.github/workflows/run-script.yml + secrets: + STEP_SECURITY_API_KEY: ${{ secrets.STEP_SECURITY_API_KEY }} + with: + run: | + just test diff --git a/.github/workflows/run-script.yml b/.github/workflows/run-script.yml new file mode 100644 index 0000000..85058e2 --- /dev/null +++ b/.github/workflows/run-script.yml @@ -0,0 +1,75 @@ +name: "Run script" +on: + workflow_call: + inputs: + run: + description: "Script for the run step" + type: string + required: true + pre-commit-cache: + description: "Restore the pre-commit cache, saving it on non-PR events" + type: boolean + default: false + full-git-history: + type: boolean + description: "Whether to fetch full git history rather than just current HEAD" + default: false + github-token: + type: boolean + description: "Expose the job's read-only GitHub token to the script as GH_TOKEN" + default: false + secrets: + STEP_SECURITY_API_KEY: + required: false +permissions: {} +jobs: + run: + runs-on: ubuntu-latest + permissions: + contents: read + env: + HARDEN_RUNNER: ${{ secrets.STEP_SECURITY_API_KEY != '' }} # zizmor: ignore[secrets-outside-env] + steps: + - name: Harden Runner + if: env.HARDEN_RUNNER == 'true' + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 + with: + use-policy-store: true + api-key: ${{ secrets.STEP_SECURITY_API_KEY }} # zizmor: ignore[secrets-outside-env] + + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + fetch-depth: ${{ inputs.full-git-history && '0' || '1' }} + + - name: Install mise + uses: jdx/mise-action@7a4e45a543138629540c9a1616d08632b893e492 # v5.0.1 + with: + minimum_release_age: 7d + cache: true + + - name: Restore pre-commit cache + if: inputs.pre-commit-cache + id: pre-commit-cache + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/.cache/pre-commit + key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }} + + - name: Run + run: ${{ inputs.run }} # zizmor: ignore[template-injection] caller-provided script + env: + GH_TOKEN: ${{ inputs.github-token && github.token || '' }} + # Point prek at the pre-commit cache path so the cache step covers both. + PREK_HOME: ~/.cache/pre-commit + + # Restore-only above, save gated to non-PR events: an untrusted PR run must + # not be able to write a hook-environment cache that a later trusted build + # restores and executes (cache poisoning). + - name: Save pre-commit cache + if: inputs.pre-commit-cache && github.event_name != 'pull_request' && steps.pre-commit-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/.cache/pre-commit + key: ${{ steps.pre-commit-cache.outputs.cache-primary-key }} diff --git a/README.md b/README.md index 967c90c..efaa98a 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,30 @@ Pin by commit SHA. This repository publishes no release tags, so a SHA is the on | --- | --- | | [validate-min-terraform](validate-min-terraform/) | Runs `terraform validate` at the floor a module's `required_version` declares, and fails when that floor is below a configured minimum. | -## Adding a tool +### Adding a tool 1. Create a directory named for the tool. 2. Add an `action.yml` and a `README.md` covering usage, inputs, and outputs. 3. Keep any non-trivial logic in its own script beside the `action.yml`, so that a linter reads it and a test can run it without a runner. Prefer Python over shell for anything that parses or compares. 4. Add a row to the table above. + +## Reusable workflows + +### `run-script` + + +Run a set of commands on the repository, with the required tools installed via `mise`. + + +``` + Lint: + permissions: + contents: read + uses: stacklet/github-tooling/.github/workflows/run-script.yml + secrets: + STEP_SECURITY_API_KEY: ${{ secrets.STEP_SECURITY_API_KEY }} + with: + pre-commit-cache: true + run: | + just lint +``` From 66a34b641fff677dcc5e3f40a75c743d372e30d1 Mon Sep 17 00:00:00 2001 From: Alberto Donato Date: Mon, 19 Oct 2026 15:22:56 +0200 Subject: [PATCH 2/4] uppercase names --- .github/workflows/ci.yml | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1cb0e64..338f84d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ on: permissions: {} jobs: - lint: + Lint: permissions: contents: read uses: ./.github/workflows/run-script.yml @@ -26,7 +26,7 @@ jobs: run: | just lint - test: + Tests: permissions: contents: read uses: ./.github/workflows/run-script.yml diff --git a/README.md b/README.md index efaa98a..e241929 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Run a set of commands on the repository, with the required tools installed via ` Lint: permissions: contents: read - uses: stacklet/github-tooling/.github/workflows/run-script.yml + uses: stacklet/github-tooling/.github/workflows/run-script.yml@ secrets: STEP_SECURITY_API_KEY: ${{ secrets.STEP_SECURITY_API_KEY }} with: From 194dd52b91a635739f9537f8759c2642af19a386 Mon Sep 17 00:00:00 2001 From: Alberto Donato Date: Mon, 19 Oct 2026 18:00:07 +0200 Subject: [PATCH 3/4] update comments --- .github/workflows/run-script.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-script.yml b/.github/workflows/run-script.yml index 85058e2..f23a750 100644 --- a/.github/workflows/run-script.yml +++ b/.github/workflows/run-script.yml @@ -49,6 +49,7 @@ jobs: minimum_release_age: 7d cache: true + # Always read the cache, it's safe to do so even on untrusted PRs. - name: Restore pre-commit cache if: inputs.pre-commit-cache id: pre-commit-cache @@ -64,8 +65,8 @@ jobs: # Point prek at the pre-commit cache path so the cache step covers both. PREK_HOME: ~/.cache/pre-commit - # Restore-only above, save gated to non-PR events: an untrusted PR run must - # not be able to write a hook-environment cache that a later trusted build + # Cache ssave is gated to non-PR events: an untrusted PR run must not be + # able to write a hook-environment cache that a later trusted build # restores and executes (cache poisoning). - name: Save pre-commit cache if: inputs.pre-commit-cache && github.event_name != 'pull_request' && steps.pre-commit-cache.outputs.cache-hit != 'true' From 1710e430f99fab2e6c2b6891c740e8fa4f043975 Mon Sep 17 00:00:00 2001 From: Alberto Donato Date: Mon, 19 Oct 2026 18:15:54 +0200 Subject: [PATCH 4/4] empty