Files
molecule-core/scripts/demo-freeze.sh
hongming f820780036
ci-arm64-advisory / fast-checks (pull_request) Waiting to run
CI / Canvas Deploy Reminder (pull_request) Blocked by required conditions
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Successful in 9s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 5s
CI / Detect changes (pull_request) Successful in 6s
CI / Python Lint & Test (pull_request) Successful in 3s
E2E API Smoke Test / detect-changes (pull_request) Successful in 6s
E2E Chat / detect-changes (pull_request) Successful in 10s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 12s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Successful in 48s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 4s
Harness Replays / detect-changes (pull_request) Successful in 5s
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Successful in 39s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 6s
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 5s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 1m8s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 1m10s
lint-required-workflows-docker-host-pinned / Lint docker-host pin on docker-touching workflows (pull_request) Successful in 5s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 53s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Successful in 1m23s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Successful in 1m23s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
gate-check-v3 / gate-check (pull_request) Successful in 5s
sop-checklist / review-refire (pull_request) Has been skipped
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request) Successful in 4s
sop-tier-check / tier-check (pull_request) Successful in 3s
Ops Scripts Tests / Ops scripts (unittest) (pull_request) Successful in 1m8s
E2E Staging External Runtime / E2E Staging External Runtime (pull_request) Successful in 5m11s
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Successful in 5m40s
qa-review / approved (pull_request) Refired via /qa-recheck by codex-local
security-review / approved (pull_request) Refired via /security-recheck by codex-local
CI / Shellcheck (E2E scripts) (pull_request) Successful in 33s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m19s
E2E Chat / E2E Chat (pull_request) Successful in 6s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
Harness Replays / Harness Replays (pull_request) Successful in 3s
CI / Platform (Go) (pull_request) Successful in 10m5s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2m3s
CI / Canvas (Next.js) (pull_request) Successful in 9m45s
CI / all-required (pull_request) Successful in 31m8s
audit-force-merge / audit (pull_request) Successful in 14s
chore: restrict maintained workspace runtimes
2026-05-24 19:48:00 -07:00

