mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
refactor(roles): migrate version/branch git-ref keys to ref + add integration guard
Unify the source-repo half of the entity naming convention introduced by `0cc37d0fd`. Every entity that declares `repository:` MUST now also declare `ref:` (git ref — tag, branch, or commit SHA). The overloaded `version:` (when used as git ref) and the alternative `branch:` key are gone. Roles migrated: * web-app-bigbluebutton: `version: main` → `ref: main` (consumed in tasks/01_core.yml as `docker_git_repository_version`). * web-app-discourse: `version: main` → `ref: main`; `DISCOURSE_VERSION` renamed to `DISCOURSE_REF`. * web-app-bridgy-fed: `branch: main` → `ref: main`; `BRIDGY_REPO_BRANCH` renamed to `BRIDGY_REF` across vars, the Dockerfile ARG, and the compose build-args. * web-app-postmarks: identical migration to bridgy-fed. New guard `tests/integration/roles/meta/services/test_repository_requires_ref.py` walks every `meta/services.yml` and fails when an entity declares `repository` without `ref` (or vice versa), so the next role that forks the source-build pattern cannot regress to either half- declaration without `make test` catching it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
2497e95e9a
commit
7b15c61dfc
14 files changed
+87
-15
No files matched your search
@@ -19,7 +19,7 @@ email:
|
||||
shared: "{{ 'web-app-mailu' in group_names }}"
|
||||
bigbluebutton:
|
||||
repository: https://github.com/bigbluebutton/docker.git
|
||||
version: main
|
||||
ref: main
|
||||
recording:
|
||||
enabled: false
|
||||
cleanup: true
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
docker_compose_flush_handlers: false
|
||||
docker_git_repository_pull: true
|
||||
docker_git_repository_address: "{{ lookup('config', application_id, 'services.' ~ entity_name ~ '.repository') }}"
|
||||
docker_git_repository_version: "{{ lookup('config', application_id, 'services.' ~ entity_name ~ '.version') }}"
|
||||
docker_git_repository_version: "{{ lookup('config', application_id, 'services.' ~ entity_name ~ '.ref') }}"
|
||||
webserver_extra_configuration: >-
|
||||
{{ lookup(
|
||||
'ansible.builtin.template',
|
||||
|
||||
@@ -3,7 +3,7 @@ ARG PY_BASE="python:3.12-bookworm"
|
||||
FROM ${PY_BASE} AS build
|
||||
|
||||
ARG BRIDGY_REPO_URL
|
||||
ARG BRIDGY_REPO_BRANCH
|
||||
ARG BRIDGY_REF
|
||||
|
||||
# System deps: git, build tools, curl for healthchecks, and gunicorn
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
@@ -11,7 +11,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
RUN git clone --depth=1 --branch "${BRIDGY_REPO_BRANCH}" "${BRIDGY_REPO_URL}" ./
|
||||
RUN git clone --depth=1 --branch "${BRIDGY_REF}" "${BRIDGY_REPO_URL}" ./
|
||||
|
||||
# Python deps
|
||||
RUN pip install --upgrade pip && \
|
||||
|
||||
@@ -25,7 +25,7 @@ application:
|
||||
version: 3.14-bookworm
|
||||
name: web-app-bridgy-fed
|
||||
repository: https://github.com/snarfed/bridgy-fed.git
|
||||
branch: main
|
||||
ref: main
|
||||
min_storage: 5GB
|
||||
cpus: "0.5"
|
||||
mem_reservation: 256m
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
{{ lookup('template', 'roles/sys-svc-container/templates/build.yml.j2') | indent(4) }}
|
||||
args:
|
||||
BRIDGY_REPO_URL: "{{ BRIDGY_REPO_URL }}"
|
||||
BRIDGY_REPO_BRANCH: "{{ BRIDGY_REPO_BRANCH }}"
|
||||
BRIDGY_REF: "{{ BRIDGY_REF }}"
|
||||
CONTAINER_PORT: "{{ container_port | string }}"
|
||||
image: "{{ BRIDGY_IMAGE }}:{{ BRIDGY_VERSION }}"
|
||||
container_name: "{{ BRIDGY_CONTAINER }}"
|
||||
|
||||
@@ -17,7 +17,7 @@ BRIDGY_ADMIN_EMAIL: "{{ lookup('users', 'administrator').email }}"
|
||||
|
||||
# Source
|
||||
BRIDGY_REPO_URL: "{{ lookup('config', application_id, 'services.application.repository') }}"
|
||||
BRIDGY_REPO_BRANCH: "{{ lookup('config', application_id, 'services.application.branch') }}"
|
||||
BRIDGY_REF: "{{ lookup('config', application_id, 'services.application.ref') }}"
|
||||
|
||||
# Runtime env defaults for Bridgy Fed (see upstream README)
|
||||
BRIDGY_APPVIEW_HOST: "api.bsky.app"
|
||||
|
||||
@@ -51,7 +51,7 @@ discourse:
|
||||
shared: true
|
||||
enabled: true
|
||||
name: discourse
|
||||
version: main
|
||||
ref: main
|
||||
min_storage: 15GB
|
||||
repository: https://github.com/discourse/discourse_docker.git
|
||||
plugins:
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
git:
|
||||
repo: "{{ DISCOURSE_REPOSITORY_URL }}"
|
||||
dest: "{{ DISCOURSE_REPOSITORY_DIR }}"
|
||||
version: "{{ DISCOURSE_VERSION }}"
|
||||
version: "{{ DISCOURSE_REF }}"
|
||||
update: true
|
||||
depth: 1
|
||||
register: discourse_repo_pull
|
||||
|
||||
@@ -15,7 +15,7 @@ DISCOURSE_PLUGINS: "{{ lookup('config', application_id, 'services.discourse.plug
|
||||
DISCOURSE_PG_NETWORK: "{{ ('svc-db-' ~ DISCOURSE_DATABASE_TYPE) | get_entity_name }}"
|
||||
DISCOURSE_REDIS_HOST: "{{ application_id | get_entity_name }}-redis"
|
||||
DISCOURSE_REPOSITORY_URL: "{{ lookup('config', application_id, 'services.' ~ entity_name ~ '.repository') }}"
|
||||
DISCOURSE_VERSION: "{{ lookup('config', application_id, 'services.' ~ entity_name ~ '.version') }}"
|
||||
DISCOURSE_REF: "{{ lookup('config', application_id, 'services.' ~ entity_name ~ '.ref') }}"
|
||||
|
||||
## Database
|
||||
DISCOURSE_DATABASE: "{{ lookup('database', application_id ) }}"
|
||||
|
||||
@@ -4,14 +4,14 @@ ARG NODE_BASE="node:20-bookworm"
|
||||
FROM ${NODE_BASE} AS build
|
||||
|
||||
ARG POSTMARKS_REPO_URL
|
||||
ARG POSTMARKS_REPO_BRANCH
|
||||
ARG POSTMARKS_REF
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
RUN git clone --depth=1 --branch "${POSTMARKS_REPO_BRANCH}" "${POSTMARKS_REPO_URL}" ./
|
||||
RUN git clone --depth=1 --branch "${POSTMARKS_REF}" "${POSTMARKS_REPO_URL}" ./
|
||||
RUN npm install --no-fund --no-audit --omit=dev
|
||||
|
||||
# Final stage. Carry over the application directory and the npm
|
||||
|
||||
@@ -34,7 +34,7 @@ application:
|
||||
version: 26-bookworm
|
||||
name: web-app-postmarks
|
||||
repository: https://github.com/ckolderup/postmarks.git
|
||||
branch: main
|
||||
ref: main
|
||||
min_storage: 1GB
|
||||
cpus: "0.5"
|
||||
mem_reservation: 256m
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
{{ lookup('template', 'roles/sys-svc-container/templates/build.yml.j2') | indent(4) }}
|
||||
args:
|
||||
POSTMARKS_REPO_URL: "{{ POSTMARKS_REPO_URL }}"
|
||||
POSTMARKS_REPO_BRANCH: "{{ POSTMARKS_REPO_BRANCH }}"
|
||||
POSTMARKS_REF: "{{ POSTMARKS_REF }}"
|
||||
CONTAINER_PORT: "{{ container_port | string }}"
|
||||
image: "{{ POSTMARKS_IMAGE }}:{{ POSTMARKS_VERSION }}"
|
||||
container_name: "{{ POSTMARKS_CONTAINER }}"
|
||||
|
||||
@@ -15,4 +15,4 @@ POSTMARKS_VERSION: "{{ lookup('config', application_id, 'services.application.ve
|
||||
|
||||
# Source
|
||||
POSTMARKS_REPO_URL: "{{ lookup('config', application_id, 'services.application.repository') }}"
|
||||
POSTMARKS_REPO_BRANCH: "{{ lookup('config', application_id, 'services.application.branch') }}"
|
||||
POSTMARKS_REF: "{{ lookup('config', application_id, 'services.application.ref') }}"
|
||||
@@ -0,0 +1,72 @@
|
||||
"""Integration guard: an entity in ``meta/services.yml`` that declares
|
||||
``repository`` MUST also declare ``ref`` (and vice versa).
|
||||
|
||||
Rationale
|
||||
---------
|
||||
|
||||
The unified entity-naming convention pairs the two dimensions:
|
||||
|
||||
- ``image`` + ``version`` → OCI image used as ``FROM image:version``.
|
||||
- ``repository`` + ``ref`` → git source clone (``git clone --branch
|
||||
<ref> <repository>``).
|
||||
|
||||
Half-declarations are silent breakage waiting to happen: a Dockerfile
|
||||
that reads ``ARG ... REPOSITORY/REF`` will render ``git clone --branch
|
||||
"" "https://…"`` (empty branch flag) or attempt to clone an empty URL,
|
||||
both of which surface only at build time and only on the variant the
|
||||
role is built into. Catch it at lint time instead.
|
||||
|
||||
This guard is symmetric: declaring ``ref`` without ``repository``
|
||||
fails just as loudly — the standalone ref has no source URL to
|
||||
clone from.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from utils.cache.yaml import load_yaml_any
|
||||
from utils.roles.mapping import ROLE_FILE_META_SERVICES
|
||||
|
||||
from . import PROJECT_ROOT
|
||||
|
||||
ROLES_DIR = PROJECT_ROOT / "roles"
|
||||
|
||||
|
||||
class TestRepositoryRequiresRef(unittest.TestCase):
|
||||
def test_repository_and_ref_are_both_set_or_both_absent(self):
|
||||
offenders: list[str] = []
|
||||
|
||||
for role_dir in sorted(p for p in ROLES_DIR.iterdir() if p.is_dir()):
|
||||
services_path = role_dir / ROLE_FILE_META_SERVICES
|
||||
if not services_path.is_file():
|
||||
continue
|
||||
data = load_yaml_any(str(services_path), default_if_missing=None)
|
||||
if not isinstance(data, dict):
|
||||
continue
|
||||
|
||||
for entity_key, entry in data.items():
|
||||
if not isinstance(entry, dict):
|
||||
continue
|
||||
has_repo = "repository" in entry and entry["repository"] not in (
|
||||
None,
|
||||
"",
|
||||
)
|
||||
has_ref = "ref" in entry and entry["ref"] not in (None, "")
|
||||
if has_repo and not has_ref:
|
||||
offenders.append(
|
||||
f"{role_dir.name}: entity '{entity_key}' declares "
|
||||
f"`repository` but is missing `ref`"
|
||||
)
|
||||
elif has_ref and not has_repo:
|
||||
offenders.append(
|
||||
f"{role_dir.name}: entity '{entity_key}' declares "
|
||||
f"`ref` but is missing `repository`"
|
||||
)
|
||||
|
||||
if offenders:
|
||||
self.fail(
|
||||
f"{len(offenders)} entity/entities violate the "
|
||||
f"`repository`↔`ref` pairing:\n"
|
||||
+ "\n".join(f"- {o}" for o in offenders)
|
||||
)
|
||||
Reference in new issue
Block a user