Skip to content

Commit 8aaa6fe

Browse files
authored
ci: Dependabot PRs merge themselves once CI passes, and wait 7 days after a release (#422)
1 parent 7efff38 commit 8aaa6fe

4 files changed

Lines changed: 38 additions & 3 deletions

File tree

‎.github/dependabot.yml‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
33

44
version: 2
5+
# Updates merge themselves once CI passes and reach prod with the nightly deploy, so each release waits 7 days in case it gets yanked
56
updates:
67
# Python dependencies (uv/pip)
78
- package-ecosystem: "uv"
89
directory: "/backend"
910
schedule:
1011
interval: "weekly"
12+
cooldown:
13+
default-days: 7
1114
commit-message:
1215
prefix: "deps(python)"
1316
groups:
@@ -19,6 +22,8 @@ updates:
1922
directory: "/frontend"
2023
schedule:
2124
interval: "weekly"
25+
cooldown:
26+
default-days: 7
2227
commit-message:
2328
prefix: "deps(npm)"
2429
groups:
@@ -36,6 +41,8 @@ updates:
3641
- "/frontend"
3742
schedule:
3843
interval: "weekly"
44+
cooldown:
45+
default-days: 7
3946
commit-message:
4047
prefix: "deps(docker)"
4148
groups:
@@ -47,6 +54,8 @@ updates:
4754
directory: "/"
4855
schedule:
4956
interval: "weekly"
57+
cooldown:
58+
default-days: 7
5059
commit-message:
5160
prefix: "deps(docker)"
5261
groups:
@@ -64,6 +73,8 @@ updates:
6473
- "/.github/actions/*"
6574
schedule:
6675
interval: "weekly"
76+
cooldown:
77+
default-days: 7
6778
commit-message:
6879
prefix: "deps(actions)"
6980
groups:
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Dependabot Auto-merge
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
7+
permissions: {}
8+
9+
jobs:
10+
auto-merge:
11+
name: Enable auto-merge
12+
if: github.event.pull_request.user.login == 'dependabot[bot]'
13+
runs-on: ubuntu-latest
14+
steps:
15+
# A merge enabled with GITHUB_TOKEN starts no workflows on main, so CI would build no image to deploy
16+
- run: gh pr merge --auto --squash "$PR_URL"
17+
env:
18+
PR_URL: ${{ github.event.pull_request.html_url }}
19+
GH_TOKEN: ${{ secrets.AUTOMERGE_TOKEN }}

‎docs/operations/cicd.md‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# CI/CD pipeline
22

3-
CI is six GitHub Actions workflows, two composite actions and a Dependabot config. Three of the workflows form the release chain: CI checks the code and builds and tests the images, Docker Scan & Promote scans them and moves `latest`, and Release & Deploy tags a CalVer release and deploys it. The first two run on every merge to `main`, so `latest` is always the newest build that passed both. Release & Deploy doesn't follow them: it runs once a day at 23:00 Europe/Berlin, or by hand, and releases whatever `latest` is then. A manual run of Docker Scan & Promote or Release & Deploy starts partway along the chain, without the steps before it.
3+
CI is seven GitHub Actions workflows, two composite actions and a Dependabot config. Three of the workflows form the release chain: CI checks the code and builds and tests the images, Docker Scan & Promote scans them and moves `latest`, and Release & Deploy tags a CalVer release and deploys it. The first two run on every merge to `main`, so `latest` is always the newest build that passed both. Release & Deploy doesn't follow them: it runs once a day at 23:00 Europe/Berlin, or by hand, and releases whatever `latest` is then. A manual run of Docker Scan & Promote or Release & Deploy starts partway along the chain, without the steps before it.
44

55
```mermaid
66
graph LR
@@ -30,6 +30,7 @@ graph LR
3030
| SBOM & Supply Chain Security | sbom-compliance.yml | push to `main`; PRs to `main` that change a dependency manifest; Sundays at midnight |
3131
| Labels | labels.yml | PRs opened or updated (`pull_request_target`), issues opened or edited |
3232
| Label Sync | label-sync.yml | push and PRs to `main` touching the label files or issue forms; Mondays at 03:00; manual |
33+
| Dependabot Auto-merge | dependabot-auto-merge.yml | Dependabot's PRs to `main` |
3334

3435
Besides the tests, CI runs one job per check. Ruff runs `ruff check`. MyPy runs `mypy --strict` with `SECRET_KEY` from the `TEST_SECRET_KEY` secret. Grimp runs `python -m scripts.check_orphan_modules`, which fails on any module nothing imports except `app.main` and `__init__.py` files that hold only a docstring. Security Scanning runs `uvx --no-build --python 3.14 bandit==1.9.4 -r backend/ -x backend/tests/ -ll`. Bandit parses with the Python it runs on, so on anything older it skips the files that use 3.14 syntax such as `except A, B:` without failing. The frontend job runs ESLint, Stylelint, the Prettier check, `svelte-check --fail-on-warnings` and a production build. The SBOM workflow generates SPDX SBOMs for backend and frontend with `anchore/sbom-action`, scans them with Grype through `anchore/scan-action` (cutoff `high`, `fail-build: false`) and uploads SARIF to the Security tab.
3536

@@ -122,7 +123,11 @@ k3s is the `rancher/k3s:v1.37.1-k3s1` image, pinned by digest in `K3S_IMAGE` in
122123

123124
## Dependabot
124125

125-
Dependabot checks weekly and opens one grouped PR per ecosystem: uv in `backend/`, npm in `frontend/`, the three Dockerfiles, the images in docker-compose.yaml, and GitHub Actions including the composite actions. It ignores TypeScript 7 and later, because typescript-eslint and svelte-check need the compiler API that 7 dropped, and Jaeger 2.21 and later, because Grafana can't search it yet.
126+
Dependabot checks weekly and opens one grouped PR per ecosystem: uv in `backend/`, npm in `frontend/`, the two Dockerfiles, the images in docker-compose.yaml, and GitHub Actions including the composite actions. It ignores TypeScript 7 and later, because typescript-eslint and svelte-check need the compiler API that 7 dropped, and Jaeger 2.21 and later, because Grafana can't search it yet.
127+
128+
Each update waits 7 days after its release (`cooldown`), so a broken or malicious release has time to get yanked before a PR picks it up. Security updates skip the wait.
129+
130+
Dependabot Auto-merge turns on auto-merge for every Dependabot PR, majors included, so it merges once All checks pass and goes out with the next nightly release. It does that with `AUTOMERGE_TOKEN`, a fine-grained token stored as a Dependabot secret, since a merge enabled with `GITHUB_TOKEN` starts no workflows on `main`: CI wouldn't build images for it and `latest` wouldn't move. The token has Contents, Pull requests and Workflows read and write on this repo only, Workflows because the Actions group edits workflow files. When it expires, the job fails and the PRs wait for a manual merge.
126131

127132
## Running checks locally
128133

‎docs/security/supply-chain.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ CI's Security Scanning job runs `uvx --no-build --python 3.14 bandit==1.9.4 -r b
1818

1919
Every GitHub Action, including those used inside the composite actions, is pinned to a full commit SHA with the version in a comment. The Trivy action is pinned to the commit of v0.36.0, since its older tags were deleted upstream. Tools are pinned to exact versions (bandit, Trivy, mkdocs and its plugins), Python installs are `uv sync --frozen --no-build` from the lockfile, and npm installs are `npm ci --ignore-scripts`. The k3s image is pinned by digest.
2020

21-
Dependabot opens one grouped PR per ecosystem every week, for uv, npm, the Dockerfiles, the compose images and the GitHub Actions, and updates SHA and version comment together. Details are in [CI/CD](../operations/cicd.md#dependabot).
21+
Dependabot opens one grouped PR per ecosystem every week, for uv, npm, the Dockerfiles, the compose images and the GitHub Actions, and updates SHA and version comment together. A release waits 7 days before Dependabot proposes it, since these PRs merge themselves once CI passes. Details are in [CI/CD](../operations/cicd.md#dependabot).
2222

2323
## Running it locally
2424

0 commit comments

Comments
 (0)