From 035287df38833b500668c7bef251f0d0ec0c80d2 Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Tue, 14 Apr 2026 16:37:49 -0700 Subject: [PATCH] =?UTF-8?q?feat(ci):=20publish-platform-image=20workflow?= =?UTF-8?q?=20=E2=86=92=20ghcr.io/molecule-ai/platform?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase B.2 companion to the private molecule-controlplane provisioner PR. On every push to main that touches platform/**, builds platform/Dockerfile and pushes to GHCR with two tags: - :latest (floating, always main's tip) - :sha- (immutable, pin-friendly) Cache via GitHub Actions cache (cache-from: type=gha). Workflow_dispatch trigger so we can re-publish after a docs-only merge if needed. The private molecule-controlplane sets TENANT_IMAGE=ghcr.io/molecule-ai/platform: and the provisioner creates each tenant Fly Machine from this image. Staying on the same base image across tenants keeps upgrades atomic. CLAUDE.md updated to document the new workflow in the CI pipeline section. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/publish-platform-image.yml | 68 ++++++++++++++++++++ CLAUDE.md | 1 + 2 files changed, 69 insertions(+) create mode 100644 .github/workflows/publish-platform-image.yml diff --git a/.github/workflows/publish-platform-image.yml b/.github/workflows/publish-platform-image.yml new file mode 100644 index 00000000..814647a5 --- /dev/null +++ b/.github/workflows/publish-platform-image.yml @@ -0,0 +1,68 @@ +name: publish-platform-image + +# Builds and pushes the tenant-platform Docker image to GHCR whenever a +# commit lands on main. The private molecule-controlplane provisioner sets +# TENANT_IMAGE=ghcr.io/molecule-ai/platform: to spawn tenant Fly +# Machines from this image. See molecule-controlplane README for the pairing. + +on: + push: + branches: [main] + paths: + # Only rebuild when something platform-relevant changes — saves GHA + # minutes on docs-only / canvas-only / MCP-only PRs. + - 'platform/**' + - '.github/workflows/publish-platform-image.yml' + # Manual trigger for re-publishing a tag after a non-platform merge. + workflow_dispatch: + +permissions: + contents: read + packages: write # required to push to ghcr.io/${{ github.repository_owner }}/* + +env: + # GHCR accepts mixed-case, but most tooling lowercases — keep us consistent. + IMAGE_NAME: ghcr.io/molecule-ai/platform + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + # Buildx enables cache-from/cache-to via GHA cache and multi-arch + # builds without local docker daemon wrangling. + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Compute tags + id: tags + # Emit two tags per build: `latest` (floating, always the main tip) + # and the short commit SHA (immutable, pin-friendly). Control plane + # can deploy `latest` today and pin to :sha in Phase H hardening. + run: | + echo "sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" + + - name: Build & push + uses: docker/build-push-action@v5 + with: + context: ./platform + file: ./platform/Dockerfile + push: true + tags: | + ${{ env.IMAGE_NAME }}:latest + ${{ env.IMAGE_NAME }}:sha-${{ steps.tags.outputs.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max + labels: | + org.opencontainers.image.source=https://github.com/${{ github.repository }} + org.opencontainers.image.revision=${{ github.sha }} + org.opencontainers.image.description=Molecule AI tenant platform (one instance per org) diff --git a/CLAUDE.md b/CLAUDE.md index b92b221c..46ff5aa7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -256,6 +256,7 @@ GitHub Actions (`.github/workflows/ci.yml`) runs on push to main and PRs: - **python-lint**: `pytest --cov=. --cov-report=term-missing` (pytest-cov enabled) - **e2e-api** (added 2026-04-13): spins up Postgres + Redis service containers, runs platform migrations via `docker exec`, then executes `tests/e2e/test_api.sh` against a locally-built binary (62/62 must pass) - **shellcheck** (added 2026-04-13): lints every `tests/e2e/*.sh` via the shellcheck marketplace action +- **publish-platform-image** (`.github/workflows/publish-platform-image.yml`, added 2026-04-14 tick-9): on push to main touching `platform/**`, builds `platform/Dockerfile` and pushes to `ghcr.io/molecule-ai/platform:latest` + `:sha-`. Used by the private `molecule-controlplane` provisioner as tenant VM image. Manual re-trigger via `workflow_dispatch`. ### Docker Compose ```bash