Architecture¶
Repo Blueprint is one repository that is its own first user: the workflows that check, release and update it are the templates of the workflows of every repository made from it, and each of its releases reaches all of them as a pull request. This page shows how it is built and the path that a release takes. The automation describes every workflow, the security design what the blueprint protects and whom it trusts, the reference every question, variable and secret, and the decisions why it works the way it does.
How the blueprint works¶
---
config:
flowchart:
wrappingWidth: 400
---
flowchart TB
accTitle: How the blueprint works
accDescr: Copier renders a new repository from the questions in copier.yml and the files in template/, which take in the workflows of .github/workflows/, the manifests of stacks/ and blueprint.py. Renovate keeps the workflows and the stacks current.
renovate(["Renovate"])
subgraph here ["This repository"]
workflows[".github/workflows/<br>run here and are<br>the templates"]
stacks["stacks/<br>manifests and<br>lock files"]
tool["blueprint.py<br>settings · checklist<br>bots"]
template["template/<br>the files of<br>a new repository"]
questions["copier.yml<br>the questions"]
end
copier(["Copier"])
repo["A new repository"]
renovate -. "keeps current" .-> workflows
renovate -.-> stacks
workflows --> template
stacks --> template
tool --> template
template --> copier
questions --> copier
copier --> repo
classDef bot fill:#526cfe2e,stroke:#526cfe,stroke-width:2px
classDef done fill:#16a34a2e,stroke:#16a34a,stroke-width:2px
class renovate bot
class repo done
copier.ymlholds the questions.template/holds the files of a new repository; a file or folder name such as[% if stack == 'php' %]composer.json[% endif %]carries its condition, and a name that renders empty is left out. The delimiters{= =}and[% %]appear in no workflow, script or manifest, so${{ }}of GitHub Actions and[[ ]]of Bash and TOML stay as they are.- The workflows in
.github/workflows/run in this repository and are the templates of the workflows of every new repository: actionlint lints them and Renovate keeps their actions current here. A line marked Not in the blueprint itself switches a job off in this repository and disappears in new ones. blueprint.pystarts the tool of the settings as code, the checklist and the bots. Its code is inblueprint/, a module for each task, with aruff.tomlof its own, so that the Ruff settings of a project don't apply to it.stacks/holds the dependency manifests and lock files of each kind of project, which Renovate keeps current; a new project starts from them.tests/checksblueprint.pyagainst a simulated GitHub and renders every kind of project; the Variants workflow renders each one and runs its own checks with its real tools, and also updates a repository of each kind from the latest release to the commit, which must bring every file of the blueprint as a new repository has it and pass the same checks: in every pull request but the release pull request, and every week. It updates one with the answers of each repository that takes the blueprint as well, fromtests/answers/, without the sample code and with their licenses, languages and discussions, whose website and lint must pass too. The Downstream workflow clones each of these repositories and updates it as its blueprint bot does, tomainand to the pull request: its summary names the files whose update the pull request changes and each conflict, before the release brings them there, and a conflict in a file that is not a page, such as a workflow, a script or a configuration, fails it (downstream.py). The Windows workflow runsscripts/check.shon Windows as well, in Git Bash like a contributor there, every week and for each pull request that changes the code or the tests. The Next runner workflow runs the Variants every week on the runner that the jobs move to next.docs/makes this documentation the website of the blueprint: its pages include the READMEs, and the Dashboard workflow publishes them with the dashboard underdashboard/.reference.pywrites the reference of the website fromcopier.yml, the workflows and the settings as code of the template, so that every question, variable and secret is described where people look for it;tests/test_reference.pyfails while a page differs from them.
---
config:
flowchart:
wrappingWidth: 400
---
flowchart TB
accTitle: How a change of the template meets the repositories before its release
accDescr: A pull request changes what the repositories get from the blueprint. The Variants workflow makes a new repository of every kind, updates one of every kind from the latest release, and updates one with the answers of each repository in tests/answers, which must give every file as a new repository with these answers has it and pass the build of its website and the lint of its workflows and Markdown. The Downstream workflow clones each repository and updates it as its blueprint bot does, to main and to the pull request, and names the files whose update the pull request changes and every conflict; a conflict that the pull request brings into a workflow or a script fails it. Then the pull request merges, and the release and the Rollout bring it to every repository.
change(["A pull request changes the template"])
variants["Variants: a new repository of every kind,<br>one updated from the latest release,<br>and one with the answers of each repository<br>in tests/answers/"]
downstream["Downstream: each repository cloned<br>and updated as its blueprint bot does,<br>to main and to the pull request"]
summary["The summary: the files whose update<br>the pull request changes, and each conflict"]
conflict{"A conflict that the pull request<br>brings into a workflow or a script?"}
fix(["Fix the template, or say in the changelog<br>how a repository resolves it"])
merge["The pull request merges"]
rollout(["The release and the Rollout bring it<br>to every repository"])
change --> variants & downstream
downstream --> summary --> conflict
conflict -- "yes: the check fails" --> fix --> change
conflict -- "no" --> merge
variants -- "every leg passes" --> merge
merge --> rollout
classDef bot fill:#526cfe2e,stroke:#526cfe,stroke-width:2px
classDef person fill:#f59e0b2e,stroke:#f59e0b,stroke-width:2px
classDef result fill:#16a34a2e,stroke:#16a34a,stroke-width:2px
class variants,downstream,summary bot
class change,fix person
class rollout result
The path of a release¶
---
config:
flowchart:
wrappingWidth: 400
---
flowchart TB
accTitle: The path of a release of the blueprint
accDescr: A pull request merges into main of the blueprint, and the Release workflow keeps the release pull request with the next version. Its merge, by the maintainer or by itself when it holds only updates, publishes the release with a signed tag and verifies it. The Rollout workflow then starts the blueprint bot of every repository that takes the blueprint, five today. Each bot verifies the release, runs copier update and keeps one update pull request as the release app, so that the checks of the repository run. Without conflicts it merges itself once they pass; with conflicts, or with BLUEPRINT_AUTOMERGE set to off, it waits for the maintainer. The dashboard shows the release of the blueprint that each repository is on.
merge(["A pull request merges<br>into main of the blueprint"])
release["Release workflow<br>keeps the release pull request<br>with the next version"]
publish["Its merge publishes the release<br>signs its tag and verifies it"]
rollout["Rollout workflow<br>starts the blueprint bot<br>of every repository"]
bot["Blueprint bot, in each of the five<br>verifies the release<br>and runs copier update"]
pull["One update pull request<br>as the release app"]
checks["The checks of the repository<br>test the update"]
merged["It merges itself<br>once every check passes"]
wait(["It waits for the maintainer<br>with conflicts or with<br>BLUEPRINT_AUTOMERGE off"])
dashboard(["The dashboard shows the release<br>that each repository is on"])
merge --> release --> publish --> rollout --> bot --> pull --> checks
checks --> merged --> dashboard
checks -. "conflicts" .-> wait
wait -. "resolved and merged" .-> dashboard
classDef bot fill:#526cfe2e,stroke:#526cfe,stroke-width:2px
classDef person fill:#f59e0b2e,stroke:#f59e0b,stroke-width:2px
classDef done fill:#16a34a2e,stroke:#16a34a,stroke-width:2px
class release,publish,rollout,bot,pull,checks,merged bot
class merge,wait person
class dashboard done
- A merge into
mainof the blueprint starts the Release workflow, whose release-please keeps one release pull request with the next version, decided from the titles of the merged pull requests, and the entries of the changelog as its notes. - The release: the maintainer merges the release pull request, or it merges itself once its checks pass when it holds only updates. The Release workflow signs its tag with gitsign, publishes the release with its SBOMs and signed provenance, and checks it as its users can.
- The Rollout workflow starts the blueprint bot of every repository of the release app right after the release, and the weekly run of the bots stays as a safety net, so that a fix reaches every repository within minutes (decisions 0052 and 0010).
- The blueprint bot of each repository verifies the release before it changes anything: the release must be immutable, its tag must point at a commit of the blueprint's
main, and the release workflow of the blueprint must have signed the tag (decision 0020). Then it runscopier updateto it and keeps one pull request on the branchblueprint/update, as the release app, so that the checks of the repository run. - The checks of the repository test the update as they test every pull request. Without conflicts, the pull request merges itself once every check passes; with conflicts, or with
BLUEPRINT_AUTOMERGEset tooff, it waits for the maintainer, as Troubleshooting shows. - The dashboard shows the release of the blueprint that each repository is on, and the update pull request in the colour of its checks, at dennis-otto.github.io/repo-blueprint/dashboard.