molecule-core/scripts/refresh-workspace-images.sh
documentation-specialist 5d4184f4a3 fix(scripts): migrate ghcr.io→ECR + raw.githubusercontent.com→Gitea (#46)
Per documentation-specialist's grep agent (2026-05-07T07:30, see
internal#46): runtime-breaking ghcr.io references in shell scripts +
docker-compose + the slip-past-workflow lint_secret_pattern_drift.py
all need migration. These were missed by security-auditor's
workflow-only audit.

Files (6):

- .github/scripts/lint_secret_pattern_drift.py:40 — workspace-runtime
  pre-commit-checks.sh consumer URL: raw.githubusercontent.com →
  Gitea raw URL (https://git.moleculesai.app/molecule-ai/.../raw/
  branch/main/...). The lint job runs in CI and would 404 today.

- scripts/refresh-workspace-images.sh:54 — workspace-template image
  pull URL: ghcr.io → ECR (153263036946.dkr.ecr.us-east-2.amazonaws.com).

- scripts/rollback-latest.sh — full rewrite of header + auth flow:
  * ghcr.io/molecule-ai/{platform,platform-tenant} → ECR
  * GITHUB_TOKEN with write:packages → AWS ECR auth
    (aws ecr get-login-password). Per saved memory
    reference_post_suspension_pipeline, prod cutover is to ECR.
  * Updated header docs to match new auth flow + prereqs.

- scripts/demo-freeze.sh:13,17 — comment-only ghcr → ECR
  (the script doesn't currently exec these URLs, but the comments
  describe the cascade and need to match reality).

- docker-compose.yml:215-216 — canvas image: ghcr.io → ECR + updated
  the auth comment to describe `aws ecr get-login-password` flow.

- tools/check-template-parity.sh:21 — inline curl install instructions:
  raw.githubusercontent.com → Gitea raw URL.

Hostile self-review:

1. rollback-latest.sh's GITHUB_TOKEN→aws-cli auth swap is a behavior
   change. Operators using this script now need aws CLI
   authenticated for region us-east-2 with ECR pull/push perms.
   Documented in updated header. Operators who don't have aws CLI
   will get 'aws: command not installed' which is a clear failure
   mode (not silent).
2. The Gitea raw URL shape (/raw/branch/main/) differs from GitHub's
   raw.githubusercontent.com structure. Verified pattern by
   inspecting other Gitea raw URLs in the codebase. If Gitea's URL
   changes (1.23+), update via the same one-line edit.
3. Doesn't touch packer/scripts/install-base.sh which has a similar
   ghcr.io ref per the grep agent's findings — that's bigger-scope
   (packer build pipeline) and lives in molecule-controlplane-ish
   territory; filing as parked follow-up under #46 if not already.

Refs: molecule-ai/internal#46, molecule-ai/internal#37,
molecule-ai/internal#38, saved memory reference_post_suspension_pipeline
2026-05-07 00:56:23 -07:00

96 lines
3.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# refresh-workspace-images.sh — pull the latest workspace template images
# from GHCR and recreate any running ws-* containers against the new digest.
#
# This is the local-dev / single-host equivalent of step 5 of the runtime CD
# chain (see docs/workspace-runtime-package.md). On a SaaS deployment the
# host's deploy pipeline does the pull on every release; this script is
# what to run on a local docker-compose host after a runtime release lands.
#
# Usage:
# bash scripts/refresh-workspace-images.sh # pull all 8 + recreate running ws-*
# bash scripts/refresh-workspace-images.sh --runtime claude-code # pull just one template
# bash scripts/refresh-workspace-images.sh --no-recreate # pull only, leave containers
#
# Behavior:
# - Always pulls fresh; docker is a no-op if local matches remote, so
# repeated runs are cheap.
# - Recreate is "kill + remove + let the next canvas interaction re-
# provision" — simpler than `docker stop / docker run` because the
# platform owns the run flags. Workspaces re-register on next probe.
# - If a container is mid-conversation, the kill cancels in-flight work.
# Run during a quiet window OR add --no-recreate and recreate manually
# via canvas Restart buttons.
set -euo pipefail
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
NC='\033[0m'
log() { echo -e "${GREEN}[refresh]${NC} $1" >&2; }
warn() { echo -e "${YELLOW}[refresh]${NC} $1" >&2; }
err() { echo -e "${RED}[refresh]${NC} $1" >&2; }
ALL_RUNTIMES=(claude-code langgraph crewai autogen deepagents hermes gemini-cli openclaw)
RUNTIMES=("${ALL_RUNTIMES[@]}")
RECREATE=true
while [ $# -gt 0 ]; do
case "$1" in
--runtime) RUNTIMES=("$2"); shift 2;;
--no-recreate) RECREATE=false; shift;;
-h|--help) sed -n '2,30p' "$0"; exit 0;;
*) err "unknown arg: $1"; exit 2;;
esac
done
# 1. Pull fresh tags. Soft-fail per runtime — one missing image (e.g., a
# template that hasn't been published yet) shouldn't abort the others.
log "pulling latest images for: ${RUNTIMES[*]}"
PULLED=()
FAILED=()
for rt in "${RUNTIMES[@]}"; do
IMG="153263036946.dkr.ecr.us-east-2.amazonaws.com/molecule-ai/workspace-template-$rt:latest"
if docker pull "$IMG" >/dev/null 2>&1; then
log "$rt"
PULLED+=("$rt")
else
warn "$rt (pull failed — image may not exist or auth missing)"
FAILED+=("$rt")
fi
done
if [ "$RECREATE" = "false" ]; then
log "skip-recreate set — leaving containers untouched"
log "done. pulled=${#PULLED[@]} failed=${#FAILED[@]}"
exit 0
fi
# 2. Find ws-* containers whose image is one of the runtimes we pulled.
# `docker inspect` exposes the image tag/digest each was created from.
log "scanning ws-* containers for stale images..."
TO_RECREATE=()
for cn in $(docker ps -a --filter "name=ws-" --format "{{.Names}}"); do
IMG=$(docker inspect "$cn" --format '{{.Config.Image}}' 2>/dev/null || echo "")
for rt in "${PULLED[@]}"; do
if [[ "$IMG" == *"workspace-template-$rt"* ]]; then
TO_RECREATE+=("$cn")
break
fi
done
done
if [ "${#TO_RECREATE[@]}" -eq 0 ]; then
log "no running ws-* containers using a refreshed image — nothing to recreate"
exit 0
fi
# 3. Kill + remove. Canvas next-interaction will re-provision.
log "recreating ${#TO_RECREATE[@]} containers (canvas will re-provision on next interaction)"
for cn in "${TO_RECREATE[@]}"; do
docker rm -f "$cn" >/dev/null 2>&1 && log " removed $cn" || warn " failed to remove $cn"
done
log "done. open the canvas and the workspaces will re-provision against the new image."