# .github/workflows/publish-images.yml — slice F (spec §6.3 + §11). # # Tag-push workflow: builds + pushes all four first-party images to the # git.adlee.work/alee/rutster-* registry namespace. linux/amd64 only (spec # §1.2 / ADR-0011 — arm64 is a named deferral). # # Gating: runs AFTER and gated on the existing fmt/clippy/test/deny/sim-bench # + image-build pipeline. The publish workflow re-runs the fmt/clippy/test/ # deny matrix as a `needs:` chain (cheap when nothing changed) + builds all # four images from the same Dockerfile CI's image-build job uses. This makes # the published image byte-identical to the one CI tested — the "registry tag # points at a tested image" invariant operators rely on. # # Registry namespace: git.adlee.work/alee/rutster-* (matches the `alee` tea # login per AGENTS.md Git workflow + the root Cargo.toml's `repository` # field). If your Gitea registry uses a different owner segment, replace # `alee` throughout this file (REGISTRY_NAMESPACE below). name: Publish Images on: push: tags: - 'v*' env: REGISTRY: git.adlee.work REGISTRY_NAMESPACE: alee PLATFORMS: linux/amd64 jobs: # The publish workflow is its own pipeline; it re-runs fmt/clippy/test/deny # + builds the images + then publishes. The fmt/clippy/test/deny re-runs # are cheap on a green repo (cache hits, fast fmt + clippy + test), and # they make a critical guarantee: the version published under a `v*` tag # has ALL the repo's quality gates green at that exact commit. Without # this `needs:` chain, a tag could be pushed at a commit where the matrix # failed but image-build happened to succeed on a flake. fmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: components: rustfmt - run: cargo fmt --check clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: components: clippy - name: Install libopus (media crate FFI dep) run: apt-get update && apt-get install -y libopus-dev - uses: Swatinem/rust-cache@v2 - run: cargo clippy --all --all-targets -- -D warnings test: runs-on: ubuntu-latest strategy: matrix: toolchain: [stable, "1.85"] steps: - uses: actions/checkout@v4 # Seam gate (mirror of .github/workflows/ci.yml's test job — # loop_driver.rs + rtc_session.rs stay byte-identical to slice-3/5). - name: Seam gate — loop_driver frozen + rtc_session pinned run: | EXPECTED_LOOP_DRIVER='744bf314edf7f4925c8bb3bd0f5176dbc88f8113' EXPECTED_RTC_SESSION='f47d63b9a2883d37066a93c9daa0e2cf8816bec4' GOT_LOOP_DRIVER=$(git hash-object crates/rutster-media/src/loop_driver.rs) GOT_RTC_SESSION=$(git hash-object crates/rutster-media/src/rtc_session.rs) [ "$GOT_LOOP_DRIVER" = "$EXPECTED_LOOP_DRIVER" ] || exit 1 [ "$GOT_RTC_SESSION" = "$EXPECTED_RTC_SESSION" ] || exit 1 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.toolchain }} - name: Install libopus (media crate FFI dep) run: apt-get update && apt-get install -y libopus-dev - uses: Swatinem/rust-cache@v2 - run: cargo test --all deny: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: EmbarkStudios/cargo-deny-action@v2 with: command: check sim-bench: name: sim-bench (stable) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: components: clippy - name: Install libopus (media crate FFI dep) run: apt-get update && apt-get install -y libopus-dev - uses: Swatinem/rust-cache@v2 - name: cargo fmt + clippy on sim-bench feature paths run: | cargo fmt --all --check cargo clippy --all --all-targets --features=sim-bench -- -D warnings - name: Run sim-bench threshold sweep run: cargo test --all --features=sim-bench -- --test-threads=1 publish: name: Publish 4 images to git.adlee.work/alee/rutster-* runs-on: ubuntu-latest needs: [fmt, clippy, test, deny, sim-bench] permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build-time host deps (belt-and-braces) run: sudo apt-get update && sudo apt-get install -y libopus-dev - name: Log in to Gitea registry # GITEA_REGISTRY_USERNAME + GITEA_REGISTRY_PASSWORD are repo secrets # set by the maintainer (per AGENTS.md Git workflow — the `alee` # tea login). The username is `alee` (matches REGISTRY_NAMESPACE). uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.GITEA_REGISTRY_USERNAME }} password: ${{ secrets.GITEA_REGISTRY_PASSWORD }} - name: Determine tags id: tags # Two tags per image: the version tag (github.ref_name, e.g. v0.1.0) # + `latest`. Operators can pin to either; `latest` is the # convenience tag that always points at the most recent release. run: | echo "version_tag=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT echo "namespace=${REGISTRY}/${REGISTRY_NAMESPACE}" >> $GITHUB_OUTPUT - name: Build + push rutster-engine uses: docker/build-push-action@v5 with: context: . file: deploy/Dockerfile target: rutster-engine platforms: ${{ env.PLATFORMS }} tags: | ${{ steps.tags.outputs.namespace }}/rutster-engine:${{ steps.tags.outputs.version_tag }} ${{ steps.tags.outputs.namespace }}/rutster-engine:latest push: true cache-from: type=gha,scope=rutster-engine cache-to: type=gha,mode=max,scope=rutster-engine - name: Build + push rutster-brain uses: docker/build-push-action@v5 with: context: . file: deploy/Dockerfile target: rutster-brain platforms: ${{ env.PLATFORMS }} tags: | ${{ steps.tags.outputs.namespace }}/rutster-brain:${{ steps.tags.outputs.version_tag }} ${{ steps.tags.outputs.namespace }}/rutster-brain:latest push: true cache-from: type=gha,scope=rutster-brain cache-to: type=gha,mode=max,scope=rutster-brain - name: Build + push rutster-edge uses: docker/build-push-action@v5 with: context: . file: deploy/Dockerfile target: rutster-edge platforms: ${{ env.PLATFORMS }} tags: | ${{ steps.tags.outputs.namespace }}/rutster-edge:${{ steps.tags.outputs.version_tag }} ${{ steps.tags.outputs.namespace }}/rutster-edge:latest push: true cache-from: type=gha,scope=rutster-edge cache-to: type=gha,mode=max,scope=rutster-edge - name: Build + push rutster-allinone uses: docker/build-push-action@v5 with: context: . file: deploy/Dockerfile target: rutster-allinone platforms: ${{ env.PLATFORMS }} tags: | ${{ steps.tags.outputs.namespace }}/rutster-allinone:${{ steps.tags.outputs.version_tag }} ${{ steps.tags.outputs.namespace }}/rutster-allinone:latest push: true cache-from: type=gha,scope=rutster-allinone cache-to: type=gha,mode=max,scope=rutster-allinone