Skip to content

Commit 6fe07aa

Browse files
committed
ci: publish orchestrator + agent container images to GHCR on push to main
Builds both the orchestrator image (Dockerfile.orchestrator) and the agent runtime image (Dockerfile.runtime) on every push to main, publishes them as public packages to ghcr.io/stainlu, and tags with both `latest` and `sha-<commit-sha>` for rollback. Multi-arch (linux/amd64 + linux/arm64) via QEMU + buildx, so the same images work on Hetzner CAX11 (ARM Ampere), Lightsail medium_3_0 (Intel x86), and any other cloud VPS we target. GHA cache (mode=max) speeds up subsequent builds by reusing intermediate layers across runs. Motivation: observed deploy time on Lightsail burstable disks was ~12 minutes (dominated by a 6.5-minute `npm install -g openclaw@2026.4.11` during the agent image build). Pre-built images pulled from GHCR drop that to ~2-3 minutes. Hetzner deploy time drops from ~4 min to ~1-2 min for the same reason. This is how every production managed-runtime ships images — build once in CI, pull everywhere. Scope of the workflow (deliberately minimal, ~90 lines): - Trigger: push to main that touches Dockerfiles, package.json, src, or the workflow file itself. Also workflow_dispatch for manual runs. - Matrix strategy with two entries (orchestrator, agent) so both images build in parallel on separate runners. - Uses docker/build-push-action@v6 with type=gha caching. - `provenance: false` avoids SLSA attestation noise on a project that isn't signing releases yet. After the first successful publish: - The two packages appear at github.com/stainlu?tab=packages - Each must be manually flipped to Public via the package settings page - Deploy scripts can then `docker pull` without auth Follow-up commits will update docker-compose.yml, scripts/deploy-hetzner.sh, and scripts/deploy-aws-lightsail.sh to pull these images instead of building locally on the target VM.
1 parent a6f6a34 commit 6fe07aa

1 file changed

Lines changed: 94 additions & 0 deletions

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
name: publish-images
2+
3+
# Builds the orchestrator and agent container images on every push to main
4+
# and publishes them to GHCR (public). Deploy scripts pull these instead of
5+
# building from source on the target VM, which cuts end-to-end Lightsail
6+
# deploy time from ~12 minutes to ~2-3 minutes.
7+
#
8+
# Packages land at:
9+
# ghcr.io/stainlu/openclaw-managed-runtime-orchestrator:{latest,sha-<sha>}
10+
# ghcr.io/stainlu/openclaw-managed-runtime-agent:{latest,sha-<sha>}
11+
#
12+
# After the first successful publish, make both packages public in the GitHub
13+
# UI (user profile -> Packages -> each package -> Package settings ->
14+
# Change visibility -> Public). Required so deploy scripts can pull without
15+
# auth.
16+
17+
on:
18+
push:
19+
branches: [main]
20+
paths:
21+
- "Dockerfile.orchestrator"
22+
- "Dockerfile.runtime"
23+
- "package.json"
24+
- "pnpm-lock.yaml"
25+
- "tsconfig.json"
26+
- "src/**"
27+
- "docker/**"
28+
- ".github/workflows/publish-images.yaml"
29+
workflow_dispatch:
30+
31+
permissions:
32+
contents: read
33+
packages: write
34+
35+
env:
36+
REGISTRY: ghcr.io
37+
OWNER: stainlu
38+
39+
jobs:
40+
build:
41+
runs-on: ubuntu-latest
42+
strategy:
43+
fail-fast: false
44+
matrix:
45+
include:
46+
- name: orchestrator
47+
dockerfile: Dockerfile.orchestrator
48+
image: openclaw-managed-runtime-orchestrator
49+
- name: agent
50+
dockerfile: Dockerfile.runtime
51+
image: openclaw-managed-runtime-agent
52+
53+
steps:
54+
- name: Checkout
55+
uses: actions/checkout@v4
56+
57+
- name: Set up QEMU
58+
uses: docker/setup-qemu-action@v3
59+
60+
- name: Set up Docker Buildx
61+
uses: docker/setup-buildx-action@v3
62+
63+
- name: Log in to GHCR
64+
uses: docker/login-action@v3
65+
with:
66+
registry: ${{ env.REGISTRY }}
67+
username: ${{ github.actor }}
68+
password: ${{ secrets.GITHUB_TOKEN }}
69+
70+
- name: Extract metadata
71+
id: meta
72+
uses: docker/metadata-action@v5
73+
with:
74+
images: ${{ env.REGISTRY }}/${{ env.OWNER }}/${{ matrix.image }}
75+
tags: |
76+
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
77+
type=sha,prefix=sha-
78+
type=ref,event=branch
79+
80+
- name: Build and push ${{ matrix.name }}
81+
uses: docker/build-push-action@v6
82+
with:
83+
context: .
84+
file: ${{ matrix.dockerfile }}
85+
platforms: linux/amd64,linux/arm64
86+
push: true
87+
tags: ${{ steps.meta.outputs.tags }}
88+
labels: ${{ steps.meta.outputs.labels }}
89+
cache-from: type=gha,scope=${{ matrix.name }}
90+
cache-to: type=gha,scope=${{ matrix.name }},mode=max
91+
provenance: false
92+
93+
- name: Image digest
94+
run: echo "${{ matrix.name }} → ${{ steps.meta.outputs.tags }}"

0 commit comments

Comments
 (0)