diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 0c4ab71..fa5dd5c 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -6,11 +6,11 @@ on: branches: - main +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + permissions: - # deployments permission to deploy GitHub pages website - deployments: write - # contents permission to update benchmark contents in gh-pages branch - contents: write + contents: read # checkout env: CARGO_TERM_COLOR: always @@ -20,6 +20,9 @@ jobs: benchmark: name: Performance regression check runs-on: ubuntu-latest + permissions: + deployments: write # deploy GitHub Pages website + contents: write # update benchmark results in gh-pages branch services: postgres: @@ -37,16 +40,20 @@ jobs: --health-retries 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # master + with: + toolchain: stable - name: Run benchmarks run: cargo bench -- --output-format bencher | tee output.txt - name: Store benchmark result - uses: benchmark-action/github-action-benchmark@v1 + uses: benchmark-action/github-action-benchmark@a60cea5bc7b49e15c1f58f411161f99e0df48372 # v1.22.0 with: name: Criterion Benchmark tool: 'cargo' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b80da00..3455d3e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,29 +6,40 @@ on: merge_group: branches: [main] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read # checkout + env: CARGO_TERM_COLOR: always DATABASE_URL: postgres://postgres:postgres@localhost:5436/test jobs: ci: + name: ci runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Start Postgres run: docker compose up -d --wait - name: Install uv - uses: astral-sh/setup-uv@v4 + uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4.2.0 - name: Validate schema matches migrations run: ./scripts/validate-schema - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # master with: + toolchain: stable components: rustfmt, clippy - name: Check formatting @@ -38,7 +49,7 @@ jobs: run: cargo clippy --all-targets --all-features -- -D warnings - name: Install nextest - uses: taiki-e/install-action@v2 + uses: taiki-e/install-action@1329c298aa20c3257846c9b2e0e55967df3e3c37 # v2.75.25 with: tool: cargo-nextest diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..ba14316 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,65 @@ +name: security + +on: + pull_request: + merge_group: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + pinact: + name: pinact + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: suzuki-shunsuke/pinact-action@cf51507d80d4d6522a07348e3d58790290eaf0b6 # v2.0.0 + with: + skip_push: "true" # validate only; fail CI on unpinned actions + + zizmor: + name: zizmor + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write # upload SARIF findings to Code Scanning + actions: read # required by zizmor-action for private/internal repos + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 + with: + persona: pedantic + + security: + name: security + permissions: {} + if: always() + needs: + - pinact + - zizmor + runs-on: ubuntu-latest + steps: + - name: Print all job results + env: + NEEDS_JSON: ${{ toJson(needs) }} + GITHUB_EVENT_NAME: ${{ github.event_name }} + run: | + echo "'needs': ${NEEDS_JSON}" + echo "github.event_name: ${GITHUB_EVENT_NAME}" + + # In the merge queue, any skipped job is a failure (everything must run). + # In PR CI, skips are allowed (e.g. fork PRs without OIDC). + - if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || (github.event_name == 'merge_group' && contains(needs.*.result, 'skipped')) }} + run: exit 1 diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000..5626e3a --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,30 @@ +rules: + dependabot-cooldown: + config: + days: 3 + + # pinact owns SHA-pinning enforcement; suppress here to avoid duplicate failures. + unpinned-uses: + ignore: + - ci.yml + - benchmark.yml + + # dtolnay/rust-toolchain is preferred over plain rustup for its caching and + # parallel component install behaviour — keeping it is an explicit choice. + superfluous-actions: + ignore: + - ci.yml + - benchmark.yml + + # dtolnay/rust-toolchain publishes only via `master`; no tags exist to pin to, + # so a SHA pointing at master is the best available reference. + stale-action-refs: + ignore: + - ci.yml + - benchmark.yml + + # The Postgres service container is for ephemeral test/bench use only; pinning + # to a SHA256 digest would add maintenance churn with no security benefit. + unpinned-images: + ignore: + - benchmark.yml