v0.2.4 #6
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: release-images | |
| # Build the four muteki images and push them to GitHub Container Registry (GHCR). | |
| # | |
| # Images (all under ghcr.io/<owner>/...): | |
| # muteki-web — FastAPI control plane (docker/web/Dockerfile, context = repo root) | |
| # muteki-ui — Next command deck (docker/ui/Dockerfile, context = apps/web/ui) | |
| # muteki-worker-slim — ubuntu base + 3 CLIs + agent (docker/worker-slim, needs cross-compiled runtime_agent) | |
| # muteki-worker — Kali base + full pentest kit (docker/worker, heavy: ~kali + 2.6G knowledges) | |
| # | |
| # Auth: the built-in GITHUB_TOKEN with packages:write — no PAT, no Docker Hub. | |
| # Triggers: | |
| # - release published → tag images with the release tag (e.g. v0.0.1) + latest | |
| # - workflow_dispatch → manual run for testing the pipeline inside the private repo | |
| # (optionally skip the slow Kali worker via the input) | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| build_kali_worker: | |
| description: "Also build the heavy Kali muteki-worker image" | |
| type: boolean | |
| default: true | |
| tag: | |
| description: "Image tag to push (defaults to the git ref name)" | |
| type: string | |
| default: "" | |
| # Least-privilege: read the code, write packages (GHCR). | |
| permissions: | |
| contents: read | |
| packages: write | |
| env: | |
| REGISTRY: ghcr.io | |
| GO_VERSION: "1.26" | |
| jobs: | |
| # ── lightweight images: plain docker build-push ─────────────────────────────── | |
| web: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Resolve image tag | |
| id: tag | |
| run: | | |
| TAG="${{ github.event.inputs.tag }}" | |
| [ -z "$TAG" ] && TAG="${{ github.ref_name }}" | |
| # strip a leading refs/tags/ if present and fall back to a sane default | |
| TAG="${TAG#refs/tags/}" | |
| [ -z "$TAG" ] && TAG="latest" | |
| OWNER="${GITHUB_REPOSITORY_OWNER,,}" | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "owner=$OWNER" >> "$GITHUB_OUTPUT" | |
| echo "Resolved image namespace: $OWNER" | |
| echo "Resolved tag: $TAG" | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build & push muteki-web | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: docker/web/Dockerfile | |
| push: true | |
| tags: | | |
| ${{ env.REGISTRY }}/${{ steps.tag.outputs.owner }}/muteki-web:${{ steps.tag.outputs.tag }} | |
| ${{ env.REGISTRY }}/${{ steps.tag.outputs.owner }}/muteki-web:latest | |
| cache-from: type=gha,scope=muteki-web | |
| cache-to: type=gha,scope=muteki-web,mode=max | |
| ui: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Resolve image tag | |
| id: tag | |
| run: | | |
| TAG="${{ github.event.inputs.tag }}" | |
| [ -z "$TAG" ] && TAG="${{ github.ref_name }}" | |
| TAG="${TAG#refs/tags/}" | |
| [ -z "$TAG" ] && TAG="latest" | |
| OWNER="${GITHUB_REPOSITORY_OWNER,,}" | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "owner=$OWNER" >> "$GITHUB_OUTPUT" | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build & push muteki-ui | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: apps/web/ui | |
| file: docker/ui/Dockerfile | |
| push: true | |
| # The Next standalone server freezes the /api proxy target at build time. | |
| # Bake the compose service alias so the composed stack works out of the box; | |
| # a non-compose deployment overrides NEXT/MUTEKI_BACKEND at its own build. | |
| build-args: | | |
| MUTEKI_BACKEND=http://web-api:8000 | |
| tags: | | |
| ${{ env.REGISTRY }}/${{ steps.tag.outputs.owner }}/muteki-ui:${{ steps.tag.outputs.tag }} | |
| ${{ env.REGISTRY }}/${{ steps.tag.outputs.owner }}/muteki-ui:latest | |
| cache-from: type=gha,scope=muteki-ui | |
| cache-to: type=gha,scope=muteki-ui,mode=max | |
| # ── worker images: cross-compile the Go runtime_agent, stage context, build ─── | |
| worker-slim: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Resolve image tag | |
| id: tag | |
| run: | | |
| TAG="${{ github.event.inputs.tag }}" | |
| [ -z "$TAG" ] && TAG="${{ github.ref_name }}" | |
| TAG="${TAG#refs/tags/}" | |
| [ -z "$TAG" ] && TAG="latest" | |
| OWNER="${GITHUB_REPOSITORY_OWNER,,}" | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "owner=$OWNER" >> "$GITHUB_OUTPUT" | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| # the runtime-agent module has no external deps (no go.sum), so the module | |
| # cache has nothing to restore — disable it to skip the "go.sum not found" warning | |
| cache: false | |
| - name: Stage build context (cross-compile runtime_agent + copy skill files) | |
| run: | | |
| set -euo pipefail | |
| ctx=docker/worker-slim | |
| echo ">> cross-compiling runtime_agent (linux/amd64, static)" | |
| CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ | |
| go build -C cmd/runtime-agent -trimpath -ldflags="-s -w" \ | |
| -o "$GITHUB_WORKSPACE/$ctx/runtime_agent" . | |
| file "$ctx/runtime_agent" || true | |
| echo ">> staging AGENTS.md + blackboard skill" | |
| cp docker/worker/AGENTS.md "$ctx/AGENTS.md" | |
| cp skills/muteki-blackboard/SKILL.md "$ctx/blackboard.SKILL.md" | |
| cp skills/muteki-blackboard/blackboard.py "$ctx/blackboard.py" | |
| chmod +x "$ctx/blackboard.py" | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build & push muteki-worker-slim | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: docker/worker-slim | |
| file: docker/worker-slim/Dockerfile | |
| platforms: linux/amd64 | |
| push: true | |
| build-args: | | |
| IMAGE_VERSION=${{ steps.tag.outputs.tag }} | |
| tags: | | |
| ${{ env.REGISTRY }}/${{ steps.tag.outputs.owner }}/muteki-worker-slim:${{ steps.tag.outputs.tag }} | |
| ${{ env.REGISTRY }}/${{ steps.tag.outputs.owner }}/muteki-worker-slim:latest | |
| cache-from: type=gha,scope=muteki-worker-slim | |
| cache-to: type=gha,scope=muteki-worker-slim,mode=max | |
| worker: | |
| runs-on: ubuntu-latest | |
| # The Kali image is large (kali-linux-headless + ~2.6G knowledges/pocs/templates). | |
| # Skip it on a manual run when the operator unchecks the input; always build on a | |
| # real release. | |
| if: ${{ github.event_name == 'release' || github.event.inputs.build_kali_worker == 'true' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Free up disk space | |
| # The default runner has ~14G free; the Kali build needs more. Drop the big | |
| # preinstalled toolchains we don't use so the build doesn't ENOSPC. | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \ | |
| /opt/hostedtoolcache/CodeQL /usr/local/share/boost || true | |
| sudo docker image prune -af || true | |
| df -h / | |
| - name: Resolve image tag | |
| id: tag | |
| run: | | |
| TAG="${{ github.event.inputs.tag }}" | |
| [ -z "$TAG" ] && TAG="${{ github.ref_name }}" | |
| TAG="${TAG#refs/tags/}" | |
| [ -z "$TAG" ] && TAG="latest" | |
| OWNER="${GITHUB_REPOSITORY_OWNER,,}" | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "owner=$OWNER" >> "$GITHUB_OUTPUT" | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| # the runtime-agent module has no external deps (no go.sum), so the module | |
| # cache has nothing to restore — disable it to skip the "go.sum not found" warning | |
| cache: false | |
| - name: Stage build context (cross-compile runtime_agent + copy skill files) | |
| run: | | |
| set -euo pipefail | |
| ctx=docker/worker | |
| echo ">> cross-compiling runtime_agent (linux/amd64, static)" | |
| CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ | |
| go build -C cmd/runtime-agent -trimpath -ldflags="-s -w" \ | |
| -o "$GITHUB_WORKSPACE/$ctx/runtime_agent" . | |
| file "$ctx/runtime_agent" || true | |
| echo ">> staging blackboard skill" | |
| cp skills/muteki-blackboard/SKILL.md "$ctx/blackboard.SKILL.md" | |
| cp skills/muteki-blackboard/blackboard.py "$ctx/blackboard.py" | |
| chmod +x "$ctx/blackboard.py" | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build & push muteki-worker (Kali) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: docker/worker | |
| file: docker/worker/Dockerfile | |
| platforms: linux/amd64 | |
| push: true | |
| build-args: | | |
| IMAGE_VERSION=${{ steps.tag.outputs.tag }} | |
| tags: | | |
| ${{ env.REGISTRY }}/${{ steps.tag.outputs.owner }}/muteki-worker:${{ steps.tag.outputs.tag }} | |
| ${{ env.REGISTRY }}/${{ steps.tag.outputs.owner }}/muteki-worker:latest | |
| # GHA cache has a 10G/repo cap; the Kali image alone can exceed it, so use a | |
| # registry-side cache image instead (does not count against the GHA cache). | |
| cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ steps.tag.outputs.owner }}/muteki-worker:buildcache | |
| cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ steps.tag.outputs.owner }}/muteki-worker:buildcache,mode=max |