name: publish-canvas-image # Builds and pushes the canvas Docker image to GHCR whenever a commit lands # on main that touches canvas code. Previously canvas changes were visible in # CI (npm run build passed) but the live container was never updated — # operators had to manually run `docker compose build canvas` each time. # # Mirror of publish-platform-image.yml, adapted for the Next.js canvas layer. # See that workflow for inline notes on macOS Keychain isolation and QEMU. on: push: branches: [main] paths: # Only rebuild when canvas source changes — saves GHA minutes on # platform-only / docs-only / MCP-only merges. - 'canvas/**' - '.github/workflows/publish-canvas-image.yml' # Manual trigger: use after a non-canvas merge that still needs a fresh # image (e.g. a Dockerfile change lives outside the canvas/ tree). workflow_dispatch: inputs: platform_url: description: 'NEXT_PUBLIC_PLATFORM_URL baked into the bundle (default: http://localhost:8080)' required: false default: '' ws_url: description: 'NEXT_PUBLIC_WS_URL baked into the bundle (default: ws://localhost:8080/ws)' required: false default: '' permissions: contents: read packages: write # required to push to ghcr.io/${{ github.repository_owner }}/* env: IMAGE_NAME: ghcr.io/molecule-ai/canvas jobs: build-and-push: name: Build & push canvas image runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Log in to GHCR uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Compute tags id: tags shell: bash run: | echo "sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" - name: Resolve build args id: build_args # Priority: workflow_dispatch input > repo secret > hardcoded default. # NEXT_PUBLIC_* env vars are baked into the JS bundle at build time by # Next.js — they cannot be changed at runtime without a full rebuild. # For local docker-compose deployments the defaults (localhost:8080) # work as-is; production deployments should set CANVAS_PLATFORM_URL # and CANVAS_WS_URL as repository secrets. # # Inputs are passed via env vars (not direct ${{ }} interpolation) to # prevent shell injection from workflow_dispatch string inputs. shell: bash env: INPUT_PLATFORM_URL: ${{ github.event.inputs.platform_url }} SECRET_PLATFORM_URL: ${{ secrets.CANVAS_PLATFORM_URL }} INPUT_WS_URL: ${{ github.event.inputs.ws_url }} SECRET_WS_URL: ${{ secrets.CANVAS_WS_URL }} run: | PLATFORM_URL="${INPUT_PLATFORM_URL:-${SECRET_PLATFORM_URL:-http://localhost:8080}}" WS_URL="${INPUT_WS_URL:-${SECRET_WS_URL:-ws://localhost:8080/ws}}" echo "platform_url=${PLATFORM_URL}" >> "$GITHUB_OUTPUT" echo "ws_url=${WS_URL}" >> "$GITHUB_OUTPUT" - name: Build & push canvas image to GHCR uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 with: context: ./canvas file: ./canvas/Dockerfile platforms: linux/amd64 push: true build-args: | NEXT_PUBLIC_PLATFORM_URL=${{ steps.build_args.outputs.platform_url }} NEXT_PUBLIC_WS_URL=${{ steps.build_args.outputs.ws_url }} 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 canvas (Next.js 15 + React Flow)