diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 68225ba..491a4f9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,6 +10,8 @@ updates: directory: "/" schedule: interval: "weekly" + cooldown: + default-days: 7 open-pull-requests-limit: 10 groups: # Maven build and reporting plugins @@ -28,15 +30,19 @@ updates: # ----------------------------------------------------------------------- # GitHub Actions — daily, all actions grouped into one PR. - # (Kept daily so security-relevant action pins are updated quickly.) + # Checked daily so security-relevant action pins are picked up quickly; + # a short 3-day cooldown still lets an obviously-bad release be pulled. # The second directory covers the composite action's setup-java pin. # ----------------------------------------------------------------------- + # zizmor: ignore[dependabot-cooldown] # 3 days is a deliberate tradeoff for actions - package-ecosystem: "github-actions" directories: - "/" - "/.github/actions/jdk-setup" schedule: interval: "daily" + cooldown: + default-days: 3 open-pull-requests-limit: 10 groups: actions-updates: # Group all action updates into one PR diff --git a/.github/workflows/build-any-branch.yml b/.github/workflows/build-any-branch.yml index 3dd2f38..a14b067 100644 --- a/.github/workflows/build-any-branch.yml +++ b/.github/workflows/build-any-branch.yml @@ -14,8 +14,7 @@ on: - "**/*.adoc" - "**/*.md" -permissions: - contents: write # needed as the default GITHUB_TOKEN is read-only and submission 403s without it +permissions: {} env: MAVEN_COMMAND: ./mvnw @@ -29,11 +28,29 @@ jobs: compile-and-unit-test: runs-on: ubuntu-latest timeout-minutes: 20 # cold runs pull ~1 GB of database container images + permissions: + contents: read steps: - uses: actions/checkout@v7 + with: + persist-credentials: false - uses: ./.github/actions/jdk-setup - name: Compile and run unit and integration tests run: ${{ env.MAVEN_COMMAND }} ${{ env.MAVEN_CLI_COMMON }} clean verify + + # Isolated from the build job so its write-scoped token is never present + # while a build of pull-request code runs. Only fires for pushes to main. + submit-dependencies: + needs: compile-and-unit-test + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: write + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: ./.github/actions/jdk-setup - name: Submit dependencies to GitHub - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: advanced-security/maven-dependency-submission-action@v5 + uses: advanced-security/maven-dependency-submission-action@a64327a7329c9939cf675e458452febe1894a70c # v6.0.1 diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml index 8fc189b..4e6914a 100644 --- a/.github/workflows/deploy-snapshot.yml +++ b/.github/workflows/deploy-snapshot.yml @@ -1,6 +1,11 @@ name: Deploy Snapshot on: + # zizmor: ignore[dangerous-triggers] + # workflow_run is used safely: the job's `if` requires a successful "Build any + # branch" run from this repository (head_repository guard, so a fork branch + # named `main` cannot reach it) on `main` (branches filter), and it checks out + # exactly that run's commit (head_sha) — never fork-controlled code. workflow_run: workflows: ["Build any branch"] types: [completed] @@ -19,7 +24,9 @@ concurrency: jobs: deploy-snapshot: - if: github.event.workflow_run.conclusion == 'success' + if: >- + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.head_repository.full_name == github.repository runs-on: ubuntu-latest timeout-minutes: 20 steps: @@ -36,7 +43,7 @@ jobs: - name: Get project version id: ver - run: echo "version=$(${{ env.MAVEN_COMMAND }} help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_OUTPUT + run: echo "version=$(${{ env.MAVEN_COMMAND }} help:evaluate -Dexpression=project.version -q -DforceStdout)" >> "$GITHUB_OUTPUT" - name: Deploy snapshot to Maven Central if: endsWith(steps.ver.outputs.version, '-SNAPSHOT') @@ -47,4 +54,6 @@ jobs: - name: Skip (not a snapshot version) if: "!endsWith(steps.ver.outputs.version, '-SNAPSHOT')" - run: echo "Version ${{ steps.ver.outputs.version }} is a release version — skipping snapshot deploy" + env: + VERSION: ${{ steps.ver.outputs.version }} + run: echo "Version ${VERSION} is a release version — skipping snapshot deploy" diff --git a/.github/workflows/lint-workflows.yml b/.github/workflows/lint-workflows.yml new file mode 100644 index 0000000..1feefa2 --- /dev/null +++ b/.github/workflows/lint-workflows.yml @@ -0,0 +1,54 @@ +name: Lint workflows + +on: + pull_request: + paths: + - '.github/**' + push: + branches: [main] + paths: + - '.github/**' + schedule: + - cron: '0 6 * * 1' # Mondays 06:00 UTC — surface newly added lint rules and freshly deprecated actions + workflow_dispatch: + +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + actionlint: + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - name: Run actionlint + uses: raven-actions/actionlint@3d39aea434753780c3b3d4a1a31c854b4dbf49d7 # v2.2.0 + with: + version: 1.7.12 # pin the tool; the action otherwise resolves 'latest' at run time + + zizmor: + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - name: Run zizmor + env: + GH_TOKEN: ${{ github.token }} + # Pinned tool, run directly (not via zizmor-action) so --strict-collection + # can fail the job on a malformed collected file — dependabot.yml, an + # action.yml — instead of warning and skipping it. pipx is preinstalled + # on ubuntu-latest. + run: >- + pipx run zizmor==1.30.1 --strict-collection --format github + --persona regular --collect all -- .github/ diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000..b8bec8d --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,21 @@ +# zizmor configuration — see .github/workflows/lint-workflows.yml. +# https://docs.zizmor.sh/configuration/ +rules: + unpinned-uses: + config: + # Symbolic (tag) refs are acceptable for actions published by GitHub's + # own organizations; anything else must be pinned to a full commit SHA. + policies: + "actions/*": ref-pin + "github/*": ref-pin + "dependabot/*": ref-pin + "*": hash-pin + + self-repository: + ignore: + # The `$/...` self-repository syntax is only a few weeks old (GitHub, + # Jul 2026). The workspace-relative `./...` form is used deliberately + # until that syntax has settled; revisit and switch over later. + - build-any-branch.yml + - deploy-snapshot.yml + - release.yml