Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
version: 2

# Maintenance mode: ViTransfer takes security patches weekly and little else.
# Everything targets dev; the weekly-security-release workflow promotes dev to
# main.
# Version updates target dev; the weekly-security-release workflow promotes dev
# to main.
#
# Security updates do NOT follow target-branch — Dependabot raises those against
# the default branch, main. sync-main-to-dev.yml carries them back to dev so the
# release train does not keep shipping a lockfile the advisory already covers.
#
# Major version updates are ignored everywhere: in maintenance mode a runtime
# or framework major is not worth the churn. The release run's
Expand All @@ -21,16 +25,17 @@ updates:
prefix: deps
prefix-development: deps-dev
groups:
# One PR a week for routine churn, one for advisories.
# One PR a week for routine churn. There is deliberately no
# security-updates group here: `target-branch` applies to version updates
# only, so Dependabot opens security PRs against the default branch and
# ignores this file's groups and commit prefix entirely. A group here
# would read as if advisories arrive on dev, and they do not.
npm:
applies-to: version-updates
patterns: ["*"]
update-types:
- patch
- minor
npm-security:
applies-to: security-updates
patterns: ["*"]
ignore:
- dependency-name: "*"
update-types:
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/docker-integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ name: Docker Integration Tests
#
# No paths filter on PRs: "Test Summary" is a required check on dev, and a
# required check that never runs leaves auto-merge pending forever.
#
# PRs to main are tested too. Setting `target-branch` in dependabot.yml turns
# off security updates from that config, so GitHub opens them against the
# default branch instead — main. Those PRs used to get CodeQL and nothing else,
# which is how a browserslist advisory sat on main untested while the release
# train kept building the vulnerable lockfile from dev.
on:
push:
branches: [dev]
Expand All @@ -18,7 +24,7 @@ on:
- '.github/workflows/docker-*.yml'
- '.github/workflows/test-dev-*.yml'
pull_request:
branches: [dev]
branches: [dev, main]
workflow_dispatch:

permissions:
Expand Down
84 changes: 84 additions & 0 deletions .github/workflows/sync-main-to-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Sync main to dev

# main moves without dev twice, and only one of them matters. The release train
# pushing the version bump and changelog is by design — dev never carries
# VERSION, so it lags every week and that is fine. A Dependabot security PR
# landing is not: those target the default branch (see dependabot.yml), so dev —
# the branch every release is cut from — keeps a lockfile the advisory already
# covers while main looks patched. That is how v1.3.5 built a vulnerable tree
# after the fix was already sitting on main, and how the release then hit a
# lockfile conflict waiting to happen the next time dev touched the same file.
#
# So this reports on what is stranded, not on how far behind dev is. Counting
# commits would file an issue after every release, weekly, for the state that is
# supposed to be true.
#
# It opens an issue rather than a PR: dev requires the "Test Summary" check, and
# a PR opened with GITHUB_TOKEN does not trigger `pull_request` workflows, so
# the check would never run and the PR would block forever. There is no PAT.

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read
issues: write

jobs:
sync:
name: Check dev is in step
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Report anything stranded on main
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git fetch origin main dev --quiet

# Release commits are authored by github-actions[bot] and dev is meant
# to lag them. Everything else on main is a patch that never reached
# the branch the next release builds from.
stranded=$(git log --format='%an %h %s' origin/dev..origin/main \
| grep -v '^github-actions\[bot\] ' || true)

# One open issue at a time, closed again once dev catches up, so a
# stale one can never mask the next strand.
open_issue=$(gh issue list --label sync --state open --json number -q '.[0].number')

if [ -z "$stranded" ]; then
echo "Nothing stranded: dev has every patch main does."
if [ -n "$open_issue" ]; then
gh issue close "$open_issue" --comment "dev has caught up with main."
fi
exit 0
fi

echo "Stranded on main:"
echo "$stranded"

if [ -n "$open_issue" ]; then
echo "Issue #$open_issue is already tracking this."
exit 0
fi

# Its own label, not `ci`: that one is the release-failure report's,
# and sharing it would make either issue suppress the other.
gh label create sync \
--description "A patch on main has not reached dev" \
--color 1d76db --force

gh issue create \
--title "Patches on main have not reached dev" \
--label sync \
--body "$(printf '%s\n\n```\n%s\n```\n\n%s\n' \
"These commits are on main but not on dev. The weekly release is cut from dev, so until they are carried back it keeps building without them — and the release's own merge will conflict as soon as dev touches the same files." \
"$stranded" \
"Carry them over with \`git checkout dev && git merge origin/main && git push origin dev\`.")"
9 changes: 7 additions & 2 deletions .github/workflows/weekly-security-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,13 @@ jobs:
run: |
npm ci --legacy-peer-deps --ignore-scripts
# Majors and unfixable advisories never reach auto-merge, so this is
# the backstop that makes them visible rather than silently skipped.
npm audit --audit-level=high || echo "::warning::npm audit reports unresolved advisories — review before approving"
# the backstop that catches them rather than silently skipping them.
#
# It fails rather than warns: the Dockerfile runs the same gate during
# `build`, which is after `publish` has merged dev and pushed the
# version bump. A warning here bought nothing and left main carrying
# release commits for a version that never tagged or shipped.
npm audit --audit-level=high

# Individual PRs are tested against dev as it was when they opened, not
# against the final merged state. With no human approving the release, this
Expand Down