Ga naar inhoud

ci-flows

Purpose: the workflow artefacts that exercise the platform, plus the guardrail that stops devs picking a label no runner serves.

Holds (flows/ + scripts/): - flows/hello.yml — smoke workflow (runs-on: con-ci); proves poll + run end-to-end. Copy into a repo's .forgejo/workflows/. - flows/README.md — outlines the Nextcloud sign+push and OCI build+push flows. - scripts/check-forgejo-runs-on.sh — the label guardrail (also exported as the forgejo-runs-on pre-commit hook via .pre-commit-hooks.yaml).

The guardrail rules: - runs-on must be con-ci or con-ci-oci (generic docker/ubuntu-latest are not served → jobs would sit queued forever). - A job with container: or services: MUST use con-ci-oci (the host backend can't run images).

Heuristic for humans (revised 2026-07-04): plain host job (scripts, lint, tooling, the sign step) → con-ci; ANYTHING involving container images — running them (container:, Hydra) AND building them — → con-ci-oci. Image builds were originally intended for con-ci (host buildah), but the stock runner image ships no buildah and host jobs are non-root without a package manager; meanwhile con-ci-oci proved podman build works rootless. Builds live there.

Use it: - Manually: scripts/check-forgejo-runs-on.sh path/to/wf.yml (needs yq). - As a pre-commit hook in a consuming repo: - repo: https://codeberg.org/Conduction/talos rev: <tag-or-sha> hooks: [{ id: forgejo-runs-on }]

Status (2026-07-04): smoke proven on con-ci; container: job proven end-to-end on con-ci-oci (test-con-ci-oci green: claim → pull via own egress-proxy → run inside image). test-con-ci.yml now tests what con-ci is for (host shell + proxy enforcement) instead of a buildah build that the class can't and shouldn't do. OCI build+push and Hydra flows validate on con-ci-oci; Nextcloud sign stays a con-ci concern (see below). A CI-side gate (running the guardrail on con-ci) needs yq in the job — add when convenient.

Image builds — the kaniko pattern (bridge until BuildKit lands)

How a repo builds + pushes a container image on this platform today. kaniko builds fully unprivileged (no daemon, no userns/seccomp relaxations — a plain container: job), which is why it works on con-ci-oci with zero extra platform surface. It is a bridge, not an end state: upstream is archived (2025-06-03, no security patches) — accepted with review date 2026-10-01 in docs/risk-acceptance-ci-on-prod.md, tied to the BuildKit enablement (docs/components/runner-oci.md, job-container seccomp section).

The four things every image-build workflow needs:

  1. Builder image — pin the self-hosted mirror by digest (never gcr.io, never a tag): yaml runs-on: con-ci-oci container: image: codeberg.org/conduction/kaniko@sha256:c3109d5926a997b100c4343944e06c6b30a6804b2f9abe0994d3de6ef92b028e That digest is the multi-arch index of gcr.io/kaniko-project/executor:v1.23.2-debug, mirrored byte-identically. Keep the -debug variant — Forgejo Actions container jobs need a shell in the image.
  2. Registry push needs a token secret. The automatic Actions token (GITHUB_TOKEN/ FORGEJO_TOKEN) has no package write on Codeberg (Forgejo v15 platform fact, not an org setting). Create an access token with scope write:package only, store it as a repo or org Actions secret (e.g. REGISTRY_TOKEN), and authenticate kaniko's push with it. Pulling the builder image needs no credentials (public org package).
  3. Proxy build-args for network access inside RUN steps. The runner injects HTTPS_PROXY etc. into the job container — kaniko's own base-image pulls and the final push honor those automatically. But RUN steps (npm/pip/apt) execute with the image-under-construction's env, so pass the proxy explicitly: --build-arg HTTP_PROXY=http://egress-proxy.con-ci-oci.svc.cluster.local:3128 \ --build-arg HTTPS_PROXY=http://egress-proxy.con-ci-oci.svc.cluster.local:3128 \ --build-arg NO_PROXY=.svc.cluster.local,.cluster.local,localhost,127.0.0.1 (Predefined proxy build-args — no ARG declarations needed in the Dockerfile.)
  4. Egress allowlist covers the build's hosts. Allowed today: codeberg.org (clone, builder pull, image push), Docker Hub (base images), registry.npmjs.org, pypi.org + files.pythonhosted.org. Anything else 403s at the proxy — additions are a one-line reviewed change to manifests/overlays/con-ci-oci/squid.conf.

Mirror maintenance (one-time done 2026-07-07; repeat only for a new version): an org member with a write:package token runs skopeo copy --all --preserve-digests docker://gcr.io/kaniko-project/executor@sha256:<digest> docker://codeberg.org/conduction/kaniko:<tag> (--all keeps the multi-arch index, --preserve-digests fails rather than rewrite bytes), then verifies with an anonymous skopeo inspect of the digest. Revoke ad-hoc tokens after use. Note: last upstream release is v1.24.0 (2025-05-23); gcr.io availability for an archived project is not guaranteed, so mirror before you need it.

Nextcloud sign+push — class decision + the crown-jewel guardrail

Design intent: runs-on: con-ci (plain/host class) — see flows/README.md. The signing key (nextcloud-signing-key, crown jewel) is mounted at runtime into the con-ci signing job, from the cluster-side SOPS Secret; it must NEVER enter the con-ci-oci build class, and apps.nextcloud.com is therefore deliberately absent from the con-ci-oci egress allowlist (it lives on con-ci only).

Unresolved tension for whoever authors the actual flow (it's still a template — task 7.2 open): - occ integrity:sign-app needs a Nextcloud/PHP environment. The stock con-ci runner image is minimal (no occ, and no buildah either — see troubleshooting.md). In practice that pushes toward a container: job → which the guardrail forces onto con-ci-oci. - If the flow ends up needing a container, do NOT "fix" it by adding apps.nextcloud.com + mounting the signing key in con-ci-oci. That drags the crown jewel into the build class and breaks the isolation the two-namespace split exists to protect.

The correct pattern if it becomes a container job — build/sign split: - con-ci-oci builds the app artifact only. No signing key, no apps.nextcloud.com. It hands the unsigned artifact off (Codeberg registry, or a shared artifact location). - A separate sign+push step in the namespace that holds the key — con-ci (host, with the nextcloud-signing-key mount + apps.nextcloud.com egress), or a dedicated con-ci-sign namespace — runs occ integrity:sign-app and pushes to apps.nextcloud.com. - The key stays exclusively in the sign namespace; the build class never sees it. Two runners, two allowlists, key co-located only with the sign step.

Until the flow is actually authored, the design stands: Nextcloud = con-ci, and the con-ci-oci allowlist stays as delivered (no apps.nextcloud.com).