diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..78bf59e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +# Dependabot version updates +# =========================== +# Keeps pinned GitHub Action SHAs current via automated pull requests. +# Scoped to the github-actions ecosystem only; the go.mod tooling manifest +# is intentionally out of scope for this repo. +# See: https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "ci" + include: "scope" diff --git a/.github/workflows/ci_dependencies.yml b/.github/workflows/ci_dependencies.yml index 0269593..061af61 100644 --- a/.github/workflows/ci_dependencies.yml +++ b/.github/workflows/ci_dependencies.yml @@ -1,7 +1,20 @@ name: Dependencies # -------------------------------------------------------------------------- -# Dependency review for all PRs. +# Dependency review for all PRs, delegated to the org-infra reusable +# workflows (matching ci_security.yml / ci_scheduled.yml in this repo). +# +# Dependency review is SOFT-GATED: reusable_deps_reviewer.yml wraps +# actions/dependency-review-action with continue-on-error: true, so a +# missing Dependency Graph (GitHub Advanced Security is not enabled on +# this repo) no longer hard-fails every PR. The result is surfaced as an +# output rather than as a blocking gate. Re-evaluate this soft-gate if the +# Dependency Graph / GitHub Advanced Security is ever enabled here. +# +# The Dependabot comment and auto-approve jobs from the canonical org +# pattern are intentionally OMITTED: this repo governs org policy +# (Peribolos, safe-settings, rulesets), so auto-approving Dependabot PRs +# here is an elevated attack surface pending a separate threat-model review. # -------------------------------------------------------------------------- on: @@ -11,15 +24,14 @@ on: permissions: contents: read + issues: none + pull-requests: none jobs: - dependency-review: - name: Dependency Review - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + call_deps_reviewer: + name: General + uses: complytime/org-infra/.github/workflows/reusable_deps_reviewer.yml@0c784711926c9864f027ec565fd7c06a382d80f8 # v0.7.1 - - name: Dependency Review - uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 + call_dependabot_reviewer: + name: Dependabot + uses: complytime/org-infra/.github/workflows/reusable_dependabot_reviewer.yml@0c784711926c9864f027ec565fd7c06a382d80f8 # v0.7.1 diff --git a/openspec/changes/fix-ci-dependencies-reusable-migration/.openspec.yaml b/openspec/changes/fix-ci-dependencies-reusable-migration/.openspec.yaml new file mode 100644 index 0000000..032461f --- /dev/null +++ b/openspec/changes/fix-ci-dependencies-reusable-migration/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-09-02 diff --git a/openspec/changes/fix-ci-dependencies-reusable-migration/design.md b/openspec/changes/fix-ci-dependencies-reusable-migration/design.md new file mode 100644 index 0000000..33bc634 --- /dev/null +++ b/openspec/changes/fix-ci-dependencies-reusable-migration/design.md @@ -0,0 +1,51 @@ +## Context + +See proposal.md — Why. The current `ci_dependencies.yml` runs `actions/dependency-review-action` inline and hard-fails every PR because the Dependency Graph is unavailable. This repo already delegates its other security CI (`ci_security.yml`, `ci_scheduled.yml`) to `complytime/org-infra` reusable workflows pinned at `0c784711926c9864f027ec565fd7c06a382d80f8 # v0.7.1`, so the reusable-caller pattern and the org-infra trust boundary are already established and operationally proven here. The canonical dependency-review callers exist in `unbound-force/unbound-force/.github/workflows/ci_dependencies.yml` and `complytime/.github/.github/workflows/ci_dependencies.yml`. `safe-settings/settings.yml` sets org-wide `enableAutomatedSecurityFixes: true`; there is currently no `.github/dependabot.yml` in this repo. + +## Goals / Non-Goals + +**Goals:** +- Stop the dependency-review hard-fail on every PR to `main` by delegating to `reusable_deps_reviewer.yml` (which wraps the action with `continue-on-error: true`). +- Align `ci_dependencies.yml` with the org-standard reusable-caller pattern used by the sibling workflows in this repo and across the org. +- Add `.github/dependabot.yml` for the `github-actions` ecosystem so action SHA pins stay current and auditable. + +**Non-Goals:** +- Enabling GitHub Advanced Security / the Dependency Graph on this repo (explicitly out of plan). +- Enabling Dependabot auto-approve / auto-merge in this repo (deferred pending a separate threat-model review — see Open Questions). +- Adding Dependabot ecosystems beyond `github-actions` (this repo has no application dependency manifests that Dependabot would act on; `go.mod` exists but is tooling-only and out of scope for this change). +- Changing `ci_security.yml`, `ci_scheduled.yml`, Peribolos config, or safe-settings config. + +## Decisions + +**D1 — Delegate to org-infra reusables rather than adding `continue-on-error` to the inline step.** +The minimal hotfix would be to add `continue-on-error: true` (or an `if:` guard) to the existing inline `dependency-review-action` step. Rejected in favor of the reusable-caller migration because: (a) it converges on the org-standard pattern already used by the two sibling workflows in this repo, reducing per-repo drift; (b) it matches the parallel adoption tracked in `unbound-force/replicator#38` and the canonical callers in `unbound-force/unbound-force` and `complytime/.github`; (c) the reusable centralizes the soft-gate posture and result-surfacing so it stays consistent across repos. + +**D2 — Pin `complytime/org-infra` by full commit SHA with a version comment.** +Both new `uses:` lines will be pinned to `0c784711926c9864f027ec565fd7c06a382d80f8 # v0.7.1` — the exact SHA already used by `ci_security.yml` and `ci_scheduled.yml` in this repo. This matches the repo-wide pin-by-SHA convention and keeps org-infra references uniform. Alternative (floating tag like `@v0.7.1`) rejected: it violates the repo convention and re-introduces the mutable-ref supply-chain risk the SHA pin exists to prevent. Implementation note: before writing, confirm `reusable_deps_reviewer.yml` and `reusable_dependabot_reviewer.yml` exist at that SHA; if v0.7.1 predates those files, bump both org-infra references (all callers in this repo) to the earliest SHA that contains them. + +**D3 — Exclude the Dependabot auto-approve/auto-merge job from this repo.** +The canonical pattern includes an auto-approve flow. It is excluded here because `unbound-force/.github` controls org membership (Peribolos) and repository security settings (safe-settings) for the entire org; auto-merging Dependabot PRs into this repo is an elevated attack surface (e.g., a malicious action SHA substitution auto-merged into org policy). The `reusable_dependabot_reviewer.yml` caller is included (review/labeling only), but no auto-approve job is added. Re-enabling is gated on a separate threat-model review. + +**D4 — Add `.github/dependabot.yml` scoped to `github-actions` only.** +This is the only ecosystem relevant to this repo's CI, and it is the prerequisite that makes SHA pins maintainable. Weekly schedule, consistent with typical org config. No `open-pull-requests-limit` change beyond defaults is required for this change. `go.mod` exists but is tooling-only and deliberately excluded (see Non-Goals); no other ecosystem entries are added. + +**D5 — Permissions posture for the caller jobs.** +Verified against org-infra at the pinned SHA: both `reusable_deps_reviewer.yml` and `reusable_dependabot_reviewer.yml` are passive (review-only, output-emitting) and declare `permissions: { contents: read, issues: none, pull-requests: none }`. The `pull-requests: write` scope in the canonical `ci_dependencies.yml` is granted only at the job level for the comment/auto-approve jobs — which this change deliberately excludes. Therefore the migrated `ci_dependencies.yml` keeps a restrictive top-level block matching the canonical caller: `contents: read`, `issues: none`, `pull-requests: none`. No per-job `permissions:` overrides are needed because the two included callers require no write scopes. + +## Risks / Trade-offs + +- **Dependency-review signal becomes advisory (`continue-on-error: true`).** If GHAS/Dependency Graph is ever enabled on this repo, real vulnerability findings would no longer block PRs — they would pass silently. → Mitigation: the reusable captures the result as an output and surfaces it (job summary / annotation) rather than discarding it; document the soft-gate posture in the workflow. Re-evaluate the gate if GHAS is ever enabled. +- **CI availability coupling to `complytime/org-infra`.** An org-infra outage or a bad reusable revision could break the dependency CI job. → Mitigation: this coupling is identical to and no worse than the coupling already accepted by `ci_security.yml`/`ci_scheduled.yml`, and the SHA pin prevents unreviewed upstream changes from flowing in. +- **Adding Dependabot PRs where there were none.** Enabling `github-actions` updates will start generating Dependabot PRs. → Mitigation: acceptable and desired (keeps SHAs current); no auto-approve means every PR still requires human review, so there is no unattended-merge risk. + +## Migration Plan + +1. Replace the `dependency-review` job (checkout + `dependency-review-action` steps) in `ci_dependencies.yml` with `call_deps_reviewer` and `call_dependabot_reviewer` caller jobs pinned per D2. +2. Add `.github/dependabot.yml` with the `github-actions` ecosystem entry. +3. Validate YAML locally (`make sanity` / `yamllint`). +4. Open a PR and confirm the Dependency Review check no longer hard-fails and no inline `dependency-review-action` step remains. +5. Rollback: revert the two files (`ci_dependencies.yml`, `.github/dependabot.yml`) to their prior state; there is no state or data migration to unwind. + +## Open Questions + +- **Should Dependabot auto-approve ever be enabled for `unbound-force/.github`?** Deferred to a separate security review with an explicit threat model and, if approved, compensating controls (e.g., required human review on top of auto-approve, restricted Dependabot scope). This does not affect the specs, approach, or task breakdown of this change — auto-approve is out of scope here regardless of the eventual answer. diff --git a/openspec/changes/fix-ci-dependencies-reusable-migration/proposal.md b/openspec/changes/fix-ci-dependencies-reusable-migration/proposal.md new file mode 100644 index 0000000..7443ac6 --- /dev/null +++ b/openspec/changes/fix-ci-dependencies-reusable-migration/proposal.md @@ -0,0 +1,29 @@ +## Why + +`.github/workflows/ci_dependencies.yml` runs `actions/dependency-review-action@da24556b… (v4.7.1)` as a bare inline step with no `continue-on-error` guard. Because the Dependency Graph feature is not enabled on this repo (and there is no plan to enable GitHub Advanced Security here), the action hard-fails on **every** pull request to `main` with "Dependency review is not supported on this repository". This is a 100%-reproducible blocking CI gate, and the inline form also diverges from the org-standard reusable-caller pattern already adopted in this repo's `ci_security.yml` / `ci_scheduled.yml` and tracked for `unbound-force/replicator#38`. + +## What Changes + +- Replace the inline `dependency-review` job in `ci_dependencies.yml` with a caller to `complytime/org-infra/.github/workflows/reusable_deps_reviewer.yml`, which wraps `dependency-review-action` with `continue-on-error: true` so a missing Dependency Graph no longer blocks PRs; the result is captured as an output and surfaced informally rather than as a hard gate. +- Add a caller to `complytime/org-infra/.github/workflows/reusable_dependabot_reviewer.yml` for Dependabot-authored PRs, matching the canonical pattern in `unbound-force/unbound-force` and `complytime/.github`. +- **Explicitly exclude** the Dependabot auto-approve / auto-merge job from scope for this repo. Because `unbound-force/.github` governs org policy (Peribolos membership, safe-settings, rulesets), auto-approving Dependabot PRs here is an elevated attack surface; it is deferred pending a separate threat-model review. +- Pin both `uses:` references to `complytime/org-infra` at a full commit SHA with a version comment, matching the existing pin-by-SHA convention (`0c784711…926c9864f027ec565fd7c06a382d80f8 # v0.7.1`) already used by `ci_security.yml` and `ci_scheduled.yml`. +- Add `.github/dependabot.yml` for the `github-actions` ecosystem so action SHA updates are proposed automatically. This complements `safe-settings/settings.yml`'s org-wide `enableAutomatedSecurityFixes: true` and keeps action pins auditable. +- Remove the existing inline `actions/dependency-review-action` step and its `actions/checkout` step. + +## Capabilities + +### New Capabilities +- `ci-dependencies`: The dependency CI workflow (`ci_dependencies.yml`) SHALL delegate dependency review to org-infra reusable workflows rather than running an inline `dependency-review-action` step, and the repo SHALL declare a `github-actions` Dependabot configuration. + +### Modified Capabilities + + +## Impact + +- `.github/workflows/ci_dependencies.yml` — inline `dependency-review` job (checkout + dependency-review-action steps) replaced by reusable-workflow callers. +- `.github/dependabot.yml` — new file declaring the `github-actions` ecosystem for automated action SHA updates. +- New CI availability coupling to `complytime/org-infra` for the dependency-review callers — identical in nature to the coupling already accepted by `ci_security.yml` and `ci_scheduled.yml`. +- The Dependency Review check name visible in GitHub CI changes from the inline job to the reusable-workflow caller job name(s) (`General`, `Dependabot`). +- No source files, Peribolos config, or safe-settings config are affected. +- Unblocks all future PRs to `main`, which currently hit a hard `dependency-review` failure. Same adoption is tracked in parallel for `unbound-force/replicator#38`. diff --git a/openspec/changes/fix-ci-dependencies-reusable-migration/specs/ci-dependencies/spec.md b/openspec/changes/fix-ci-dependencies-reusable-migration/specs/ci-dependencies/spec.md new file mode 100644 index 0000000..a83e13f --- /dev/null +++ b/openspec/changes/fix-ci-dependencies-reusable-migration/specs/ci-dependencies/spec.md @@ -0,0 +1,50 @@ +## Purpose + +Defines the dependency CI workflow's obligations for dependency review on pull requests — delegating execution to org-infra reusable workflows rather than running an inline `dependency-review-action` step — and the repository's obligation to declare a `github-actions` Dependabot configuration for automated action SHA updates. + +## ADDED Requirements + +### Requirement: Dependency review via org-infra reusable workflow +The dependency CI workflow SHALL delegate dependency review to `complytime/org-infra/.github/workflows/reusable_deps_reviewer.yml` rather than running an inline `actions/dependency-review-action` step. + +#### Scenario: Dependency review runs on pull_request to main +- **WHEN** a pull request targets the `main` branch +- **THEN** the `reusable_deps_reviewer.yml` caller job is triggered and its Dependency Review check is reported in CI + +#### Scenario: Missing Dependency Graph no longer blocks CI +- **WHEN** a pull request is opened against `main` and the Dependency Graph feature is not enabled on the repository +- **THEN** the dependency CI workflow SHALL NOT hard-fail the pull request +- **AND** the dependency review outcome is surfaced informally rather than as a blocking gate + +#### Scenario: Inline dependency-review-action step removed +- **WHEN** `ci_dependencies.yml` is read +- **THEN** it SHALL NOT contain an inline `uses: actions/dependency-review-action` step + +### Requirement: Dependabot PR review via org-infra reusable workflow +The dependency CI workflow SHALL delegate review of Dependabot-authored pull requests to `complytime/org-infra/.github/workflows/reusable_dependabot_reviewer.yml`. + +#### Scenario: Dependabot reviewer caller present +- **WHEN** `ci_dependencies.yml` is read +- **THEN** it SHALL contain a caller job for `reusable_dependabot_reviewer.yml` + +### Requirement: Dependabot auto-approve excluded from this repository +The dependency CI workflow SHALL NOT enable Dependabot auto-approve or auto-merge behavior, because this repository governs organization policy (Peribolos membership, safe-settings, rulesets) and auto-approval represents an elevated attack surface pending a separate threat-model review. + +#### Scenario: No auto-approve job configured +- **WHEN** `ci_dependencies.yml` is read +- **THEN** it SHALL NOT contain a job that automatically approves or auto-merges Dependabot pull requests + +### Requirement: Caller workflows pin org-infra reusable by SHA +The `uses:` reference to each org-infra reusable workflow in `ci_dependencies.yml` SHALL be pinned to a full commit SHA with an inline version comment, following the same pin-by-SHA convention used throughout this repository. + +#### Scenario: SHA pin present for each caller +- **WHEN** `ci_dependencies.yml` is read +- **THEN** each `uses: complytime/org-infra/...` line includes a full 40-character SHA and an inline version comment (e.g., `# v0.7.1`) + +### Requirement: Dependabot configuration for the github-actions ecosystem +The repository SHALL declare a `.github/dependabot.yml` configuration covering the `github-actions` package ecosystem so that action SHA updates are proposed automatically. As part of this change, no ecosystem entries beyond `github-actions` SHALL be added (the `go.mod` tooling manifest is deliberately out of scope). + +#### Scenario: dependabot.yml declares github-actions ecosystem +- **WHEN** `.github/dependabot.yml` is read +- **THEN** it SHALL contain an entry with `package-ecosystem: "github-actions"` and a defined update schedule +- **AND** it SHALL NOT contain any `package-ecosystem` entry other than `github-actions` diff --git a/openspec/changes/fix-ci-dependencies-reusable-migration/tasks.md b/openspec/changes/fix-ci-dependencies-reusable-migration/tasks.md new file mode 100644 index 0000000..4764626 --- /dev/null +++ b/openspec/changes/fix-ci-dependencies-reusable-migration/tasks.md @@ -0,0 +1,24 @@ +## 1. Confirm org-infra reusable references + +- [x] 1.1 Verify `reusable_deps_reviewer.yml` and `reusable_dependabot_reviewer.yml` exist in `complytime/org-infra` at SHA `0c784711926c9864f027ec565fd7c06a382d80f8` (v0.7.1). Verify by inspecting the org-infra repo at that ref; if either file is absent at that SHA, identify the earliest SHA that contains both and note it for use in tasks 2.x. +- [x] 1.2 If (and only if) the SHA is bumped from v0.7.1 in 1.1, also update the `uses:` pin in `ci_security.yml` and `ci_scheduled.yml` to the same new SHA so all org-infra callers in this repo stay uniform (design.md D2). Verify by grepping for `complytime/org-infra` and confirming every hit uses the identical SHA. If the SHA remains v0.7.1, this task is a no-op — mark it complete. + +## 2. Migrate ci_dependencies.yml to reusable callers + +- [x] 2.1 Remove the inline `dependency-review` job from `.github/workflows/ci_dependencies.yml` (both the `actions/checkout` and `actions/dependency-review-action` steps). Verify no `uses: actions/dependency-review-action` line remains (`grep` returns nothing). +- [x] 2.2 Add a `call_deps_reviewer` job (name: `General`) calling `complytime/org-infra/.github/workflows/reusable_deps_reviewer.yml` pinned to the confirmed SHA with a version comment. Ensure the inline version comment (`# vX.Y.Z`) matches the actual release tag of the chosen SHA. Verify the `uses:` line includes a full 40-char SHA and a matching version comment. +- [x] 2.3 Add a `call_dependabot_reviewer` job (name: `Dependabot`) calling `complytime/org-infra/.github/workflows/reusable_dependabot_reviewer.yml` pinned to the same SHA with a matching version comment. Verify no auto-approve/auto-merge job is added anywhere in the file. +- [x] 2.4 Preserve the workflow trigger (`pull_request` to `main`) and set a restrictive top-level `permissions:` block — `contents: read`, `issues: none`, `pull-requests: none` — matching the canonical caller and the reusables' own passive permissions (verified: both reusables declare `pull-requests: none` at the pinned SHA; no write scope is needed for the two included callers). Verify the file's `on:` and `permissions:` blocks are correct for the callers. +- [x] 2.5 Add an inline comment in `ci_dependencies.yml` above the caller jobs noting that the dependency review is soft-gated (`continue-on-error: true` is applied inside the reusable) and that this gate should be re-evaluated if the Dependency Graph / GitHub Advanced Security is ever enabled on this repo (design.md Risk 1). Verify the comment is present. + +## 3. Add Dependabot configuration + +- [x] 3.1 Create `.github/dependabot.yml` with a `version: 2` config and one `updates` entry for `package-ecosystem: "github-actions"`, `directory: "/"`, and a weekly `schedule`. Verify the file contains `package-ecosystem: "github-actions"` and a defined schedule. + +## 4. Validate and verify + +- [x] 4.1 Run `make sanity` (yamllint + Go tests) and confirm it passes, including YAML validation of the two changed/new files. (Verified equivalents: `make lint`, `go vet ./...`, `go build ./...`, `go test ./...`, and `yamllint` on both changed files all pass with exit 0. Note: literal `make sanity` also runs `git diff --exit-code`, which is expected to fail pre-commit while changes are uncommitted; that gate is satisfied by `/uf.finale` at commit time.) +- [x] 4.2 Open a PR against `main` and confirm the Dependency Review check no longer hard-fails (the `General` caller job runs without blocking the PR), satisfying the `ci-dependencies` spec scenarios. Note that the `call_dependabot_reviewer` job is conditional on the PR author being Dependabot and will NOT fire on a human-authored validation PR; verify its correctness by inspecting the job's `if:` condition in the merged YAML rather than expecting it to run on the test PR. (DEFERRED to PR time / `/uf.finale`: PR creation is not available in this environment. Static verification done in lieu: the caller `uses:` targets `reusable_deps_reviewer.yml`, which was confirmed at the pinned SHA to wrap `dependency-review-action` with `continue-on-error: true` — so the hard-fail is structurally resolved; and `reusable_dependabot_reviewer.yml`'s inner job is gated on `github.event.pull_request.user.login == 'dependabot[bot]'`, confirmed at the pinned SHA.) + + +