Skip to content
CommonLayer
Documentation menu
On this page

ADR-0049: Every project is its own repository

Status: Accepted (WP-83). Specified in plan 14; implemented in projects/*/, templates/project-template/, scripts/export-project.sh, scripts/check-project-trees.py, scripts/check-template-drift.sh, mk/projects.mk and .github/workflows/projects.yml.

Context

ADR-0046 and plan 13 say that a project lives in a repository of its own, connected through the GitHub App. The first-party projects did not: the base image, nginx, Valkey and PostgreSQL were directories of the platform repository with no .github/ and no schema copy. Their scripts found the manifest schema by walking up into the platform repository, the platform's workflows built, released (projects-release.yml, tags <project>/v<version>-r<revision>) and rescanned (projects-rescan.yml) them, their signing identity was the platform repository's, and the rule engine carried an exemption (hosted_ci) so that their missing CI did not fail the scan. The platform held them to a weaker standard than any maintainer's project, and none of them could be handed to a maintainer as it was.

Decision

  • A project is a complete repository. Nothing in it refers to a file outside it, and nothing outside it is needed to validate, build, test, scan, release or onboard it. The platform repository builds the platform; it does not build, release or rescan projects.
  • The four first-party projects are projects like any other: repositories commonlayer-org/base, commonlayer-org/nginx, commonlayer-org/valkey, commonlayer-org/postgresql. Until they exist their trees are kept under projects/<name>/, each exactly the content its repository will have: the template's .github/ (workflows, CODEOWNERS, Dependabot, pull request template), a copy of the schema, the shared scripts, tag pattern v{version}-r{revision}, registry source ghcr.io/commonlayer-org/<project> and signing identity https://github.com/commonlayer-org/<project>/.github/workflows/*@refs/tags/v*.
  • Every tree carries the template's workflows unchanged. Differences between projects are expressed in commonlayer.yaml (several charts, several version lines, no chart) and in hooks of the shared scripts under tests/; a workflow file may differ only when that is impossible, and is then listed in the project's docs/MAINTENANCE.md and in the drift check's exceptions. Today none differs.
  • The base image is another project's release, used by digest. Before a base release exists a build takes BASE_IMAGE, or builds the base project's repository with the project's own scripts/base-image.sh --build <checkout>; in CI the repository variable CL_BASE_REPOSITORY names it. Releases always need the digest.
  • The schema is the project's copy (or CL_SCHEMA); the search through parent directories is removed from the shared scripts/lib.sh.
  • While the trees are here, the platform proves that each one stands alone: the projects workflow exports each changed tree to a directory outside the checkout as a git repository (scripts/export-project.sh) and runs the project's own targets there; scripts/check-project-trees.py fails on any path that leaves a tree or names the platform repository's layout; the drift check covers .github/ and the schema copy.

Alternatives rejected

  • Keep the projects in the platform repository and give them a .github/ that is never used. The trees would look standalone without being tested as such, and the platform's own workflows would still be what releases them.
  • One repository for all first-party projects. It would need project-prefixed tags and a signing identity shared by unrelated projects, exactly what the rule engine rejects for a maintainer's repository; and a maintainer could not be given one project without the others.
  • Forked workflow files per project (PostgreSQL's several charts and majors, the base without a chart). Every difference would have to be kept in step with the template by hand and would be a scan finding; the manifest and the script hooks already express them.
  • A git submodule or subtree per project. It keeps the projects inside the platform repository's history and tooling, which is what this decision removes.

Consequences

  • Creating the four repositories and pushing the exported trees is the owner's step (plan 14 §4). Until then nothing is released, and the trees, the projects workflow, mk/projects.mk and the platform's local base fallback stay in this repository; they are removed afterwards.
  • Each project repository needs, once: the variable CL_BASE_REPOSITORY until the base digest is set, the CODEOWNERS team, code scanning, the GitHub App.
  • A release tag now names a version without a project prefix (v1.30.6-r1), and the signatures of a project name its own repository. Nothing had been released under the old identity, so no published signature changes meaning.
  • Every project's validate.yml builds each supported version line in its own job and rebuilds weekly; the platform's weekly base rebuild is gone. These workflows have only been linted (actionlint) and their targets run locally in exported trees; they have not run on a runner.
  • The rule engine checks every tree with every rule, CI rules included; the hosted_ci exemption is removed (backend part of WP-83).