From eb8179a78ed2e773984be4592ce5589e5375d944 Mon Sep 17 00:00:00 2001 From: Scott McCarty Date: Wed, 11 Nov 2026 10:29:42 -0500 Subject: [PATCH] ci: build once, gate the digest, promote what was tested (RT#1550) Replace the Quay+GHCR build/push workflow with the constitution v1.39.1 container-release caller. Gate: boot mcp over stdio (--storage-backend sqlite_vec) since the default CMD serves SSE on 0.0.0.0 and needs MCP_API_KEY. Drop GHCR, bump constitution to v1.39.1. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01R7Y1jh5QhdxRuFKyWVev5h --- .gemini/styleguide.md | 5 +- .github/workflows/constitution.yml | 2 +- .github/workflows/container.yml | 131 +++------------------ .github/workflows/dependabot-automerge.yml | 2 +- .specify/memory/constitution.md | 6 +- 5 files changed, 26 insertions(+), 120 deletions(-) diff --git a/.gemini/styleguide.md b/.gemini/styleguide.md index 588c4e4..1d7aafe 100644 --- a/.gemini/styleguide.md +++ b/.gemini/styleguide.md @@ -21,9 +21,8 @@ ## CI - Workflows MUST include weekly cron trigger for base image security updates -- Build caching is MANDATORY: `cache-from: type=gha` / `cache-to: type=gha,mode=max` -- Never use `no-cache: true` in CI workflows -- Dual-push (Quay + GHCR) uses two separate jobs, not steps +- Container builds go through `crunchtools/constitution/.github/workflows/container-release.yml`: build once per change, gate the digest, promote by `skopeo copy --all --preserve-digests`. Outside the repos listed in `container_release_custom` (profiles/requirements.toml), flag any job that publishes an image (`push`, `skopeo copy`, `docker/build-push-action` with push) or moves `:latest`/`:main`; a local build that is never pushed (an integration-test image) is fine. Everywhere, flag `imagetools create` and a `ghcr.io/crunchtools` reference +- Quay.io is the only registry ## Versioning - Semantic Versioning 2.0.0 diff --git a/.github/workflows/constitution.yml b/.github/workflows/constitution.yml index 25788b8..51be531 100644 --- a/.github/workflows/constitution.yml +++ b/.github/workflows/constitution.yml @@ -18,4 +18,4 @@ permissions: jobs: constitution: name: Constitution - uses: crunchtools/constitution/.github/workflows/validate.yml@v1.31.0 + uses: crunchtools/constitution/.github/workflows/validate.yml@v1.39.1 diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index a96cd9a..e33c5e1 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -1,120 +1,27 @@ -name: Container Build & Push - +name: Container on: + pull_request: push: branches: [main] tags: ["v*"] - pull_request: - branches: [main] + schedule: + - cron: "17 5 * * 1" workflow_dispatch: - -env: - REGISTRY: quay.io - IMAGE_NAME: crunchtools/memory - GHCR_IMAGE: ghcr.io/crunchtools/memory - +permissions: {} jobs: - build-and-push-quay: - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: Checkout repository - uses: actions/checkout@v7 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - - name: Log in to Quay.io - if: github.event_name != 'pull_request' - uses: docker/login-action@v4 - with: - registry: ${{ env.REGISTRY }} - username: ${{ secrets.QUAY_USERNAME }} - password: ${{ secrets.QUAY_PASSWORD }} - - - name: Extract metadata (tags, labels) - id: meta - uses: docker/metadata-action@v6 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - # `latest` is the newest STABLE RELEASE, not the newest build of the - # branch, which has its own tag above (constitution 1.33.0, RT#1559). - # A prerelease is not what `latest` means, so it is excluded by the - # hyphen: SemVer puts every prerelease identifier after one. - type=raw,value=latest,enable=${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') && !contains(github.ref_name, '-') }} - - - name: Build and push container image - uses: docker/build-push-action@v7 - with: - context: . - file: ./Containerfile - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: Run Trivy vulnerability scanner - continue-on-error: true - if: github.event_name != 'pull_request' - uses: aquasecurity/trivy-action@v0.36.0 - with: - image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} - format: "table" - exit-code: "0" - severity: "CRITICAL,HIGH" - - build-and-push-ghcr: - runs-on: ubuntu-latest - needs: build-and-push-quay - if: github.event_name != 'pull_request' + container: + uses: crunchtools/constitution/.github/workflows/container-release.yml@v1.39.1 permissions: contents: read - packages: write - - steps: - - name: Checkout repository - uses: actions/checkout@v7 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v4 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ github.token }} - - - name: Extract metadata (tags, labels) - id: meta - uses: docker/metadata-action@v6 - with: - images: ${{ env.GHCR_IMAGE }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - # `latest` is the newest STABLE RELEASE, not the newest build of the - # branch, which has its own tag above (constitution 1.33.0, RT#1559). - # A prerelease is not what `latest` means, so it is excluded by the - # hyphen: SemVer puts every prerelease identifier after one. - type=raw,value=latest,enable=${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') && !contains(github.ref_name, '-') }} - - - name: Build and push container image - uses: docker/build-push-action@v7 - with: - context: . - file: ./Containerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha + pull-requests: read + with: + image: quay.io/crunchtools/memory + # The image's default CMD serves SSE on 0.0.0.0, which the server refuses + # without MCP_API_KEY and which the mcp-http profile cannot speak. Boot it + # over stdio (no transport flag) and require initialize + tools/list. + boot: mcp + boot-cmd: "--storage-backend sqlite_vec" + boot-timeout: 240 + secrets: + QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }} + QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index 794a085..47017c6 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -15,4 +15,4 @@ permissions: jobs: automerge: name: Dependabot - uses: crunchtools/constitution/.github/workflows/dependabot-automerge.yml@v1.31.0 + uses: crunchtools/constitution/.github/workflows/dependabot-automerge.yml@v1.39.1 diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md index dc37992..0f20c88 100644 --- a/.specify/memory/constitution.md +++ b/.specify/memory/constitution.md @@ -3,7 +3,7 @@ > **Version:** 1.0.1 > **Ratified:** 2026-10-02 > **Status:** Active -> **Inherits:** [crunchtools/constitution](https://github.com/crunchtools/constitution) v1.31.0 +> **Inherits:** [crunchtools/constitution](https://github.com/crunchtools/constitution) v1.39.1 > **Profile:** Forked MCP Server This file holds what is specific to the memory image. The fleet rules and the @@ -58,8 +58,8 @@ file still crashes; keep it populated. ## Deviations - **Image name and registries.** The image is `quay.io/crunchtools/memory`, - not `mcp-memory`, and `container.yml` also pushes `ghcr.io/crunchtools/memory`. - Both predate the profile; renaming would break running deployments. + not `mcp-memory`; the name predates the profile and renaming would break + running deployments. Quay.io is the only registry. - **Base image.** The Hummingbird Python image is pulled from `registry.access.redhat.com/hi/python:3.14` (builder and runtime), pinned to Python 3.14, rather than `quay.io/hummingbird/python:latest`. The builder