From fcdb90c39b810d913b08c1b7f4ab509ec193a7b5 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Wed, 29 Jul 2026 20:48:49 +0200 Subject: [PATCH] feat(ci): builder images move to the LAN registry, content-keyed; fan-out scoped by paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The five builder images now live on home-ci-core's LAN registry (192.168.1.58:5010) under content keys — a hash of the ci/ tree (+ rust-toolchain.toml for the cross image). docker.yml builds one only when its key has no manifest yet, so a push that doesn't touch ci/ costs a curl per image instead of seven WAN pushes and a set of per-SHA tags that no plain prune could ever reclaim. Releases pin builders by copying the key manifest to a vX.Y.Z tag via the registry API — no rebuild, no bytes moved. Around that: deb/rpm/arch/android/apple/decky get path filters so docs-only pushes stop lighting up the whole fleet (branch pushes only — tag runs match tags:, as flatpak/windows-msix releases have proven for months); the report-only bench job moves to bench.yml (nightly + dispatch) and stops occupying a fleet slot per push; flatpak caches its Flathub runtimes and builder state instead of re-downloading multi-GB every run; rpm's cargo registry cache gets its own key namespace instead of sharing the Ubuntu jobs'; audit caches cargo bin+registry rather than the whole toolchain dir; docker-prune.sh loses the local act-cache cap/burst-clear (the cache is central now — deleting it under disk pressure was how runner-2 ended up cold-building everything) and gains a leaked-network prune. Co-Authored-By: Claude Fable 5 --- .gitea/workflows/android.yml | 19 ++ .gitea/workflows/apple.yml | 21 ++ .gitea/workflows/arch.yml | 18 ++ .gitea/workflows/audit.yml | 12 +- .gitea/workflows/bench.yml | 34 +++ .gitea/workflows/ci.yml | 35 +-- .gitea/workflows/deb.yml | 24 +- .gitea/workflows/decky.yml | 7 + .gitea/workflows/docker.yml | 224 ++++++++++++------ .gitea/workflows/flatpak.yml | 19 ++ .gitea/workflows/linux-client-screenshots.yml | 2 +- .gitea/workflows/rpm.yml | 21 +- .gitea/workflows/sbom.yml | 2 +- ci/rust-ci-arm64cross.Dockerfile | 2 +- scripts/ci/docker-prune.sh | 30 +-- 15 files changed, 345 insertions(+), 125 deletions(-) create mode 100644 .gitea/workflows/bench.yml diff --git a/.gitea/workflows/android.yml b/.gitea/workflows/android.yml index 90812150..325863b8 100644 --- a/.gitea/workflows/android.yml +++ b/.gitea/workflows/android.yml @@ -20,11 +20,30 @@ concurrency: on: push: branches: [main] + # Scope canary builds to what this artifact is built FROM — a docs-only or + # web-only push should not light up the whole fleet. Applies to branch pushes; + # tag runs are matched by `tags:` (proven by flatpak/windows-msix releases). + paths: + - 'crates/**' + - 'clients/android/**' + - 'Cargo.toml' + - 'Cargo.lock' + - 'rust-toolchain.toml' + - 'scripts/ci/**' + - '.gitea/workflows/android.yml' # Single project version: a `vX.Y.Z` tag is THE release (uploads to Play's `alpha` closed # track for manual promotion + attaches the .aab/.apk to the unified Gitea Release). A main # push is canary (Play `internal`). tags: ['v*'] pull_request: + paths: + - 'crates/**' + - 'clients/android/**' + - 'Cargo.toml' + - 'Cargo.lock' + - 'rust-toolchain.toml' + - 'scripts/ci/**' + - '.gitea/workflows/android.yml' workflow_dispatch: jobs: diff --git a/.gitea/workflows/apple.yml b/.gitea/workflows/apple.yml index 89a500f8..600e2e3c 100644 --- a/.gitea/workflows/apple.yml +++ b/.gitea/workflows/apple.yml @@ -20,7 +20,28 @@ concurrency: on: push: branches: [main] + # Scope canary builds to what this artifact is built FROM — a docs-only or + # web-only push should not light up the whole fleet. Applies to branch pushes; + # tag runs are matched by `tags:` (proven by flatpak/windows-msix releases). + paths: + - 'crates/**' + - 'clients/apple/**' + - 'scripts/build-xcframework.sh' + - 'Cargo.toml' + - 'Cargo.lock' + - 'rust-toolchain.toml' + - 'scripts/ci/**' + - '.gitea/workflows/apple.yml' pull_request: + paths: + - 'crates/**' + - 'clients/apple/**' + - 'scripts/build-xcframework.sh' + - 'Cargo.toml' + - 'Cargo.lock' + - 'rust-toolchain.toml' + - 'scripts/ci/**' + - '.gitea/workflows/apple.yml' workflow_dispatch: jobs: diff --git a/.gitea/workflows/arch.yml b/.gitea/workflows/arch.yml index 01cd8cd3..7d0e7675 100644 --- a/.gitea/workflows/arch.yml +++ b/.gitea/workflows/arch.yml @@ -26,6 +26,24 @@ concurrency: on: push: branches: [main] + # Scope canary builds to what this artifact is built FROM — a docs-only or + # web-only push should not light up the whole fleet. Applies to branch pushes; + # tag runs are matched by `tags:` (proven by flatpak/windows-msix releases). + paths: + - 'crates/**' + - 'clients/linux/**' + - 'clients/session/**' + - 'clients/shared/**' + - 'clients/cli/**' + - 'web/**' + - 'sdk/**' + - 'packaging/arch/**' + - 'packaging/gamescope/**' + - 'Cargo.toml' + - 'Cargo.lock' + - 'rust-toolchain.toml' + - 'scripts/ci/**' + - '.gitea/workflows/arch.yml' # Single project version: a `vX.Y.Z` tag is THE release. main publishes to the # `punktfunk-canary` pacman repo as X.Y.Z-0. (sorts below the eventual X.Y.Z-1), # tags to `punktfunk` — separate repos, so neither channel can shadow the other. diff --git a/.gitea/workflows/audit.yml b/.gitea/workflows/audit.yml index a7477233..1472a53f 100644 --- a/.gitea/workflows/audit.yml +++ b/.gitea/workflows/audit.yml @@ -49,14 +49,16 @@ jobs: cargo-audit: runs-on: ubuntu-24.04 container: - image: git.unom.io/unom/punktfunk-rust-ci:latest + image: 192.168.1.58:5010/punktfunk-rust-ci:latest timeout-minutes: 30 steps: - uses: actions/checkout@v4 # Cache /usr/local/cargo so the cargo-audit binary (and the advisory DB clone) persist. - uses: actions/cache@v4 with: - path: /usr/local/cargo + path: | + /usr/local/cargo/bin + /usr/local/cargo/registry key: cargo-audit-${{ hashFiles('Cargo.lock') }} restore-keys: cargo-audit- - name: cargo audit @@ -139,13 +141,15 @@ jobs: license-gate: runs-on: ubuntu-24.04 container: - image: git.unom.io/unom/punktfunk-rust-ci:latest + image: 192.168.1.58:5010/punktfunk-rust-ci:latest timeout-minutes: 30 steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 with: - path: /usr/local/cargo + path: | + /usr/local/cargo/bin + /usr/local/cargo/registry key: cargo-about-0.9.1 restore-keys: cargo-about- - name: cargo about license gate (host + driver workspaces) diff --git a/.gitea/workflows/bench.yml b/.gitea/workflows/bench.yml new file mode 100644 index 00000000..d2f89318 --- /dev/null +++ b/.gitea/workflows/bench.yml @@ -0,0 +1,34 @@ +# Report-only CPU benchmarks, moved out of ci.yml: they never fail the build (shared CI +# hardware is too noisy to gate on), so running them per-push only occupied a fleet slot +# during fan-out storms. Nightly + on demand is exactly as much signal at none of the +# queue cost. The tight regression gate + the real encode/stream path live on the +# self-hosted GPU runner (Tier 3, bench-gpu.yml). +name: bench +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + schedule: + - cron: '30 4 * * *' + workflow_dispatch: + +jobs: + bench: + # Tier-1 (criterion microbenchmarks) + Tier-2 (FEC loss recovery) — GPU-free, so they run here. + runs-on: ubuntu-24.04 + container: + image: 192.168.1.58:5010/punktfunk-rust-ci:latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - name: Prep + run: | + git config --global --add safe.directory "$PWD" + command -v python3 >/dev/null || { apt-get update && apt-get install -y --no-install-recommends python3; } + - name: Tier-1 microbenchmarks (criterion) + run: cargo bench -p punktfunk-core --bench pipeline -- --warm-up-time 1 --measurement-time 3 + - name: Tier-2 FEC loss recovery (loss-harness) + run: cargo run -q -p loss-harness + - name: Compare vs baseline (report-only) + run: python3 scripts/bench/compare.py --threshold 0.5 diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 564c0825..2f077686 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,7 +1,10 @@ -# CI for punktfunk (Gitea Actions). Linux jobs run on the `ubuntu-latest` runner; the Rust -# job runs inside the prebuilt builder image (ci/rust-ci.Dockerfile — system FFmpeg 8, +# CI for punktfunk (Gitea Actions). Linux jobs run on the `ubuntu-24.04` fleet label; the +# Rust job runs inside the prebuilt builder image (ci/rust-ci.Dockerfile — system FFmpeg 8, # PipeWire, GL/GBM, libcuda link stub, pinned-channel rustup) so the workspace links the -# same libs as the dev boxes. Apple client CI lives in apple.yml (macOS runner). +# same libs as the dev boxes. Builder images come from the LAN registry on home-ci-core +# (content-keyed, docker.yml) — never the WAN. Apple client CI lives in apple.yml (macOS +# runner). The report-only benchmarks moved to bench.yml (nightly + dispatch) so they stop +# occupying a fleet slot on every push. name: ci # One pending run per workflow+ref: a newer push supersedes the queued/running one and cancels # it (a canary only needs the latest commit; each release tag is its own ref so tag runs never @@ -21,7 +24,7 @@ jobs: rust: runs-on: ubuntu-24.04 container: - image: git.unom.io/unom/punktfunk-rust-ci:latest + image: 192.168.1.58:5010/punktfunk-rust-ci:latest timeout-minutes: 90 steps: - uses: actions/checkout@v4 @@ -152,7 +155,7 @@ jobs: rust-arm64: runs-on: ubuntu-24.04 container: - image: git.unom.io/unom/punktfunk-rust-ci-arm64cross:latest + image: 192.168.1.58:5010/punktfunk-rust-ci-arm64cross:latest timeout-minutes: 60 steps: - uses: actions/checkout@v4 @@ -235,25 +238,3 @@ jobs: run: bun run build - name: Typecheck run: bun run lint - - bench: - # Tier-1 (criterion microbenchmarks) + Tier-2 (FEC loss recovery) — GPU-free, so they run here. - # Report-only: prints the numbers + a diff vs the committed baseline to the job summary and never - # fails the build (shared CI hardware is too noisy to gate on). The tight regression gate + the - # real encode/stream path live on the self-hosted GPU runner (Tier 3, bench-gpu.yml). - runs-on: ubuntu-24.04 - container: - image: git.unom.io/unom/punktfunk-rust-ci:latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - - name: Prep - run: | - git config --global --add safe.directory "$PWD" - command -v python3 >/dev/null || { apt-get update && apt-get install -y --no-install-recommends python3; } - - name: Tier-1 microbenchmarks (criterion) - run: cargo bench -p punktfunk-core --bench pipeline -- --warm-up-time 1 --measurement-time 3 - - name: Tier-2 FEC loss recovery (loss-harness) - run: cargo run -q -p loss-harness - - name: Compare vs baseline (report-only) - run: python3 scripts/bench/compare.py --threshold 0.5 diff --git a/.gitea/workflows/deb.yml b/.gitea/workflows/deb.yml index 0d7e6eb7..b1d564d9 100644 --- a/.gitea/workflows/deb.yml +++ b/.gitea/workflows/deb.yml @@ -35,6 +35,24 @@ concurrency: on: push: branches: [main] + # Scope canary builds to what this artifact is built FROM — a docs-only or + # web-only push should not light up the whole fleet. Applies to branch pushes; + # tag runs are matched by `tags:` (proven by flatpak/windows-msix releases). + paths: + - 'crates/**' + - 'clients/linux/**' + - 'clients/session/**' + - 'clients/shared/**' + - 'clients/cli/**' + - 'web/**' + - 'sdk/**' + - 'packaging/debian/**' + - 'packaging/linux/**' + - 'Cargo.toml' + - 'Cargo.lock' + - 'rust-toolchain.toml' + - 'scripts/ci/**' + - '.gitea/workflows/deb.yml' # Single project version: a `vX.Y.Z` tag is THE release for every platform (see # docs-site channels.md). The old version-shadow (a client tag shipping a host package # that outranked rolling builds) is now structurally impossible — main publishes to the @@ -51,7 +69,7 @@ jobs: build-publish: runs-on: ubuntu-24.04 container: - image: git.unom.io/unom/punktfunk-rust-ci:latest + image: 192.168.1.58:5010/punktfunk-rust-ci:latest timeout-minutes: 90 steps: - uses: actions/checkout@v4 @@ -197,7 +215,7 @@ jobs: build-publish-host: runs-on: ubuntu-24.04 container: - image: git.unom.io/unom/punktfunk-rust-ci-noble:latest + image: 192.168.1.58:5010/punktfunk-rust-ci-noble:latest timeout-minutes: 90 steps: - uses: actions/checkout@v4 @@ -304,7 +322,7 @@ jobs: build-publish-client-arm64: runs-on: ubuntu-24.04 container: - image: git.unom.io/unom/punktfunk-rust-ci-arm64cross:latest + image: 192.168.1.58:5010/punktfunk-rust-ci-arm64cross:latest timeout-minutes: 90 steps: - uses: actions/checkout@v4 diff --git a/.gitea/workflows/decky.yml b/.gitea/workflows/decky.yml index fa7e0073..a5363174 100644 --- a/.gitea/workflows/decky.yml +++ b/.gitea/workflows/decky.yml @@ -32,6 +32,13 @@ concurrency: on: push: branches: [main] + # Scope canary builds to what this artifact is built FROM — a docs-only or + # web-only push should not light up the whole fleet. Applies to branch pushes; + # tag runs are matched by `tags:` (proven by flatpak/windows-msix releases). + paths: + - 'clients/decky/**' + - 'scripts/ci/**' + - '.gitea/workflows/decky.yml' tags: ['v*'] workflow_dispatch: diff --git a/.gitea/workflows/docker.yml b/.gitea/workflows/docker.yml index e58b7d98..d7f6b2c2 100644 --- a/.gitea/workflows/docker.yml +++ b/.gitea/workflows/docker.yml @@ -1,17 +1,28 @@ -# Build + push the dockerized pieces to the Gitea container registry: -# punktfunk-web — management console (web/Dockerfile, repo-root context) -# punktfunk-docs — documentation site (docs-site/Dockerfile) -# punktfunk-rust-ci — Rust CI builder image consumed by ci.yml -# punktfunk-rust-ci-arm64cross — the above + an arm64 sysroot, for the aarch64 client legs -# punktfunk-fedora-rpm — Fedora 43 builder image consumed by rpm.yml (Bazzite RPM) +# Build + push the dockerized pieces. +# +# Two very different image families now: +# +# BUILDER images (punktfunk-rust-ci{,-noble,-arm64cross}, punktfunk-fedora{,44}-rpm) +# live on the LAN registry (home-ci-core, 192.168.1.58:5010 — unom/infra +# runners/ci-core/) and are CONTENT-KEYED: the tag is a hash of what they are built +# from (the ci/ tree, + rust-toolchain.toml for the cross image), and a build only +# happens when that key has no manifest yet. A push that doesn't touch ci/ costs one +# curl per image (~seconds), pushes nothing over the WAN, and mints no per-SHA tag +# debris on the runners — the failure mode that filled the fleet's disks. `:latest` +# is re-pushed alongside every new key and is what the consuming workflows pin. +# +# APP images (punktfunk-web, punktfunk-docs) are deployables: they keep going to the +# Gitea registry (git.unom.io) with :latest + :sha-<8> (+ :vX.Y.Z on tags), because +# unom-1 deploys pull from there and releases pin them. +# # Host and clients are intentionally NOT containerized (see CLAUDE.md "What's left"). # -# REGISTRY_TOKEN: repo Actions secret, a PAT with write:package scope. +# REGISTRY_TOKEN: repo Actions secret, a PAT with write:package scope (app images only — +# the LAN registry is unauthenticated inside the LAN). # -# Bootstrap note: ci.yml's rust job pulls punktfunk-rust-ci:latest from the registry, so -# this workflow (or a manual push) must have succeeded once before that job can run; on -# the same push, ci.yml builds against the PREVIOUS image. All three were seeded manually -# on 2026-06-12. +# Bootstrap note: consuming workflows pull /punktfunk-rust-ci:latest, so the LAN +# registry must hold a seeded :latest once (done 2026-07-29 from the last Gitea-registry +# images); after that, this workflow keeps :latest current whenever ci/ changes. name: docker # One pending run per workflow+ref: a newer push supersedes the queued/running one and cancels # it (a canary only needs the latest commit; each release tag is its own ref so tag runs never @@ -31,9 +42,142 @@ on: env: REGISTRY: git.unom.io OWNER: unom + CI_REGISTRY: 192.168.1.58:5010 jobs: - build-push: + builders: + runs-on: ubuntu-24.04 + timeout-minutes: 60 + strategy: + matrix: + include: + - image: punktfunk-rust-ci + dockerfile: ci/rust-ci.Dockerfile + # Ubuntu 24.04 LTS host builder: same purpose as rust-ci but lowers the host .deb's glibc + # floor to 2.39 and bundles a from-source FFmpeg 8, so the package installs on 24.04 LTS + # (rust-ci's 26.04 build is uninstallable there). Consumed by deb.yml's build-publish-host job. + - image: punktfunk-rust-ci-noble + dockerfile: ci/rust-ci-noble.Dockerfile + - image: punktfunk-fedora-rpm + dockerfile: ci/fedora-rpm.Dockerfile + # Fedora 44 builder (Fedora KDE spin): same Dockerfile, newer base → libavcodec.so.62. + - image: punktfunk-fedora44-rpm + dockerfile: ci/fedora-rpm.Dockerfile + buildargs: --build-arg FEDORA_VERSION=44 + keysuffix: -f44 + steps: + - uses: actions/checkout@v4 + + # The key is the git TREE HASH of ci/ — every byte any of these Dockerfiles can see + # (they all use ci/ as build context). One key for the whole family on purpose: a + # change to any of them re-keys all four, and a spurious rebuild of a sibling is + # cheap, rare, and infinitely better than a stale one. + - name: Content key + run: | + git config --global --add safe.directory "$PWD" + echo "KEY=ck-$(git rev-parse HEAD:ci | cut -c1-12)${{ matrix.keysuffix }}" >> "$GITHUB_ENV" + + - name: Check whether this key already exists + id: exists + run: | + ACCEPT='Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json' + if curl -sf -o /dev/null -H "$ACCEPT" \ + "http://$CI_REGISTRY/v2/${{ matrix.image }}/manifests/$KEY"; then + echo "hit=true" >> "$GITHUB_OUTPUT" + echo "::notice::${{ matrix.image }}:$KEY already in the LAN registry — nothing to build" + else + echo "hit=false" >> "$GITHUB_OUTPUT" + fi + + - name: Build + if: steps.exists.outputs.hit == 'false' + # --pull is cheap now: base images come through the ci-core pull-through mirror. + run: | + docker build --pull ${{ matrix.buildargs }} \ + -f "${{ matrix.dockerfile }}" \ + -t "$CI_REGISTRY/${{ matrix.image }}:$KEY" \ + -t "$CI_REGISTRY/${{ matrix.image }}:latest" \ + ci + + - name: Push + if: steps.exists.outputs.hit == 'false' + run: | + docker push "$CI_REGISTRY/${{ matrix.image }}:$KEY" + docker push "$CI_REGISTRY/${{ matrix.image }}:latest" + + # A release pins reproducible builder images without any rebuild: copy the key's + # manifest to a vX.Y.Z tag via the registry API (no image bytes move). + - name: Tag for release + if: startsWith(github.ref, 'refs/tags/v') + run: | + ACCEPT='Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json' + MT=$(curl -sfI -H "$ACCEPT" "http://$CI_REGISTRY/v2/${{ matrix.image }}/manifests/$KEY" \ + | tr -d '\r' | sed -n 's/^[Cc]ontent-[Tt]ype: //p') + curl -sf -H "$ACCEPT" -o /tmp/manifest.json \ + "http://$CI_REGISTRY/v2/${{ matrix.image }}/manifests/$KEY" + curl -sf -X PUT -H "Content-Type: $MT" --data-binary @/tmp/manifest.json \ + "http://$CI_REGISTRY/v2/${{ matrix.image }}/manifests/$GITHUB_REF_NAME" + + # The aarch64 CROSS builder — a SEPARATE job because it is `FROM punktfunk-rust-ci:latest` + # (the LAN copy) and so must not race the matrix entry that publishes that base. Consumed + # by the arm64 client legs in ci.yml/deb.yml. Its key also folds in rust-toolchain.toml: + # the Dockerfile installs the aarch64 target against the toolchain the workspace pins. + builders-arm64cross: + runs-on: ubuntu-24.04 + needs: builders + timeout-minutes: 60 + env: + IMAGE: punktfunk-rust-ci-arm64cross + steps: + - uses: actions/checkout@v4 + + - name: Content key + run: | + git config --global --add safe.directory "$PWD" + echo "KEY=ck-$(printf '%s%s' "$(git rev-parse HEAD:ci)" "$(git rev-parse HEAD:rust-toolchain.toml)" | sha256sum | cut -c1-12)" >> "$GITHUB_ENV" + + - name: Check whether this key already exists + id: exists + run: | + ACCEPT='Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json' + if curl -sf -o /dev/null -H "$ACCEPT" \ + "http://$CI_REGISTRY/v2/$IMAGE/manifests/$KEY"; then + echo "hit=true" >> "$GITHUB_OUTPUT" + echo "::notice::$IMAGE:$KEY already in the LAN registry — nothing to build" + else + echo "hit=false" >> "$GITHUB_OUTPUT" + fi + + - name: Build + if: steps.exists.outputs.hit == 'false' + # Root context: it needs rust-toolchain.toml to install the target against the + # toolchain the workspace actually pins. + run: | + docker build --pull \ + -f ci/rust-ci-arm64cross.Dockerfile \ + -t "$CI_REGISTRY/$IMAGE:$KEY" \ + -t "$CI_REGISTRY/$IMAGE:latest" \ + . + + - name: Push + if: steps.exists.outputs.hit == 'false' + run: | + docker push "$CI_REGISTRY/$IMAGE:$KEY" + docker push "$CI_REGISTRY/$IMAGE:latest" + + - name: Tag for release + if: startsWith(github.ref, 'refs/tags/v') + run: | + ACCEPT='Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json' + MT=$(curl -sfI -H "$ACCEPT" "http://$CI_REGISTRY/v2/$IMAGE/manifests/$KEY" \ + | tr -d '\r' | sed -n 's/^[Cc]ontent-[Tt]ype: //p') + curl -sf -H "$ACCEPT" -o /tmp/manifest.json \ + "http://$CI_REGISTRY/v2/$IMAGE/manifests/$KEY" + curl -sf -X PUT -H "Content-Type: $MT" --data-binary @/tmp/manifest.json \ + "http://$CI_REGISTRY/v2/$IMAGE/manifests/$GITHUB_REF_NAME" + + # Deployable app images — unchanged flow, Gitea registry, per-SHA + release tags. + apps: runs-on: ubuntu-24.04 timeout-minutes: 45 strategy: @@ -45,23 +189,6 @@ jobs: - image: punktfunk-docs dockerfile: docs-site/Dockerfile context: docs-site - - image: punktfunk-rust-ci - dockerfile: ci/rust-ci.Dockerfile - context: ci - # Ubuntu 24.04 LTS host builder: same purpose as rust-ci but lowers the host .deb's glibc - # floor to 2.39 and bundles a from-source FFmpeg 8, so the package installs on 24.04 LTS - # (rust-ci's 26.04 build is uninstallable there). Consumed by deb.yml's build-publish-host job. - - image: punktfunk-rust-ci-noble - dockerfile: ci/rust-ci-noble.Dockerfile - context: ci - - image: punktfunk-fedora-rpm - dockerfile: ci/fedora-rpm.Dockerfile - context: ci - # Fedora 44 builder (Fedora KDE spin): same Dockerfile, newer base → libavcodec.so.62. - - image: punktfunk-fedora44-rpm - dockerfile: ci/fedora-rpm.Dockerfile - context: ci - buildargs: --build-arg FEDORA_VERSION=44 steps: - uses: actions/checkout@v4 @@ -76,7 +203,7 @@ jobs: # On a release tag, also tag the image vX.Y.Z so a release pins reproducible web/docs images. EXTRA="" case "$GITHUB_REF" in refs/tags/v*) EXTRA="-t $REGISTRY/$OWNER/${{ matrix.image }}:${GITHUB_REF_NAME}" ;; esac - docker build --pull ${{ matrix.buildargs }} \ + docker build --pull \ -f "${{ matrix.dockerfile }}" \ -t "$REGISTRY/$OWNER/${{ matrix.image }}:latest" \ -t "$REGISTRY/$OWNER/${{ matrix.image }}:sha-${GITHUB_SHA::8}" \ @@ -89,48 +216,13 @@ jobs: docker push "$REGISTRY/$OWNER/${{ matrix.image }}:latest" case "$GITHUB_REF" in refs/tags/v*) docker push "$REGISTRY/$OWNER/${{ matrix.image }}:${GITHUB_REF_NAME}" ;; esac - # The aarch64 CROSS builder — a SEPARATE job because it is `FROM punktfunk-rust-ci:latest` - # and so must not race the matrix entry that publishes that base. Consumed by the arm64 - # client legs in deb.yml/rpm.yml/arch.yml. Root context: it needs rust-toolchain.toml to - # install the target against the toolchain the workspace actually pins. - build-push-arm64cross: - runs-on: ubuntu-24.04 - needs: build-push - timeout-minutes: 45 - env: - IMAGE: punktfunk-rust-ci-arm64cross - steps: - - uses: actions/checkout@v4 - - - name: Login to registry - run: | - echo "${{ secrets.REGISTRY_TOKEN }}" \ - | docker login "$REGISTRY" -u enricobuehler --password-stdin - - - name: Build - run: | - EXTRA="" - case "$GITHUB_REF" in refs/tags/v*) EXTRA="-t $REGISTRY/$OWNER/$IMAGE:${GITHUB_REF_NAME}" ;; esac - docker build --pull \ - -f ci/rust-ci-arm64cross.Dockerfile \ - -t "$REGISTRY/$OWNER/$IMAGE:latest" \ - -t "$REGISTRY/$OWNER/$IMAGE:sha-${GITHUB_SHA::8}" \ - $EXTRA \ - . - - - name: Push - run: | - docker push "$REGISTRY/$OWNER/$IMAGE:sha-${GITHUB_SHA::8}" - docker push "$REGISTRY/$OWNER/$IMAGE:latest" - case "$GITHUB_REF" in refs/tags/v*) docker push "$REGISTRY/$OWNER/$IMAGE:${GITHUB_REF_NAME}" ;; esac - # Deploy the docs site to unom-1, the DMZ services VM website/cms also deploy to # (docs.punktfunk.unom.io via Caddy on home-reverse-proxy-1 -> :3220). Same secret set # as unom/website's deploy: DEPLOY_HOST/DEPLOY_USER/DEPLOY_PORT/DEPLOY_SSH_KEY (the # unom-ci-deploy key). deploy-docs: runs-on: ubuntu-24.04 - needs: build-push + needs: apps timeout-minutes: 10 steps: - uses: actions/checkout@v4 diff --git a/.gitea/workflows/flatpak.yml b/.gitea/workflows/flatpak.yml index 50257803..7ec49f5e 100644 --- a/.gitea/workflows/flatpak.yml +++ b/.gitea/workflows/flatpak.yml @@ -135,6 +135,25 @@ jobs: https://dl.flathub.org/repo/flathub.flatpakrepo git config --global --add safe.directory "$PWD" + # This job was the fleet's single heaviest network consumer: every run re-downloaded + # the GNOME runtime + SDK + llvm/rust/ffmpeg extensions (multi-GB from Flathub) and + # every crate source. Both live in well-defined directories, both are idempotently + # verified/extended by the steps below, and the central cache server restores them + # at LAN speed — so cache them. Keyed on what actually pins them: the manifest tree + # (runtimes/extensions) and manifest+Cargo.lock (crate sources + builder state). + - name: Cache Flathub runtimes + uses: actions/cache@v4 + with: + path: ~/.local/share/flatpak + key: flatpak-runtimes-${{ hashFiles('packaging/flatpak/**') }} + restore-keys: flatpak-runtimes- + - name: Cache flatpak-builder state (crate sources, ccache) + uses: actions/cache@v4 + with: + path: .flatpak-builder + key: flatpak-builder-state-${{ hashFiles('Cargo.lock', 'packaging/flatpak/**') }} + restore-keys: flatpak-builder-state- + - name: Version + channel # Tag vX.Y.Z -> X.Y.Z on the OSTree `stable` branch (a real release); a main push -> # -ciN.g on the `canary` branch (base one minor ahead of the latest stable diff --git a/.gitea/workflows/linux-client-screenshots.yml b/.gitea/workflows/linux-client-screenshots.yml index 0ed1e177..14b49a13 100644 --- a/.gitea/workflows/linux-client-screenshots.yml +++ b/.gitea/workflows/linux-client-screenshots.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-24.04 # Same image as ci.yml/deb.yml — already carries the Rust toolchain + GTK/SDL build deps. container: - image: git.unom.io/unom/punktfunk-rust-ci:latest + image: 192.168.1.58:5010/punktfunk-rust-ci:latest timeout-minutes: 90 steps: - uses: actions/checkout@v4 diff --git a/.gitea/workflows/rpm.yml b/.gitea/workflows/rpm.yml index 22575f04..02902a1b 100644 --- a/.gitea/workflows/rpm.yml +++ b/.gitea/workflows/rpm.yml @@ -21,6 +21,21 @@ concurrency: on: push: branches: [main] + # Scope canary builds to what this artifact is built FROM — a docs-only or + # web-only push should not light up the whole fleet. Applies to branch pushes; + # tag runs are matched by `tags:` (proven by flatpak/windows-msix releases). + paths: + - 'crates/**' + - 'web/**' + - 'sdk/**' + - 'packaging/rpm/**' + - 'packaging/gamescope/**' + - 'packaging/bazzite/**' + - 'Cargo.toml' + - 'Cargo.lock' + - 'rust-toolchain.toml' + - 'scripts/ci/**' + - '.gitea/workflows/rpm.yml' # Single project version: a `vX.Y.Z` tag is THE release. main publishes to the `*-canary` rpm # groups, tags to the base groups (`bazzite`/`fedora-44`) — separate repos, so the old # version-shadow (a release outranking rolling builds in one group) is structurally gone. @@ -48,7 +63,7 @@ jobs: group: fedora-44 fedver: 44 container: - image: git.unom.io/unom/${{ matrix.image }}:latest + image: 192.168.1.58:5010/${{ matrix.image }}:latest timeout-minutes: 90 env: CARGO_HOME: /usr/local/cargo @@ -83,8 +98,8 @@ jobs: - uses: actions/cache@v4 with: path: /usr/local/cargo/registry - key: cargo-home-${{ hashFiles('Cargo.lock') }} - restore-keys: cargo-home- + key: cargo-home-fedora-${{ hashFiles('Cargo.lock') }} + restore-keys: cargo-home-fedora- - name: Version + channel # vX.Y.Z tag -> X.Y.Z-1 in the base group (a real release); main push -> -0.ciN.g diff --git a/.gitea/workflows/sbom.yml b/.gitea/workflows/sbom.yml index 3e2dabac..657f1a94 100644 --- a/.gitea/workflows/sbom.yml +++ b/.gitea/workflows/sbom.yml @@ -29,7 +29,7 @@ jobs: sbom: runs-on: ubuntu-24.04 container: - image: git.unom.io/unom/punktfunk-rust-ci:latest + image: 192.168.1.58:5010/punktfunk-rust-ci:latest timeout-minutes: 20 steps: # fetch-depth 0: the dispatch path derives the canary base from the tag history diff --git a/ci/rust-ci-arm64cross.Dockerfile b/ci/rust-ci-arm64cross.Dockerfile index 283e9a4e..6bd4f109 100644 --- a/ci/rust-ci-arm64cross.Dockerfile +++ b/ci/rust-ci-arm64cross.Dockerfile @@ -16,7 +16,7 @@ # explicit `Architectures:` or apt tries to fetch arm64 from the amd64 mirror and 404s. # # Built from the REPO ROOT context (not ci/) — see the rust-toolchain.toml copy below. -FROM git.unom.io/unom/punktfunk-rust-ci:latest +FROM 192.168.1.58:5010/punktfunk-rust-ci:latest ENV DEBIAN_FRONTEND=noninteractive diff --git a/scripts/ci/docker-prune.sh b/scripts/ci/docker-prune.sh index b7feac39..b6b4f4dc 100644 --- a/scripts/ci/docker-prune.sh +++ b/scripts/ci/docker-prune.sh @@ -9,16 +9,12 @@ set -u export PATH=/usr/bin:/bin:/usr/local/bin:$PATH -# The cache-server's blob store is a HOST directory: the fleet runs a standalone cache-server -# service (compose.yml) that bind-mounts this path to /data, and every replica points at it over -# HTTP (`external_server`). It used to live inside a runner container's writable layer, which is -# why this reached in with `docker exec` — that is no longer where it is, and the container name it -# looked for (`gitea-runner-runner`) does not exist either now that the replicas are named -# `gitea-runner-fleet-runner-N-1`. Both halves silently did nothing: the filter matched zero -# containers, so the cap and the burst-clear below were dead code. A plain host path needs neither. -CACHE_DIR=${CACHE_DIR:-/home/runner/gitea-runner-fleet/cache} -CAP_MB=${CAP_MB:-20000} # clear the cache store once it exceeds ~20 GB -BURST_PCT=${BURST_PCT:-80} # full clear once the disk is this % full +# The actions cache no longer lives on this box AT ALL: home-ci-core (192.168.1.58, see +# unom/infra runners/ci-core/) serves every runner host, sized and GC'd there. The old local +# store cap + burst-clear are gone with it — they were self-defeating anyway: under disk +# pressure they deleted exactly the cache that made the next job smaller, which is how +# runner-2 ended up cold-building every Rust job with an empty 28 KB cache dir. +BURST_PCT=${BURST_PCT:-80} # burst-clear docker debris once the disk is this % full MIN_FREE_GB=${MIN_FREE_GB:-60} # ...or this little is left, whichever trips first. # 60, not 45: this has to fire BEFORE the disk is # actually tight, because the clear only reclaims idle @@ -36,14 +32,11 @@ docker builder prune -af --filter until=2h || true docker buildx prune -af --filter until=2h || true docker container prune -f --filter until=2h || true -# 2) Cap the cache-server store. Clearing the blobs is safe — act_runner repopulates it and cache -# keys are content-hashed, so this only drops stale entries. -if [ -d "$CACHE_DIR" ]; then - SZ=$(du -sm "$CACHE_DIR" 2>/dev/null | cut -f1) - if [ -n "${SZ:-}" ] && [ "$SZ" -ge "$CAP_MB" ]; then - rm -rf "${CACHE_DIR:?}"/* && echo "cache-server store cleared (was ${SZ} MB)" - fi -fi +# 2) Leaked job networks. act_runner leaks per-job GITEA-ACTIONS-TASK-* bridges when jobs are +# killed; enough of them exhausted the docker address pool once (it then swallowed the DMZ +# subnet — see unom/infra runners/ci-core/README.md) and each one is another interface for +# the host dnsmasq to bind. until=2h protects the networks of live jobs. +docker network prune -f --filter until=2h || true # 3) Burst guard: a push-storm fills the disk WITHIN one interval — three concurrent Rust builds, # each with a multi-GB target/, on top of a ~40 GB containerd image baseline. Trigger on a free @@ -65,5 +58,4 @@ if [ "$BURST" = 1 ]; then docker image prune -af || true docker builder prune -af || true docker buildx prune -af || true - [ -d "$CACHE_DIR" ] && rm -rf "${CACHE_DIR:?}"/* || true fi