//Eddie Brinton-Quinn ghostwire

2nd Line Support Engineer · 24/7 tinkerer · united kingdom

DOSSIER — PRJ-004

AI-assisted draft

This dossier was drafted with AI assistance. The long-term plan is to hand-write these as time allows; this one hasn't been rewritten yet.

MediTek — shared CI pipeline templates

Project ID

PRJ-004

Status

Perpetual · maintained

Commenced

2026 (May)

Shape

Versioned GitLab CI include templates

Contract

Consumers override by variable, never by fork

Line

Estate CI foundation · consumed by PRJ-003 and relic

Source

foundry/meditek (internal)


MediTek has no surface of its own. It exists so that other repositories stop re-solving the same pipeline problems — scanning for secrets, validating what's about to be merged, getting a container onto a host — and its value is only ever visible somewhere else, in the pipelines it shapes on the repos that consume it. The design line is stated plainly in its README: boring, explicit CI over clever pipeline magic.

What it covers

The templates are grouped by the stage of a consumer's pipeline they own, and pulled in individually through GitLab's include: mechanism. A repo takes what it needs and ignores the rest.

Secret scanning runs over both the working tree and the full commit history, on the reasoning that a credential removed in a later commit is still a credential that leaked. Validation parses every YAML, shell and Python file in the tree, alongside a structural check on Compose files that enforces house rules rather than mere well-formedness. Container security covers filesystem configuration, Dockerfile linting, and the images a Compose file actually references — the last mattering most, since a clean Dockerfile says nothing about a base image pinned a year ago.

The deploy contract

The deploy template is the load-bearing piece, and the one that justifies the repo existing at all.

It fixes the shape of a deployment — clone the playbook repository, assemble the variables, run Ansible against the target — and lets each consumer fill in only what genuinely differs between apps: which playbook, which CI variables hold the credentials, and a map of app-specific runtime inputs. Everything a consumer might reasonably need to change is a variable. Nothing requires a fork, because the moment one repo forks the template to change one line, the shared base has stopped being shared.

Deployment gating lives here too, in one place rather than restated in every consumer. Variants cover automatic and manually-gated deploys off master, and merge-request pipelines never deploy at all — they scan and validate only. The result is that a consumer's deploy configuration is a short block of variables and a single extends:, and the semantics of when a deploy is allowed to happen aren't a per-repo decision.

Secure by default

Checks are opt-out rather than opt-in, on the basis that a security check a repo has to remember to add is one that gets forgotten. The secret-scanning rules layer a custom pattern over the upstream defaults, because stock rules recognise known credential formats and say nothing about the homegrown ones — the config file with an obvious name and a long opaque string in it.

Where a repo genuinely needs an exception, it declares it as structured data the checker is guaranteed to read, not as a comment alongside the thing being excepted. Comments are not part of the parsed document, and an allowlist that depends on surviving a parser is an allowlist that eventually doesn't.

Maintenance

A shared base is only useful while every part of it still earns its place, so pruning is maintenance — a check that stopped being worth its runtime gets deleted rather than left switched off, because every consumer inherits the cost of anything kept out of politeness. Cadence tracks demand: quiet while the consumer set is stable, busier when a new family of repos arrives with a pipeline shape the templates haven't met yet. Internal only, and maintained rather than finished.


← back to selected work