From c90b0095d3fde90214999e5c8a1fc586f2c557e8 Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Fri, 1 May 2026 20:14:42 -0700 Subject: [PATCH] docs(readme): add publish-template-image.yml section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The README documents the four validator workflows but omits publish-template-image.yml — which is arguably the most consequential workflow in this repo (it's the GHCR publish gate that would have caught the 2026-04-27 RuntimeCapabilities outage if it had existed then, and is what the runtime-publish cascade fans out to today). Adds: - Caller-side usage example matching the validator-section format - Trigger table (push/dispatch/cascade) verified against the workflow's consumers in the template repos - Inputs table (runtime_name, runtime_version) verified against the workflow's on.workflow_call.inputs block - Pipeline order: lint → static import smoke → boot smoke → push, with the load-bearing detail that GHCR push only happens after all three gates pass - Smoke timeout calibration explainer (90s inner / 120s outer) with link to PR #33 and the wedge-coverage rationale, so future readers don't lower the timeouts and silently blind the gate - Cross-link to molecule-core/workspace/smoke_mode.py and to publish-runtime.yml for the cascade trigger Doc-only. No workflow or code changes. --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/README.md b/README.md index 83fb730..47ca41a 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,26 @@ jobs: uses: Molecule-AI/molecule-ci/.github/workflows/validate-org-template.yml@v1 ``` +### Workspace template repos publishing to GHCR + +```yaml +# .github/workflows/publish-image.yml +name: publish-image +on: + push: + branches: [main] + workflow_dispatch: +permissions: + contents: read + packages: write +jobs: + publish: + uses: Molecule-AI/molecule-ci/.github/workflows/publish-template-image.yml@v1 + secrets: inherit +``` + +Also fires from the runtime-publish cascade (`repository_dispatch`) so a fresh `molecule-ai-workspace-runtime` PyPI release auto-rebuilds every template image. + ### Any repo with auto-merge enabled PR-time guards (currently: disable auto-merge on follow-up push). Consume from a thin caller: @@ -108,6 +128,48 @@ Together they cover the full lifecycle of "auto-merge enabled → new commits ar **False-positive note:** if a CI bot pushes (dependency update, secret rotation), this also disables auto-merge. That's intentional — the operator who originally enabled auto-merge gets notified and re-engages, which is exactly the verify-after-machine-edits behavior we want. +## publish-template-image + +Builds + publishes Docker template images for workspace runtimes to GHCR (`ghcr.io/molecule-ai/workspace-template-:latest` plus a per-commit `:sha-<7>` tag). Auto-derives `` from the caller repo name (`molecule-ai-workspace-template-`). + +**Triggers** (caller-side `on:` block): + +| Event | When | Source | +|---|---|---| +| `push` to `main` | Template Dockerfile / config / adapter changes | Caller commit | +| `workflow_dispatch` | Manual rebuild | Operator | +| `repository_dispatch` (cascade) | New `molecule-ai-workspace-runtime` PyPI release | molecule-core `publish-runtime.yml` fans out to every template repo | + +**Inputs** (all optional): + +| Input | Default | Purpose | +|---|---|---| +| `runtime_name` | derived from repo name | Override only when image should diverge from `molecule-ai-workspace-template-` convention | +| `runtime_version` | empty (Dockerfile pin wins) | Forwarded as `RUNTIME_VERSION` build-arg → unique cache key per version. Cascade builds set this to the just-published wheel version so each rebuild gets a fresh `pip install`. | + +**Secrets:** `secrets: inherit` (uses caller's `GITHUB_TOKEN` for GHCR push — no custom secrets needed). + +**Outputs:** `image` (full ref pushed) and `sha` (short tag). + +**Pipeline order** (each step is a publish gate — fail = no GHCR push): + +1. **Lint** — bare imports of runtime modules (e.g. `from plugins import ...` instead of `from molecule_runtime.plugins import ...`). Module list pulled live from the latest wheel's `_runtime_modules.json` so the lint never drifts from the rewriter. Catches the 2026-04-27 5-template ImportError outage class. +2. **Static import smoke** — boots the image and `import`s every `/app/*.py`, exercising adapter-level module-load failures and runtime version skew. +3. **Boot smoke** (`MOLECULE_SMOKE_MODE=1`) — actually runs `executor.execute()` against stub deps + stub creds, catching **lazy imports** buried inside `async def execute(...)` bodies that the static smoke can't see (the a2a-sdk v0→v1 migration shipped 5 such regressions). Also consults `runtime_wedge.is_wedged()` to upgrade provisional PASS to FAIL when an adapter marked the runtime wedged. +4. **Push to GHCR** — only after all three gates pass. + +**Smoke timeout calibration** (load-bearing — do not lower without re-testing with an injected wedge): + +- `MOLECULE_SMOKE_TIMEOUT_SECS=90` — inner timeout. Outlasts claude-agent-sdk's 60s `initialize()` handshake so the adapter's wedge-catch arm runs **before** smoke gives up. Lowering this back to the original 10s blinds the gate to PR-25-class init-wedge bugs. +- `timeout 120` outer wrapper — runner-level safety net; surfaces `exit 124` (smoke_mode itself wedged) as a distinct error from `exit 1` (adapter ImportError / wedge). +- 90s/120s pair landed in [PR #33](https://github.com/Molecule-AI/molecule-ci/pull/33) (2026-05-02) for SDK-init-wedge coverage. The workflow's inline comment is the source of truth — read it before changing. + +**Cross-references:** + +- Boot-smoke contract + wedge protocol: `molecule-core/workspace/smoke_mode.py` docstring +- Cascade trigger (runtime publish → template rebuild fan-out): `molecule-core/.github/workflows/publish-runtime.yml` +- Why this gate exists at all (original outage post-mortem): the 2026-04-27 `RuntimeCapabilities` ImportError that shipped to `:latest` because the old smoke only inspected the entrypoint string + ## License Business Source License 1.1 — © Molecule AI.