896afc5bd7
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Waiting to run
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 8s
CI / Detect changes (pull_request) Successful in 8s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 15s
E2E API Smoke Test / detect-changes (pull_request) Successful in 9s
E2E Chat / detect-changes (pull_request) Successful in 12s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 12s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 7s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 1m33s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 4s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 4s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 3s
CI / Platform (Go) (pull_request) Successful in 4m34s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 1m17s
CI / Canvas (Next.js) (pull_request) Successful in 5m43s
lint-required-workflows-docker-host-pinned / Lint docker-host pin on docker-touching workflows (pull_request) Successful in 4s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Successful in 1m21s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m20s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 10s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
gate-check-v3 / gate-check (pull_request) Failing after 4s
qa-review / approved (pull_request) Failing after 4s
security-review / approved (pull_request) Failing after 3s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request) Successful in 5s
sop-checklist / review-refire (pull_request) Has been skipped
sop-tier-check / tier-check (pull_request) Successful in 4s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Successful in 1m14s
CI / Python Lint & Test (pull_request) Successful in 6m58s
CI / all-required (pull_request) Successful in 6m56s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 7s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 2s
E2E Chat / E2E Chat (pull_request) Successful in 3s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 1s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 2s
audit-force-merge / audit (pull_request) Successful in 3s
The ECR registry triplet (account.dkr.ecr.region.amazonaws.com = 153263036946.dkr.ecr.us-east-2.amazonaws.com) is currently hardcoded in every publish/verify workflow across 4+ repos. Switching AWS accounts or regions means touching every workflow. Refactor each affected workflow's env block to source the triplet from `vars.ECR_REGISTRY` with the current prod-account literal as a bootstrap fallback. Once the org-level variable is set, the fallback becomes dead code and an account/region migration is a one-line change at the org level instead of N PRs. Pattern mirrors `vars.CP_URL || 'https://api.moleculesai.app'` already in use in molecule-core/staging-verify.yml + redeploy-tenants-on-main.yml — proven to work on Gitea 1.22.6. Constraints honored: - No cross-repo `uses:` (blocked on 1.22.6 per feedback_gitea_cross_repo_uses_blocked). - No new admin-required setup (the org-level var can be set later by CTO without touching these workflows again). - Zero functional change today (fallback literal == current hardcoded value), so the in-flight cascade (publish → ECR → redeploy-fleet) is unaffected.
177 lines
8.2 KiB
YAML
177 lines
8.2 KiB
YAML
name: publish-canvas-image
|
|
|
|
# Ported from .github/workflows/publish-canvas-image.yml on 2026-05-11 per RFC
|
|
# internal#219 §1 sweep. Differences from the GitHub version:
|
|
# - Dropped `workflow_dispatch.inputs` (Gitea 1.22.6 parser rejects them
|
|
# per feedback_gitea_workflow_dispatch_inputs_unsupported).
|
|
# - Dropped `merge_group:` (no Gitea merge queue).
|
|
# - Dropped `environment:` blocks (Gitea has no environments).
|
|
# - Workflow-level env.GITHUB_SERVER_URL pinned per
|
|
# feedback_act_runner_github_server_url.
|
|
# - `continue-on-error: true` on each job (RFC §1 contract).
|
|
# - Retargeted the image push from GHCR to ECR. GHCR was retired during
|
|
# the 2026-05-06 Gitea migration, and Gitea's GITHUB_TOKEN cannot
|
|
# authenticate to ghcr.io.
|
|
#
|
|
|
|
# Builds and pushes the canvas Docker image to ECR 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/**'
|
|
- '.gitea/workflows/publish-canvas-image.yml'
|
|
# NOTE (Gitea port): the original GitHub workflow had a
|
|
# `workflow_dispatch:` manual trigger for the
|
|
# non-canvas-merge-but-need-fresh-image scenario. Dropped in the
|
|
# Gitea port (1.22.6 parser-finicky). Manual rebuilds require
|
|
# pushing an empty commit to canvas/ or running the operator-host
|
|
# build directly.
|
|
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
|
|
env:
|
|
# SSOT-Instance-10 (#333): ECR registry triplet (account.dkr.ecr.region.amazonaws.com)
|
|
# sourced from org/repo var `ECR_REGISTRY` with the current prod-account literal as
|
|
# bootstrap fallback. When the org var is set, the fallback becomes dead code and
|
|
# switching accounts/regions is a one-line change at the org level (instead of
|
|
# touching every workflow). Pattern mirrors `vars.CP_URL || 'literal'` already in
|
|
# use below in this repo's staging-verify.yml.
|
|
IMAGE_NAME: ${{ vars.ECR_REGISTRY || '153263036946.dkr.ecr.us-east-2.amazonaws.com' }}/molecule-ai/canvas
|
|
GITHUB_SERVER_URL: https://git.moleculesai.app
|
|
|
|
jobs:
|
|
# bp-exempt: post-merge image publication side effect; CI / all-required gates source changes.
|
|
build-and-push:
|
|
name: Build & push canvas image
|
|
# Dedicated publish/release lane (internal#462 / #394 / #399). Ship
|
|
# path (on: push:main, canvas/**) — reserved capacity so a merged
|
|
# canvas fix's image build never FIFO-queues behind PR required-CI.
|
|
# The `publish` label resolves ONLY to the molecule-runner-publish-*
|
|
# sub-pool (config.publish.yaml). HARD DEPENDENCY: this MUST land
|
|
# AFTER the publish-lane runners are registered/advertising `publish`
|
|
# — the earlier #599 `docker` label attempt queued indefinitely with
|
|
# zero eligible runners precisely because the label was targeted
|
|
# before any runner advertised it (see #576). The lane is registered
|
|
# in this rollout (internal#462) so the precondition holds.
|
|
runs-on: publish
|
|
# Phase 3 (RFC #219 §1): surface broken workflows without blocking.
|
|
# mc#774: pre-existing continue-on-error mask; root-fix and remove, do not renew silently.
|
|
continue-on-error: true
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- name: Log in to ECR
|
|
env:
|
|
IMAGE_NAME: ${{ env.IMAGE_NAME }}
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
AWS_DEFAULT_REGION: us-east-2
|
|
run: |
|
|
set -euo pipefail
|
|
ECR_REGISTRY="${IMAGE_NAME%%/*}"
|
|
aws ecr get-login-password --region us-east-2 | \
|
|
docker login --username AWS --password-stdin "${ECR_REGISTRY}"
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
|
|
|
- name: Ensure ECR repository exists
|
|
env:
|
|
IMAGE_NAME: ${{ env.IMAGE_NAME }}
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
AWS_DEFAULT_REGION: us-east-2
|
|
run: |
|
|
set -euo pipefail
|
|
repo_path="${IMAGE_NAME#*/}"
|
|
if ! aws ecr describe-repositories --repository-names "${repo_path}" --region us-east-2 >/dev/null 2>&1; then
|
|
aws ecr create-repository \
|
|
--repository-name "${repo_path}" \
|
|
--image-scanning-configuration scanOnPush=true \
|
|
--region us-east-2 >/dev/null
|
|
fi
|
|
|
|
# Health check: verify Docker daemon is accessible before attempting any
|
|
# build steps. This fails loudly at step 1 when the runner's docker.sock
|
|
# is inaccessible rather than silently continuing to the build step
|
|
# where docker build fails deep in ECR auth with a cryptic error.
|
|
- name: Verify Docker daemon access
|
|
run: |
|
|
set -euo pipefail
|
|
echo "::group::Docker daemon health check"
|
|
echo "Runner: ${HOSTNAME:-unknown}"
|
|
docker_info="$(docker info 2>&1)" || {
|
|
echo "::error::Docker daemon is not accessible at /var/run/docker.sock"
|
|
echo "::error::Runner: ${HOSTNAME:-unknown}"
|
|
printf '%s\n' "${docker_info}"
|
|
echo "::error::Check: (1) daemon running, (2) runner user in docker group, (3) sock perms 660+"
|
|
exit 1
|
|
}
|
|
printf '%s\n' "${docker_info}" | sed -n '1,5p'
|
|
echo "Docker daemon OK"
|
|
echo "::endgroup::"
|
|
|
|
- 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 ECR
|
|
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 }}
|
|
# Gitea artifact-cache reachability is best-effort on the operator
|
|
# runner network. Do not let cache export fail an image that already
|
|
# built and pushed successfully.
|
|
labels: |
|
|
org.opencontainers.image.source=https://git.moleculesai.app/${{ github.repository }}
|
|
org.opencontainers.image.revision=${{ github.sha }}
|
|
org.opencontainers.image.description=Molecule AI canvas (Next.js 15 + React Flow)
|