From 393ef3ca7653bb76cd6e08ad867b26e99df0ee1f Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 22 Mar 2026 00:18:12 +0100 Subject: [PATCH] =?UTF-8?q?feat(workplan):=20KEY-WP-0002=20=E2=80=94=20bui?= =?UTF-8?q?ld=20&=20publish=20KeyCape=20image=20to=20Gitea=20OCI=20registr?= =?UTF-8?q?y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds workplan for containerising KeyCape and publishing to the self-hosted Gitea registry on CoulombCore (92.205.130.254:32166) instead of GHCR. Covers Makefile targets, Gitea Actions workflow, k3s insecure registry config, machine account/token management, and a smoke test round-trip. Co-Authored-By: Claude Sonnet 4.6 --- .../KEY-WP-0002-container-image-gitea.md | 245 ++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 workplans/KEY-WP-0002-container-image-gitea.md diff --git a/workplans/KEY-WP-0002-container-image-gitea.md b/workplans/KEY-WP-0002-container-image-gitea.md new file mode 100644 index 0000000..0565fa7 --- /dev/null +++ b/workplans/KEY-WP-0002-container-image-gitea.md @@ -0,0 +1,245 @@ +--- +id: KEY-WP-0002 +type: workplan +title: "KeyCape Container Image — Build & Publish to Gitea OCI Registry" +domain: netkingdom +repo: key-cape +status: active +owner: netkingdom +topic_slug: netkingdom +created: "2026-03-22" +updated: "2026-03-22" +capability_request_id: "" +state_hub_workstream_id: "c8843c7a-460a-47a2-b45a-b8d3940f9aa2" +--- + +# KEY-WP-0002 — KeyCape Container Image — Build & Publish to Gitea OCI Registry + +## Problem + +KeyCape has a `Dockerfile` but no automated build pipeline and no published +image. Other services (k3s deployments, local dev) that need to run KeyCape +must build locally from source. There is no versioned artefact to reference +in Helm charts or manifests. + +The capability request for this work was originally misrouted to railiance. +It belongs here: KeyCape owns its own image. + +## Goal + +Produce a versioned OCI image for KeyCape, published to the Gitea container +registry on CoulombCore (`92.205.130.254:32166`), triggered automatically on +every merge to `main` and on semver tags (`v*`). + +**Gitea OCI registry endpoint:** `92.205.130.254:32166` +**Image name:** `92.205.130.254:32166/netkingdom/key-cape` + +> **Why Gitea, not GHCR?** +> The net-kingdom cluster is self-hosted. Keeping images in Gitea (also +> self-hosted on CoulombCore) avoids any external registry dependency and +> keeps image pulls within the cluster network. GHCR is a future option +> once public distribution is needed. + +## Design + +### Image naming & tagging + +| Trigger | Tags applied | +|---------|-------------| +| push to `main` | `latest`, `main-` | +| tag `v1.2.3` | `1.2.3`, `1.2`, `1`, `latest` | + +### Build + +Multi-stage Dockerfile already present — no changes needed to the build +itself. The image builds to a distroless static binary (~10 MB). + +### Registry auth + +Gitea issues a personal access token (or machine account token) with +`write:packages` scope. Stored as Gitea Actions secret `REGISTRY_TOKEN`; +username stored as `REGISTRY_USER`. + +For local `make push`, credentials are passed via `docker login` before +the push target runs. + +### Makefile targets + +```makefile +IMAGE_REGISTRY ?= 92.205.130.254:32166 +IMAGE_REPO ?= netkingdom/key-cape +IMAGE_TAG ?= latest +IMAGE := $(IMAGE_REGISTRY)/$(IMAGE_REPO):$(IMAGE_TAG) + +image: + docker build -t $(IMAGE) . + +push: image + docker push $(IMAGE) + +image-tag: + docker tag $(IMAGE) $(IMAGE_REGISTRY)/$(IMAGE_REPO):$(IMAGE_TAG) +``` + +### Gitea Actions workflow + +`.gitea/workflows/image.yaml` — triggers on push to `main` and on `v*` tags: +- Checkout +- Set up Docker Buildx +- Login to `92.205.130.254:32166` using secrets +- Build and push with metadata-action tags +- (Optional) sign with cosign if available + +### k3s insecure registry + +Gitea runs over plain HTTP on port 32166 (NodePort). k3s must be configured +to treat this endpoint as an insecure registry so image pulls work from +within the cluster: + +```yaml +# /etc/rancher/k3s/registries.yaml (on CoulombCore) +mirrors: + "92.205.130.254:32166": + endpoint: + - "http://92.205.130.254:32166" +``` + +k3s picks this up on restart (or SIGHUP). Worker nodes (if any) need the +same file. + +--- + +## Tasks + +### T01 — Makefile: image, push, image-tag targets + +```task +id: KEY-WP-0002-T01 +status: todo +priority: high +state_hub_task_id: "749472fc-edb9-4948-9ebc-58d5f38327ee" +``` + +Add `image`, `push`, and `image-tag` targets to `Makefile` with +`IMAGE_REGISTRY`, `IMAGE_REPO`, `IMAGE_TAG` variables defaulting to the +Gitea endpoint and `netkingdom/key-cape:latest`. + +Gate: `make image` builds successfully locally; `IMAGE_TAG=dev make image` +produces a differently-tagged image. + +--- + +### T02 — Gitea Actions workflow + +```task +id: KEY-WP-0002-T02 +status: todo +priority: high +state_hub_task_id: "8ecf18cc-a3bb-4ede-a09c-fcd0d26d7f9d" +``` + +Create `.gitea/workflows/image.yaml`: +- Trigger: `push` to `main`, `push` tags matching `v*` +- Runner: `act_runner` label (or `ubuntu-latest` if configured) +- Steps: checkout → docker buildx → login → build+push +- Tags via `docker/metadata-action`: `latest` on main, semver on tags + +Secrets required (document in README.md under "CI"): +- `REGISTRY_USER` — Gitea username or machine account +- `REGISTRY_TOKEN` — Gitea personal access token with `write:packages` + +Gate: workflow file is syntactically valid; documented in README. + +--- + +### T03 — k3s insecure registry config on CoulombCore + +```task +id: KEY-WP-0002-T03 +status: todo +priority: high +state_hub_task_id: "2dde67f9-944f-418d-a2e9-7367bc556425" +``` + +On CoulombCore, create/update `/etc/rancher/k3s/registries.yaml` to add +the Gitea NodePort as an HTTP mirror. Restart k3s (or send SIGHUP) and +verify `crictl pull 92.205.130.254:32166/netkingdom/key-cape:latest` works. + +Gate: image pull from within the cluster succeeds without TLS errors. + +--- + +### T04 — Create Gitea machine account & token + +```task +id: KEY-WP-0002-T04 +status: todo +priority: medium +state_hub_task_id: "25775e10-3164-4adb-9c41-835c86fde5f8" +``` + +In Gitea (http://92.205.130.254:32166), create a machine account +`ci-netkingdom` (or reuse an existing service account) with access to +the `netkingdom` organisation. Generate a token with `write:packages` +scope and store it in: +- Gitea Actions secrets on the `key-cape` repo: `REGISTRY_USER`, `REGISTRY_TOKEN` +- The net-kingdom credential store (SOPS-encrypted) under + `credentials/gitea-ci-token.enc.yaml` + +Gate: `docker login 92.205.130.254:32166` succeeds with the token; +secret is in the credential store. + +--- + +### T05 — Smoke test: push and pull a dev image + +```task +id: KEY-WP-0002-T05 +status: todo +priority: medium +state_hub_task_id: "0f6ab38f-6d34-41af-9180-f19c687947b5" +``` + +Manually trigger a build-and-push: +```bash +docker login 92.205.130.254:32166 +IMAGE_TAG=dev make push +``` + +Then verify the image is pullable from CoulombCore: +```bash +# on CoulombCore +crictl pull 92.205.130.254:32166/netkingdom/key-cape:dev +``` + +Gate: pull succeeds; image is listed in Gitea → Packages → netkingdom/key-cape. + +--- + +### T06 — Update README with registry & CI docs + +```task +id: KEY-WP-0002-T06 +status: todo +priority: low +state_hub_task_id: "946cd34d-94da-4fa9-a781-ed36f6c827a3" +``` + +Add a "Container Image" section to `README.md` documenting: +- Registry URL and image name +- How to pull (`docker pull 92.205.130.254:32166/netkingdom/key-cape:latest`) +- How to build and push locally (Makefile targets) +- CI secrets required for the Actions workflow + +Gate: README section present and accurate. + +--- + +## Done Criteria + +- [ ] `make image` and `make push` work locally +- [ ] `.gitea/workflows/image.yaml` present and documented +- [ ] k3s can pull the image from Gitea without TLS errors +- [ ] Machine account token stored in credential store +- [ ] Smoke test: `dev` image pushed and pulled successfully +- [ ] README updated