diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1ec714..338f84d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,42 +11,27 @@ on: permissions: {} jobs: - lint: - runs-on: ubuntu-latest + Lint: permissions: contents: read - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false + 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 - - 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 }} - - test: - runs-on: ubuntu-latest + Tests: 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..f23a750 --- /dev/null +++ b/.github/workflows/run-script.yml @@ -0,0 +1,76 @@ +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 + + # 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 + 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 + + # 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' + 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..e241929 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 +```