generated from coulomb/repo-seed
feat(workplan): KEY-WP-0002 — build & publish KeyCape image to Gitea OCI registry
Some checks failed
CI / Build and Test (push) Has been cancelled
Some checks failed
CI / Build and Test (push) Has been cancelled
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 <noreply@anthropic.com>
This commit is contained in:
245
workplans/KEY-WP-0002-container-image-gitea.md
Normal file
245
workplans/KEY-WP-0002-container-image-gitea.md
Normal file
@@ -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-<short-sha>` |
|
||||
| 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
|
||||
Reference in New Issue
Block a user