From 49c3b25f37d4e8e797e65f8eafc5dd14508ec6f5 Mon Sep 17 00:00:00 2001 From: jvogan <6239693+jvogan@users.noreply.github.com> Date: Thu, 28 May 2026 18:00:41 -0700 Subject: [PATCH] Fix red CI: lowercase GHCR tags; stop gating release check on local Tier A Three workflows were failing on main. Build GeneCluster runner image / Build GeneCluster Superpowers image: Both derived IMAGE_NAME from ${{ github.repository_owner }} ("BioSymphony"), producing tags like ghcr.io/BioSymphony/... which GHCR rejects with "repository name must be lowercase". Compute IMAGE_NAME in a new "Normalize image name" step via ${OWNER,,}, and lowercase the Superpowers DEFAULT_BASE_IMAGE owner the same way so it can pull the runner base image. Public release check (runs on every push + PR): make public-audit -> capability ran capability_probe.py, which exits 1 unless Tier A local tooling (PyMOL/ChimeraX GUIs, conda, the ValarTTS server on :8787) is present. A hosted runner never has these, so the gate could not pass in CI. Add a --no-fail (report-only) flag to the probe and pass it from the `capability` Make target; direct invocation without the flag keeps the exit-1 local-readiness signal. Verified: `make public-release-check` now exits 0 locally (101 unit tests pass, all audits clean). The image-build workflows trigger on push to main (and their own paths), so they re-run on merge. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/build-genecluster-runner.yml | 9 ++++++++- .github/workflows/build-genecluster-superpowers.yml | 11 +++++++++-- Makefile | 2 +- skills/biosymphony/scripts/capability_probe.py | 12 ++++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-genecluster-runner.yml b/.github/workflows/build-genecluster-runner.yml index 68416ee..372de72 100644 --- a/.github/workflows/build-genecluster-runner.yml +++ b/.github/workflows/build-genecluster-runner.yml @@ -19,7 +19,8 @@ permissions: env: REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository_owner }}/genecluster-runner + # IMAGE_NAME is computed (lowercased) in the "Normalize image name" step below. + # GHCR rejects tags containing uppercase characters and the owner is "BioSymphony". jobs: build: @@ -29,6 +30,12 @@ jobs: digest: ${{ steps.build.outputs.digest }} steps: + - name: Normalize image name + # GHCR repository names must be lowercase; github.repository_owner is "BioSymphony". + env: + OWNER: ${{ github.repository_owner }} + run: echo "IMAGE_NAME=${OWNER,,}/genecluster-runner" >> "$GITHUB_ENV" + - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/build-genecluster-superpowers.yml b/.github/workflows/build-genecluster-superpowers.yml index c614776..4decafa 100644 --- a/.github/workflows/build-genecluster-superpowers.yml +++ b/.github/workflows/build-genecluster-superpowers.yml @@ -39,7 +39,8 @@ permissions: env: REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository_owner }}/genecluster-superpowers + # IMAGE_NAME is computed (lowercased) in the "Normalize image name" step below. + # GHCR rejects tags containing uppercase characters and the owner is "BioSymphony". jobs: build: @@ -49,6 +50,12 @@ jobs: digest: ${{ steps.build.outputs.digest }} steps: + - name: Normalize image name + # GHCR repository names must be lowercase; github.repository_owner is "BioSymphony". + env: + OWNER: ${{ github.repository_owner }} + run: echo "IMAGE_NAME=${OWNER,,}/genecluster-superpowers" >> "$GITHUB_ENV" + - name: Checkout uses: actions/checkout@v6 @@ -79,7 +86,7 @@ jobs: BASE_IMAGE_INPUT: ${{ inputs.base_runner_image }} GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} run: | - DEFAULT_BASE_IMAGE="${REGISTRY}/${GITHUB_REPOSITORY_OWNER}/genecluster-runner:v0.1" + DEFAULT_BASE_IMAGE="${REGISTRY}/${GITHUB_REPOSITORY_OWNER,,}/genecluster-runner:v0.1" { echo "args< dict[str, Any]: def main() -> int: parser = argparse.ArgumentParser(description="Probe local BioSymphony capability tiers.") parser.add_argument("--json", action="store_true", help="Emit machine-readable JSON.") + parser.add_argument( + "--no-fail", + action="store_true", + help=( + "Report-only: always exit 0 even when Tier A is not locally ready. " + "Used by `make capability` so CI (which lacks local GUI apps / tooling / " + "the ValarTTS server) does not fail. Direct invocation without this flag " + "keeps the exit-1 signal for local readiness checks." + ), + ) args = parser.parse_args() path_commands = {cmd: shutil.which(cmd) for cmd in PATH_COMMANDS} @@ -173,6 +183,8 @@ def main() -> int: if missing_mods: print("Missing Python modules: " + ", ".join(missing_mods)) + if args.no_fail: + return 0 return 0 if tier_a_ready else 1