redline-sync.yml
Distributes standards and gate callers to onboarded repos as PRs. Active — runs on a push to main touching standards/, cli/render/ or templates/, and on workflow_dispatch.
What this is
Distributes the rendered standards to onboarded repos as pull requests — never as pushes, never as merges. Targets come from registry.json, derived nightly from the .redline.json each onboarded repo carries rather than a list anyone maintains.
How to onboard it
Nothing to install: it lives in this repository and runs here. It distributes to registered repositories only, so a repository that has never appeared in registry.json has never been a target.
- Lives in: This (source) repo.
- Trigger: push to main touching standards/**, cli/render/**, templates/** or the PR template, plus workflow_dispatch with dry-run and only <owner/name> inputs.
How to use it
Nothing, normally — a push to standards/ triggers it. Run it by hand with the dry-run input first when changing the renderer itself, and use only <owner/name> to rehearse against a single repository before the estate.
What a run does, in order:
- Verifies this repo's own rendered artifacts are current (scripts/render-self.mjs --check) before distributing anything — a sync of stale artifacts would propagate the staleness to every onboarded repo at once, as a pull request each team is asked to trust.
- Builds the CLI and runs `redline sync`, which reads registry.json, renders each target's artifacts against its own recorded profile and vendors, and opens or updates one pull request per repo that is behind.
Active. GitHub only — Azure DevOps sync is outstanding, and a registered Azure repository is reported as unsupported rather than skipped silently.
Expected output
One pull request per registered repository that is behind, titled with the standards version and listing the generated files it changes. A repository already carrying the current render gets nothing rather than an empty pull request, and one whose previous sync pull request is still open has that one updated rather than a second opened. Content above each REDLINE:BEGIN marker is never touched.
How to edit it
- Edit the YAML in workflows/ or .github/workflows/workflows/ holds files destined for other repositories; .github/workflows/ is this repository's own CI. The two are not interchangeable — check where this one lives before editing.
- actionlintCI lints .github/workflows/*.yml, workflows/*.yml and templates/redline.yml together. workflows/ is pointed at explicitly because actionlint's own discovery would skip it.
- node scripts/check-pins.mjsIf you add a third-party action, pin it to a 40-character commit SHA with a trailing # vX.Y.Z comment. First-party actions/* are referenced by tag. The pin checker re-resolves the SHA against the tag the comment claims.
- node scripts/validate.mjsAsserts the workflow files the bundle depends on still exist, and that the gate's job ids still match the required check name derived from them.
The full file
# Lives in the Redline source repo. Distributes the rendered standards to onboarded
# repos as pull requests — never as direct pushes, and never as a merge.
#
# Targets come from registry.json, the derived register the Redline Registry workflow
# refreshes nightly from the .redline.json each onboarded repo carries. Nothing here is
# hand-maintained: a repository that removes Redline leaves the register, and stops being
# a target, without anyone editing a list.
#
# What a repository does with its sync pull request is the repository's decision. Sync
# makes the change available; the estate dashboard's coverage figure is what makes an
# ignored one visible.
name: Redline Sync
on:
push:
branches: [main]
paths:
- 'standards/**'
- 'cli/render/**'
- 'templates/**'
- '.github/pull_request_template.md'
workflow_dispatch:
inputs:
dry-run:
description: Print the plan without opening pull requests
type: boolean
default: false
only:
description: Sync a single repo, as owner/name
type: string
required: false
permissions:
contents: read
concurrency:
group: redline-sync
cancel-in-progress: false
jobs:
sync:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Install
run: npm ci
# A sync that distributed stale artifacts would propagate the staleness to
# every onboarded repository at once, as a pull request each team is being
# asked to trust. This runs first for that reason.
- name: Verify rendered artifacts in this repo are current
run: node scripts/render-self.mjs --check
- name: Build CLI
run: npm run build
- name: Open sync pull requests
env:
# Fine-grained PAT or GitHub App installation token. Required permissions on
# every target repo: contents:write, pull_requests:write, workflows:write.
# Without `workflows` the push of .github/workflows/redline.yml is rejected.
GH_TOKEN: ${{ secrets.REDLINE_SYNC_TOKEN }}
DRY: ${{ inputs.dry-run && '--dry-run' || '' }}
ONLY: ${{ inputs.only }}
run: |
set -euo pipefail
args=()
[[ -n "$DRY" ]] && args+=("$DRY")
[[ -n "$ONLY" ]] && args+=(--repo "$ONLY")
node dist/bin/redline.js sync ${args[@]+"${args[@]}"}
- name: Summarise
if: always()
run: echo "Sync finished with status ${{ job.status }}" >> "$GITHUB_STEP_SUMMARY"