From 25a599f12d5a1800e5abc0ff62344942ac0819e8 Mon Sep 17 00:00:00 2001 From: alexander <259883306+AlexHardAtWork@users.noreply.github.com> Date: Fri, 9 Oct 2026 10:25:24 +0000 Subject: [PATCH 1/2] ci: manage dev-kit's own dependencies with Renovate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dev-kit had no renovate.json, so the actions it pins internally only moved when someone bumped them by hand — seventeen references across ten actions, including the installer, cachix and osv-scanner that every consumer's CI runs through. Centralising the governance workflows made that load-bearing: those pins used to live in each consumer, where Renovate managed them, and now they live only here. The config is the base preset alone, as example/renovate.json already shows. There is no go.mod or tools.lock here, and goVersion is an mkShell parameter rather than a pin, so :go would manage nothing. Neither Makefile matches the dev-kit regex manager — both track main rather than a release tag — so this does not bump its own version. renovate-auto-approve is now called the way a consumer calls it, by path rather than by SHA: a same-repo reusable workflow can be addressed locally, which always exercises the version in the branch and avoids a self-reference needing a release's own SHA before that commit exists. Writing that stub turned up a documentation bug. The README and the workflow's own header both describe the trigger as `pull_request`, which defaults to opened, reopened and synchronize. The workflow reacts to labels: Renovate usually adds the automerge label after opening the PR, and the revoke path fires when a PR gains the block label or loses the automerge one. A stub following the documentation silently never approves anything. Both now say which types are needed, and the header shows the `on:` block instead of leaving it implied. Closes #40 --- .../workflows/renovate-auto-approve-self.yml | 24 +++++++++++++++++++ .github/workflows/renovate-auto-approve.yml | 10 ++++++-- README.md | 18 +++++++------- renovate.json | 6 +++++ 4 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/renovate-auto-approve-self.yml create mode 100644 renovate.json diff --git a/.github/workflows/renovate-auto-approve-self.yml b/.github/workflows/renovate-auto-approve-self.yml new file mode 100644 index 0000000..612a001 --- /dev/null +++ b/.github/workflows/renovate-auto-approve-self.yml @@ -0,0 +1,24 @@ +name: renovate-auto-approve + +# dev-kit calling its own reusable workflow, the way a consumer calls it. The +# reference is local rather than `opendefensecloud/dev-kit/...@`: a +# same-repo reusable workflow can be addressed by path, which keeps this honest +# — it always exercises the version in the branch — and avoids a self-reference +# that would need a release's own SHA before that commit exists. +# +# The label triggers are not optional. Renovate adds the automerge label, often +# after opening the PR, and the workflow revokes its approval when a PR gains +# the block label or loses the automerge one. Without `labeled`/`unlabeled` the +# stub silently never fires for either. +on: + pull_request: + types: [opened, reopened, synchronize, labeled, unlabeled] + +permissions: + contents: read + +jobs: + approve: + uses: ./.github/workflows/renovate-auto-approve.yml + permissions: + pull-requests: write diff --git a/.github/workflows/renovate-auto-approve.yml b/.github/workflows/renovate-auto-approve.yml index f449cfc..109d33b 100644 --- a/.github/workflows/renovate-auto-approve.yml +++ b/.github/workflows/renovate-auto-approve.yml @@ -1,12 +1,18 @@ name: renovate-auto-approve -# Reusable workflow. A consuming repo triggers it on pull_request and calls it: +# Reusable workflow. A consuming repo calls it like this — the label triggers are +# not optional, since Renovate usually adds the automerge label after opening the +# PR, and the revoke path reacts to labels changing: # +# on: +# pull_request: +# types: [opened, reopened, synchronize, labeled, unlabeled] +# permissions: +# contents: read # jobs: # approve: # uses: opendefensecloud/dev-kit/.github/workflows/renovate-auto-approve.yml@ # permissions: -# contents: read # pull-requests: write # # It approves a Renovate PR that carries the automerge label, added by Renovate, diff --git a/README.md b/README.md index 8aa1f25..3636e73 100644 --- a/README.md +++ b/README.md @@ -276,15 +276,15 @@ keeps one small stub per workflow, which sets the triggers and the permissions and calls dev-kit. Pin each stub to a SHA like any other action; Renovate bumps them together with `DEV_KIT_VERSION`. -| Workflow | Stub triggers | Needs | -| --------------------------- | ------------------------------------------------------ | ------------------------------- | -| `update-action-pins.yml` | `pull_request` | | -| `conventional-commits.yml` | `pull_request` (opened, edited, synchronize, reopened) | `.commitlintrc.yml` in the repo | -| `osv-scanner.yml` | `pull_request`, `push` to main, weekly | optional `scan-args` input | -| `issues-add-labels.yaml` | `issues` (opened, reopened) | | -| `issues-add-to-project.yml` | `issues`, `pull_request` (opened) | `ADD_TO_PROJECT_PAT` secret | -| `renovate-auto-approve.yml` | `pull_request` | see the header of the workflow | -| `renovate-dev-kit-lock.yml` | `pull_request` (opened, synchronize, reopened) | `DEV_KIT_BOT_APP_*` org secrets | +| Workflow | Stub triggers | Needs | +| --------------------------- | ------------------------------------------------------------------ | ------------------------------- | +| `update-action-pins.yml` | `pull_request` | | +| `conventional-commits.yml` | `pull_request` (opened, edited, synchronize, reopened) | `.commitlintrc.yml` in the repo | +| `osv-scanner.yml` | `pull_request`, `push` to main, weekly | optional `scan-args` input | +| `issues-add-labels.yaml` | `issues` (opened, reopened) | | +| `issues-add-to-project.yml` | `issues`, `pull_request` (opened) | `ADD_TO_PROJECT_PAT` secret | +| `renovate-auto-approve.yml` | `pull_request` (opened, reopened, synchronize, labeled, unlabeled) | see the header of the workflow | +| `renovate-dev-kit-lock.yml` | `pull_request` (opened, synchronize, reopened) | `DEV_KIT_BOT_APP_*` org secrets | No stub triggers on `merge_group`, as no repo uses a merge queue. A repo that turns one on must add the trigger to every stub whose check it requires, and diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..d769d9f --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "github>opendefensecloud/renovate-config" + ] +} From 45e7f5978c999d377783350a10a63bd2d8e931ea Mon Sep 17 00:00:00 2001 From: alexander <259883306+AlexHardAtWork@users.noreply.github.com> Date: Fri, 9 Oct 2026 11:23:27 +0000 Subject: [PATCH 2/2] fix: grant contents:read to the auto-approve job, not only the workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A job-level permissions block replaces the workflow-level one rather than adding to it, and a called workflow cannot elevate what its caller grants — GitHub documents permissions as maintained or reduced, never elevated, through a reusable-workflow chain. Passing only pull-requests:write therefore left the called workflow without contents:read, which it needs for the repository activity API: the check on who pushed the head, and the revoke path. The documented example had the same split, with contents:read at the workflow level and only pull-requests:write on the job, so a stub copied from it would fail the first time a Renovate PR actually carried the automerge label — the earlier calls return before reaching the activity API, so nothing fails until then. solution-arsenal's stub has that shape today and needs the same fix. --- .github/workflows/renovate-auto-approve-self.yml | 5 +++++ .github/workflows/renovate-auto-approve.yml | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/.github/workflows/renovate-auto-approve-self.yml b/.github/workflows/renovate-auto-approve-self.yml index 612a001..4cd6c09 100644 --- a/.github/workflows/renovate-auto-approve-self.yml +++ b/.github/workflows/renovate-auto-approve-self.yml @@ -21,4 +21,9 @@ jobs: approve: uses: ./.github/workflows/renovate-auto-approve.yml permissions: + # Both are needed, and a job-level block replaces the workflow-level one + # rather than adding to it. A called workflow cannot elevate what the + # caller grants, so omitting contents here leaves the repository activity + # API — the pusher check, and the revoke path — without access. + contents: read pull-requests: write diff --git a/.github/workflows/renovate-auto-approve.yml b/.github/workflows/renovate-auto-approve.yml index 109d33b..0791736 100644 --- a/.github/workflows/renovate-auto-approve.yml +++ b/.github/workflows/renovate-auto-approve.yml @@ -13,8 +13,15 @@ name: renovate-auto-approve # approve: # uses: opendefensecloud/dev-kit/.github/workflows/renovate-auto-approve.yml@ # permissions: +# contents: read # pull-requests: write # +# Both permissions belong on the job, not only at the workflow level: a +# job-level block replaces the workflow-level one rather than adding to it, and +# a called workflow cannot elevate what its caller grants. Omitting contents +# there leaves the repository activity API — the pusher check, and the revoke +# path — without access. +# # It approves a Renovate PR that carries the automerge label, added by Renovate, # and not the block label (security), and only while the branch still holds # exactly one commit, authored by Renovate and signed, and pushed by Renovate.