feat(ci): reusable-docker-build pushes images to the forge registry, repo-linked
All checks were successful
CI / preflight (push) Successful in 5s
CI / typecheck (push) Successful in 11s
ecosystem-selftest / semantic-release-bumptest (push) Successful in 10s
ecosystem-selftest / eslint-gate (push) Successful in 3s
ecosystem-selftest / yamllint-gate (push) Successful in 3s
pulumi-preview / preview (push) Successful in 16s

push:true now authenticates as ci-bot (org secrets FORGE_REGISTRY_USER/
FORGE_REGISTRY_TOKEN) via `docker login forge.olsitec.net` before `docker push`,
so CI-built images land in Forgejo's built-in container registry. New `source-url`
input stamps the org.opencontainers.image.source OCI label so Forgejo links the
package to that repo's /packages page (without it the package only shows org-wide).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andreas Niemann 2026-07-01 13:41:56 +02:00
parent d5652ee3d8
commit 777af81197
2 changed files with 53 additions and 11 deletions

View file

@ -45,7 +45,7 @@ upgrade, drop the `runs-on` from callers and these notes become obsolete.
| Workflow | Purpose | Key inputs |
|----------|---------|------------|
| `reusable-node-build.yml` | install + build npm/bun/none | `package-manager`, `build`, `workdir` |
| `reusable-docker-build.yml` | `docker build` via the host socket | `image`, `dockerfile`, `context`, `build-args`, `push` |
| `reusable-docker-build.yml` | `docker build` + optional push to the forge registry | `image`, `dockerfile`, `context`, `build-args`, `source-url`, `push` |
| `reusable-lint.yml` | eslint + yamllint gate (error → non-zero) | `eslint-paths`, `yamllint-paths`, `package-manager` |
| `reusable-semantic-release.yml` | dry-run next-version probe (conventionalcommits) | `branch` → output `version` |
@ -61,8 +61,30 @@ publishing is deferred (no `@semantic-release/forgejo` analogue yet) — it comp
| olsicrypto | npm package (tsc) | `reusable-node-build` (npm) | self-contained ✓ |
| document-engine | bun package (tsc) | `reusable-node-build` (bun) | self-contained ✓ |
| olsitrack/api | no-artifact / versioned | `reusable-node-build` (empty build) | self-contained ✓ |
| seaspots-homepage | docker, dep `@olsitec/svelte-common` | `reusable-docker-build` | blocked on the package registry (Stage-2) |
| token-service | docker, dep `@olsitec/olsicrypto` | `reusable-docker-build` | blocked on the package registry (Stage-2) |
| seaspots-homepage | docker, dep `@olsitec/svelte-common` | `reusable-docker-build` | green ✓ — pushes to registry (repo-linked) |
| token-service | docker, dep `@olsitec/olsicrypto` | `reusable-docker-build` | green ✓ — pushes to registry (repo-linked) |
The semantic-release bump sequence and the eslint/yamllint gates are continuously
proven by `ecosystem-selftest.yml` on the foundation's own runner.
### Pushing images to the forge container registry
`reusable-docker-build` with `push: true` pushes the built image to Forgejo's
built-in container registry and — when `source-url` is passed — links the package to
its repo's `/packages` page (via the `org.opencontainers.image.source` OCI label). It
authenticates as the org-scoped **`ci-bot`** user, reading org-level Actions secrets
`FORGE_REGISTRY_USER` / `FORGE_REGISTRY_TOKEN`. Those secrets + the `ci-bot` identity
are provisioned out-of-band as a Tier-1 step ([`ci-bot/`](../../ci-bot/), peer to
`runners/`), **not** in `bootstrap` — so CI never depends on Vault being unsealed.
A calling repo:
```yaml
jobs:
image:
runs-on: docker
uses: olsitec/foundation/.forgejo/workflows/reusable-docker-build.yml@master
with:
image: "forge.olsitec.net/olsitec/<repo>:ci"
source-url: "https://forge.olsitec.net/olsitec/<repo>"
push: true
```

View file

@ -1,21 +1,28 @@
# reusable-docker-build — build a Docker image (999_testing candidates C1/C5).
# reusable-docker-build — build a Docker image and (optionally) push it to the
# forge container registry, repo-linked.
#
# A REUSABLE workflow (on: workflow_call) downstream repos call:
# jobs:
# image:
# runs-on: docker # REQUIRED on Forgejo 11 (pre-v15 reusable-workflow quirk; see README)
# uses: olsitec/foundation/.forgejo/workflows/reusable-docker-build.yml@master
# with: { image: "olsitec/seaspots-homepage:ci", push: false }
# with:
# image: "forge.olsitec.net/olsitec/seaspots-homepage:ci"
# source-url: "https://forge.olsitec.net/olsitec/seaspots-homepage"
# push: true
#
# Builds against the HOST Docker daemon via the mounted socket (the foundation-ci
# image ships the docker CLI; the runner's valid_volumes allows the mount). NOTE
# (R5): the host socket is root-equivalent on the forge VM — this is acceptable
# ONLY for trusted first-party repos until the runner is fenced to its own VM.
#
# Candidates C1 (seaspots-homepage) and C5 (token-service) depend on @olsitec
# packages from a private registry that is not published yet (Stage-2). Their real
# builds need a registry / npmrc; this workflow proves the docker-build path and
# accepts a `build-args`/`npmrc` hook for when the registry exists.
# PUSH + REPO-LINK: with push:true the image is pushed to Forgejo's built-in
# container registry (forge.olsitec.net/olsitec/<name>) authenticating as the
# org-level `ci-bot` (org Actions secrets FORGE_REGISTRY_USER/FORGE_REGISTRY_TOKEN,
# provisioned out-of-band as a Tier-1 step — see HANDOVER / provision/ci-bot).
# Passing `source-url` stamps the OCI label org.opencontainers.image.source so
# Forgejo links the package to that repo's /packages page (without it the package
# only shows on the org-level packages page).
name: reusable-docker-build
on:
workflow_call:
@ -27,15 +34,19 @@ on:
type: string
default: "Dockerfile"
image:
description: "image ref to tag, e.g. name:tag"
description: "image ref to tag/push, e.g. forge.olsitec.net/olsitec/name:tag"
type: string
required: true
build-args:
description: "newline-separated KEY=VALUE docker --build-arg pairs"
type: string
default: ""
source-url:
description: "repo URL for the org.opencontainers.image.source OCI label (registry repo-linking)"
type: string
default: ""
push:
description: "push to the foundation registry after build (registry must exist)"
description: "push to the forge container registry after build (needs image=forge.olsitec.net/...)"
type: boolean
default: false
@ -60,9 +71,18 @@ jobs:
${{ inputs.build-args }}
EOF
fi
if [ -n "${{ inputs.source-url }}" ]; then
args="$args --label org.opencontainers.image.source=${{ inputs.source-url }}"
fi
echo "+ docker build -f ${{ inputs.dockerfile }} -t ${{ inputs.image }} $args ${{ inputs.context }}"
docker build -f "${{ inputs.dockerfile }}" -t "${{ inputs.image }}" $args "${{ inputs.context }}"
- name: Registry login
if: ${{ inputs.push }}
run: |
printf '%s' "${{ secrets.FORGE_REGISTRY_TOKEN }}" \
| docker login forge.olsitec.net -u "${{ secrets.FORGE_REGISTRY_USER }}" --password-stdin
- name: Push
if: ${{ inputs.push }}
run: docker push "${{ inputs.image }}"