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
142 changes: 142 additions & 0 deletions .github/workflows/publish-standard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Moves every repository in standards/repositories.json to a release of the
# standard. Each template is regenerated from its example; each consumer runs
# the release's own updater. Every repository gets one pull request on
# automation/repository-standard-<tag>, which merges itself once Validate
# passes. A newer release closes the older pull requests.
#
# Runs when a release tag is pushed, by hand for any stable tag, and daily for
# the latest release so an update branch that fell behind main is rebuilt.
# A branch someone pushed a fix to is left as it is.
#
# Needs the LVBT standard bot GitHub App: the LVBT_BOT_CLIENT_ID repository
# variable and the LVBT_BOT_PRIVATE_KEY secret. See
# docs/how-to/set-up-the-standard-bot.md.
name: Publish standard

on:
push:
tags: ['v[0-9]+.[0-9]+.[0-9]+']
schedule:
- cron: '17 14 * * *'
workflow_dispatch:
inputs:
tag:
description: The release tag to publish (for example v0.4.5); empty means the latest
required: false
repository:
description: One repository to update; empty means every repository
required: false

permissions:
contents: read
packages: read

concurrency:
group: publish-standard
cancel-in-progress: false

jobs:
targets:
name: Resolve release and repositories
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
tag: ${{ steps.resolve.outputs.tag }}
repositories: ${{ steps.resolve.outputs.repositories }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
fetch-tags: true
persist-credentials: false

- name: Resolve
id: resolve
shell: bash
env:
INPUT_TAG: ${{ inputs.tag }}
INPUT_REPOSITORY: ${{ inputs.repository }}
PUSHED_TAG: ${{ github.ref_type == 'tag' && github.ref_name || '' }}
run: |
set -euo pipefail
TAG="${INPUT_TAG:-$PUSHED_TAG}"
if [ -z "$TAG" ]; then
TAG=$(git tag --list 'v*' --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)
fi
if ! [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Publishing the standard requires a stable release tag, not '${TAG}'." >&2
exit 1
fi
REPOSITORIES=$(jq -c --arg only "$INPUT_REPOSITORY" \
'[.repositories[] | select(.kind != "source") | .name | select($only == "" or . == $only)]' \
standards/repositories.json)
if [ "$REPOSITORIES" = "[]" ]; then
echo "No propagation target is named '${INPUT_REPOSITORY}'." >&2
exit 1
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "repositories=${REPOSITORIES}" >> "$GITHUB_OUTPUT"

publish:
name: Update ${{ matrix.repository }}
needs: targets
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
repository: ${{ fromJSON(needs.targets.outputs.repositories) }}
env:
TAG: ${{ needs.targets.outputs.tag }}
REPOSITORY: ${{ matrix.repository }}
steps:
- name: Create the standard bot token
id: bot
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.LVBT_BOT_CLIENT_ID }}
private-key: ${{ secrets.LVBT_BOT_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: ${{ matrix.repository }}

- name: Checkout the propagation tooling
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
path: tooling

- name: Checkout the release
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ env.TAG }}
persist-credentials: false
path: source

- name: Checkout ${{ matrix.repository }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: ${{ github.repository_owner }}/${{ matrix.repository }}
token: ${{ steps.bot.outputs.token }}
fetch-depth: 0
path: target

- name: Setup pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
with:
version: 11.25.0

- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: tooling/package.json

- name: Open the update pull request
shell: bash
env:
GH_TOKEN: ${{ steps.bot.outputs.token }}
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
node tooling/standards/propagate.ts \
--source source --target target --repository "$REPOSITORY" --release "$TAG"
134 changes: 0 additions & 134 deletions .github/workflows/publish-template.yml

This file was deleted.

48 changes: 48 additions & 0 deletions .github/workflows/standard-status.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Checks every repository in standards/repositories.json against the latest
# release and the organization settings, and fails when one has drifted:
# behind the latest release for more than three days, an unreleased vendored
# commit on main, a failing update pull request, a stale plugin ref, or merge
# settings that differ from the standard. The job summary lists every
# repository. A documented, unexpired exception in repositories.json silences
# one rule for one repository.
name: Standard status

on:
schedule:
- cron: '47 14 * * *'
workflow_run:
workflows: [Publish standard]
types: [completed]
workflow_dispatch:

permissions:
contents: read

jobs:
status:
name: Check repositories
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Create the standard bot token
id: bot
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.LVBT_BOT_CLIENT_ID }}
private-key: ${{ secrets.LVBT_BOT_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}

- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: package.json

- name: Report drift
env:
GH_TOKEN: ${{ steps.bot.outputs.token }}
run: node standards/status.ts
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ Commit scopes are optional. The repository's [`.lvbt/commit-scopes.txt`](.lvbt/c
file is the complete list of durable boundaries for this repository. Do not invent a scope for a
feature, file, or task; omit it when the change crosses boundaries.

Nothing is published or tagged from this repository without the maintainer's explicit approval. The
publish workflow runs only by hand.
Nothing is published or tagged from this repository without the maintainer's explicit approval.
`Publish packages` runs only by hand. Pushing a release tag runs `Publish standard`, which opens a
self-merging update pull request in every repository in `standards/repositories.json`.
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ reference pages record facts and contracts, and explanation gives the reasoning.
- [Adopt the standard in an existing repository](how-to/adopt-in-an-existing-repository.md)
- [Set up a repository's production platform](how-to/set-up-production.md)
- [Publish a tooling release](how-to/publish-a-release.md)
- [Set up the standard bot](how-to/set-up-the-standard-bot.md)

## Reference

Expand Down
5 changes: 3 additions & 2 deletions docs/explanation/packages-and-examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,6 @@ snapshot. It does not generate or overwrite application-owned configuration.

The organization must keep the packages small and stable, because every repository feels a change to
them. A repository that needs to diverge does so in its own file, on top of the shared rule, and
says why in the commit. A release still needs a tag and release notes, followed by an explicit,
reviewed preset update in each repository.
says why in the commit. A release still needs a tag and release notes. The `Publish standard`
workflow then opens the preset update in every repository, and each one merges once that
repository's own checks pass.
7 changes: 4 additions & 3 deletions docs/how-to/create-a-repository.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ git commit -m "chore(dx): start from the LVBT repository standard"
git push
```

Then add the repository's name to `standards/repositories.json` here so the organization ruleset
applies. A deployable repository also needs the `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID`
secrets in a `production` environment before `.github/workflows/deploy.yml` can run.
Then add the repository to `standards/repositories.json` here with `"kind": "consumer"`, so the
organization ruleset applies and every release opens its update pull request. A deployable
repository also needs the `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` secrets in a
`production` environment before `.github/workflows/deploy.yml` can run.

## Common problems

Expand Down
Loading
Loading