- Python 52%
- Shell 25.6%
- Dockerfile 22.4%
README was still describing the pre-launch "vendored templates vs include:" design discussion. Replaces with the post-2026-05-13 reality: 4 base images, ci-lint + bao-checks + bump.sh tooling, canonical kaniko+sm-zot pattern, :v1 pinning rule, pitfalls. Cross-references the new aienv runbooks (setup-new-loco-pipeline, bump-loco-base-image, debug-pipeline-failure) and the 4 new memory entries documenting the same. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .woodpecker | ||
| containers | ||
| schema | ||
| scripts | ||
| templates | ||
| .gitignore | ||
| build.sh | ||
| Containerfile | ||
| README.md | ||
ci-shared
Shared CI tooling for every loco/* workload. Carries the per-language
base images, the bao tooling, the version bumper, and the CI-template
drift-checker.
What's in here
ci-shared/
├── Containerfile # builds ci-shared:v1.X.Y (alpine + bao+release tools)
├── containers/
│ ├── rust/Containerfile # → shared-rust-base
│ ├── python/Containerfile # → shared-python-base (3.12)
│ ├── python-3.13/Containerfile # → shared-python-base-3.13
│ └── go/Containerfile # → shared-go-base
├── scripts/
│ ├── bump.sh # Conventional-Commits version bumper
│ ├── bao-manifest-check # validate bao.yml against the v1 schema
│ ├── bao-lint # forbidden-pattern grep linter
│ ├── ci-lint # drift-check ci.yml against templates/python-ci.yml
│ └── reverse-coverage # nightly bao.yml ↔ policies drift
├── templates/
│ ├── ci.example.yml # generic single-workflow starter
│ └── python-ci.yml # canonical Python CI shape
├── schema/
│ └── v1.json # frozen bao-manifest schema
└── .woodpecker/
├── release.yml # ci-shared:v1.X.Y on `vX.Y.Z` tags
├── release-rust-base.yml # on `base-rust-vX.Y.Z` tags
├── release-python-base.yml # on `base-python-vX.Y.Z` tags
├── release-python-3.13-base.yml # on `base-python-3.13-vX.Y.Z` tags
└── release-go-base.yml # on `base-go-vX.Y.Z` tags
Images in the loco registry
| Image | Tag prefix | FROM | Pre-installed |
|---|---|---|---|
loco/shared-ci-shared |
vX.Y.Z |
alpine:3.21 | bash, curl, git, py3-yaml/jsonschema + the 5 scripts above + schema + templates |
loco/shared-rust-base |
base-rust-vX.Y.Z |
rust:1.93-slim | clang, mold, libssl/libsqlite/pkg-config, sccache 0.10.0 (sha-verified), jq |
loco/shared-python-base |
base-python-vX.Y.Z |
python:3.12-slim | pyflakes, ruff, pip-audit, pytest, build, build-essential, libffi/libssl/libsqlite-dev, jq |
loco/shared-python-base-3.13 |
base-python-3.13-vX.Y.Z |
python:3.13-slim | same as 3.12 variant |
loco/shared-go-base |
base-go-vX.Y.Z |
golang:1.25-alpine | git, gcc, musl-dev, make, helm v3.16.4, jq |
Consumers always pin :v<MAJOR> (e.g. :v1), never an exact patch.
The Forgejo registry has a "keep last 3 versions" retention; exact pins
break on the next prune. See aienv memory
feedback_forgejo_package_keep3.
Release flow
Each image is its own release pipeline gated on its tag prefix — they're independent. Bump one without affecting the others.
# bump ci-shared:v1.x.y itself (alpine image)
git tag -a v1.0.10 -m "..."
git push origin v1.0.10
# bump a base image
git tag -a base-rust-v1.0.2 -m "..."
git push origin base-rust-v1.0.2
Full procedure + caveats: ~/aienv/docs/runbooks/bump-loco-base-image.md.
How consumers wire it up
For a fresh consumer:
~/aienv/docs/runbooks/setup-new-loco-pipeline.md.
In short, every consumer's .woodpecker/ci.yml:
- pins shared-ci-shared:v1 for the
bao-checks,bump-prepare,bump-pushsteps - pins the relevant
shared-{lang}-base:v1for lint/audit/test steps - uses the canonical kaniko+sm-zot pattern in
build-image(pin v1.20.0-debug, sm-zot cache + mirror,--compressed-caching=false)
The canonical Python step shape lives at templates/python-ci.yml.
Run ci-lint .woodpecker/ci.yml (against the bundled template) to
spot drift.
Pitfalls
- Don't push 3+ base tags in rapid succession. Hit a Woodpecker
DB unique-constraint race once (2026-05-13,
base-python-v1.0.0got stuck inerrorwith empty steps). Either space the tag pushes by ~30s or, if you get stuck, nudge the file's content hash on a new commit and retag with the next patch. See the pinned commitea55fbdfor the dodge. - kaniko 1.22+ breaks sm-zot. All release pipelines pin
executor:v1.20.0-debug. Don't "modernise" without first upgrading sm-zot. - Project-specific Python libs don't go in the base image. Each
consumer pip-installs its own
requirements.txt. shared-python-base carries lint/audit/test tooling + system headers only.
Cross-references
- aienv runbook:
setup-new-loco-pipeline.md - aienv runbook:
bump-loco-base-image.md - aienv runbook:
debug-pipeline-failure.md - aienv memory:
reference_loco_shared_base_images - aienv memory:
reference_kaniko_sm_zot_pattern - aienv memory:
feedback_forgejo_package_keep3 - aienv memory:
feedback_kaniko_sm_zot_cache_pin