diff --git a/.github/workflows/broker-audit.yml b/.github/workflows/broker-audit.yml index 98d779b..fdde466 100644 --- a/.github/workflows/broker-audit.yml +++ b/.github/workflows/broker-audit.yml @@ -24,6 +24,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 + with: + persist-credentials: true # this workflow pushes with the checkout's credentials - uses: actions/setup-go@v7 with: @@ -39,7 +41,7 @@ jobs: run: go run ./cmd/eraser validate-brokers data/brokers.yaml - name: Open prune PR if anything changed - uses: peter-evans/create-pull-request@v8 + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: branch: broker-audit/prune base: main diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ebff59..52b2241 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,18 +15,27 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +# Read-only token; no job here writes to the repo or reads other scopes. +permissions: + contents: read + jobs: ci: uses: drumandbytes/reusable-actions/.github/workflows/go-ci.yml@v1 with: test-race: true + # Audit of this repo's own workflows; accepted findings are in + # .github/zizmor.yml. + zizmor: + uses: drumandbytes/reusable-actions/.github/workflows/zizmor.yml@v1 + # Single stable name for the org's required-status-check ruleset to point # at, regardless of how the real jobs above are split or renamed. required-checks-passed: name: Required checks passed runs-on: ubuntu-latest - needs: [ci] + needs: [ci, zizmor] if: always() steps: - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 1c7e96f..d96fcb6 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -35,12 +35,18 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 + with: + persist-credentials: false + # No module cache in a deploy job: a cache written by another ref could + # end up in what gets published. The Go step here only generates site + # content, so the cold fetch costs little. - uses: actions/setup-go@v7 with: go-version: "1.26" + cache: false - - uses: peaceiris/actions-hugo@v3 + - uses: peaceiris/actions-hugo@2752ce1d29631191ea3f27c23495fa06139a5b78 # v3.2.1 with: hugo-version: "0.165.0" @@ -53,7 +59,7 @@ jobs: - name: Build run: hugo --minify --source site - - uses: cloudflare/wrangler-action@v4 + - uses: cloudflare/wrangler-action@25853364521e0d392ece9b0c1e97a4b37b638087 # v4.1.2 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 76c159d..3022ba9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,21 +16,16 @@ jobs: steps: - uses: actions/checkout@v7 with: + persist-credentials: false fetch-depth: 0 # full history + tags for the changelog and git describe + # No caches in the release build: a cache written by another ref could + # be restored into the published binaries. Releases are rare, so the + # cold build is the cheaper side of that trade. - uses: actions/setup-go@v7 with: go-version: "1.26" - # setup-go caches the module cache; add the build cache ourselves, - # keyed on the toolchain + deps + release config. Cross-compiling - # six targets from a cold build cache is most of the run time. - - uses: actions/cache@v6 - with: - path: ~/.cache/go-build - key: ${{ runner.os }}-go-build-release-${{ hashFiles('go.sum', '.goreleaser.yaml') }} - restore-keys: | - ${{ runner.os }}-go-build-release- - ${{ runner.os }}-go-build- + cache: false # Short-lived token for the dnb-robot GitHub App, scoped to this repo (to # publish the release) and homebrew-tap (for GoReleaser to push the @@ -40,10 +35,12 @@ jobs: with: client-id: ${{ secrets.DNB_ROBOT_CLIENT_ID }} private-key: ${{ secrets.AUTOMATION_APP_PRIVATE_KEY }} + # Only what this job needs (goreleaser publishes the release and pushes the cask to homebrew-tap), not the app's whole grant. + permission-contents: write owner: drumandbytes repositories: eraser,homebrew-tap - - uses: goreleaser/goreleaser-action@v7 + - uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3 with: version: "~> v2" args: release --clean diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000..c9f6ee2 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,18 @@ +# zizmor configuration, read by the zizmor job in ci.yml. Anything accepted +# here is accepted on purpose -- each entry says why. + +rules: + unpinned-uses: + config: + policies: + # GitHub's own and the org's actions stay on tags (Dependabot moves + # them); third-party actions are SHA-pinned. + "actions/*": ref-pin + "drumandbytes/*": ref-pin + "*": hash-pin + + dangerous-triggers: + ignore: + # workflow_run so Dependabot PRs get a token that can merge; never + # checks out PR code. + - auto-merge.yml