211 lines
7.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# demo-freeze.sh — disable the runtime + template image publish cascades
# during a demo-prep window so a stray staging merge can't auto-rebuild
# `:latest` for the 8 workspace-template images mid-demo.
#
# Demo prep typically runs T-48h to T+1h. During that window:
#
# PATH 1: any merge to molecule-core/staging that touches workspace/**
# → publish-runtime.yml fires
# → PyPI auto-bumps molecule-ai-workspace-runtime patch version
# → repository_dispatch fans out to 8 workspace-template-* repos
# → each template repo rebuilds and re-tags
# 153263036946.dkr.ecr.us-east-2.amazonaws.com/molecule-ai/workspace-template-<runtime>:latest
#
# PATH 2: any merge to a workspace-template-* repo's main branch
# → that repo's publish-image.yml fires
# → 153263036946.dkr.ecr.us-east-2.amazonaws.com/molecule-ai/workspace-template-<runtime>:latest
# gets re-tagged
#
# provisioner.go:296 RuntimeImages[runtime] reads `:latest` at every
# workspace boot. A new workspace provision during demo pulls whatever
# `:latest` resolved to seconds earlier — so a bad merge minutes
# before the demo can break a tenant the funder is about to see.
#
# This script captures the current good `:latest` digests for all 8
# templates and disables both cascade vectors. The complementary
# demo-thaw.sh re-enables them.
#
# Usage:
# scripts/demo-freeze.sh # dry run — print what would happen
# scripts/demo-freeze.sh --execute # actually disable workflows + snapshot
#
# Prereqs:
# - gh CLI authenticated with workflow:write scope on Molecule-AI org
# - curl + jq (for digest snapshot via GHCR anonymous registry API)
#
# Output:
# <snapshot dir>/digests-YYYYMMDD-HHMMSS.txt
# One line per template: "<runtime>: <digest>"
# <snapshot dir>/disabled-workflows-YYYYMMDD-HHMMSS.txt
# One line per disabled workflow: "<repo>: <workflow>"
#
# Exit codes:
# 0 — freeze complete (or dry-run successful)
# 1 — pre-flight failure (missing tooling, missing auth, etc.)
# 2 — partial freeze (some workflows did not disable cleanly; see log)
set -euo pipefail
usage() {
cat <<'USAGE'
demo-freeze.sh — disable the runtime + template image publish cascades
during a demo-prep window.
Captures current :latest digests for all 8 workspace-template-* images
and disables the workflows that would otherwise re-tag them.
Usage:
scripts/demo-freeze.sh # dry run — print what would happen
scripts/demo-freeze.sh --execute # actually disable workflows + snapshot
See the comment block at the top of this script for the full procedure.
USAGE
}
EXECUTE=0
case "${1:-}" in
--execute)
EXECUTE=1
;;
--help|-h)
usage
exit 0
;;
"")
;;
*)
echo "unknown arg: $1" >&2
usage >&2
exit 2
;;
esac
# Templates and their GHCR repository slugs. Source of truth for the
# runtime → image map is workspace-server/internal/provisioner/provisioner.go
# RuntimeImages — keep this list in sync if a runtime is added.
TEMPLATES=(
"claude-code"
"codex"
"hermes"
"openclaw"
)
# Pre-flight: required tooling.
need() {
command -v "$1" >/dev/null || { echo "ERROR: missing required tool: $1" >&2; exit 1; }
}
need gh
need curl
need jq
# Pre-flight: gh auth. Snapshot via anonymous GHCR token works without
# org auth, but workflow disable needs an authenticated gh.
if ! gh auth status >/dev/null 2>&1; then
echo "ERROR: gh not authenticated. Run 'gh auth login' first." >&2
exit 1
fi
# Snapshot location relative to this script. Keeping it under scripts/
# rather than a temp dir means freeze receipts are easy to find again
# during the actual demo.
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SNAPSHOT_DIR="${SCRIPT_DIR}/demo-freeze-snapshots"
mkdir -p "$SNAPSHOT_DIR"
TS="$(date -u +%Y%m%d-%H%M%S)"
DIGESTS_FILE="${SNAPSHOT_DIR}/digests-${TS}.txt"
WORKFLOWS_FILE="${SNAPSHOT_DIR}/disabled-workflows-${TS}.txt"
if [ $EXECUTE -eq 0 ]; then
echo "=== DRY RUN (no changes will be made; pass --execute to apply) ==="
else
echo "=== EXECUTING FREEZE — workflows will be disabled ==="
fi
echo "Snapshot timestamp: $TS"
echo "Digest log: $DIGESTS_FILE"
echo "Workflow log: $WORKFLOWS_FILE"
echo
# Step 1: capture current :latest digest for each template.
echo "→ Capturing current :latest digests"
for tpl in "${TEMPLATES[@]}"; do
token=$(curl -fsS "https://ghcr.io/token?scope=repository:molecule-ai/workspace-template-${tpl}:pull" | jq -r .token 2>/dev/null || true)
if [ -z "$token" ] || [ "$token" = "null" ]; then
echo " WARN: token fetch failed for $tpl — skipping digest capture"
continue
fi
digest=$(curl -fsSI \
-H "Authorization: Bearer $token" \
-H "Accept: application/vnd.oci.image.index.v1+json" \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
"https://ghcr.io/v2/molecule-ai/workspace-template-${tpl}/manifests/latest" 2>/dev/null \
| grep -i 'docker-content-digest' \
| awk '{print $2}' \
| tr -d '\r')
if [ -z "$digest" ]; then
echo " WARN: digest fetch failed for $tpl"
continue
fi
echo " $tpl: $digest"
if [ $EXECUTE -eq 1 ]; then
echo "$tpl: $digest" >> "$DIGESTS_FILE"
fi
done
echo
# Step 2: disable publish-runtime.yml in molecule-core (PATH 1 source).
echo "→ Disabling publish-runtime.yml in molecule-core (kills runtime → 8-template cascade)"
if [ $EXECUTE -eq 1 ]; then
if gh workflow disable publish-runtime.yml -R Molecule-AI/molecule-core 2>/tmp/freeze.err; then
echo " OK molecule-core/publish-runtime.yml disabled"
echo "Molecule-AI/molecule-core: publish-runtime.yml" >> "$WORKFLOWS_FILE"
else
echo " FAIL molecule-core/publish-runtime.yml: $(cat /tmp/freeze.err)" >&2
fi
else
echo " (dry-run) would disable: gh workflow disable publish-runtime.yml -R Molecule-AI/molecule-core"
fi
echo
# Step 3: disable publish-image.yml in each of the 8 template repos (PATH 2 sources).
echo "→ Disabling publish-image.yml in each workspace-template-* repo"
PARTIAL_FAIL=0
for tpl in "${TEMPLATES[@]}"; do
repo="Molecule-AI/molecule-ai-workspace-template-${tpl}"
if [ $EXECUTE -eq 1 ]; then
if gh workflow disable publish-image.yml -R "$repo" 2>/tmp/freeze.err; then
echo " OK $repo/publish-image.yml disabled"
echo "${repo}: publish-image.yml" >> "$WORKFLOWS_FILE"
else
echo " FAIL $repo/publish-image.yml: $(cat /tmp/freeze.err)" >&2
PARTIAL_FAIL=1
fi
else
echo " (dry-run) would disable: gh workflow disable publish-image.yml -R $repo"
fi
done
echo
if [ $EXECUTE -eq 0 ]; then
echo "=== DRY RUN COMPLETE ==="
echo "Re-run with --execute to apply the freeze."
exit 0
fi
echo "=== FREEZE COMPLETE ==="
echo "Receipts: $DIGESTS_FILE"
echo " $WORKFLOWS_FILE"
echo
echo "Next steps:"
echo " - Verify by running: gh workflow list -R Molecule-AI/molecule-core | grep publish-runtime"
echo " Status should be 'disabled_manually'."
echo " - Demo proceeds; new workspaces pull the snapshotted :latest digests."
echo " - Post-demo, run: scripts/demo-thaw.sh ${TS}"
echo " to re-enable every workflow this freeze disabled."
echo
if [ $PARTIAL_FAIL -ne 0 ]; then
echo "WARNING: one or more workflows did not disable cleanly. Re-run after fixing." >&2
exit 2
fi
exit 0