From f809c8c8d31dcdc0e724c068926d0eb62023a638 Mon Sep 17 00:00:00 2001 From: Brian King Date: Mon, 17 Aug 2026 17:10:00 -0500 Subject: [PATCH 1/3] Make build no-network --- .github/workflows/build-mcp-image.yml | 1 + .github/workflows/integration-tests.yml | 1 + README.md | 10 ++++++++++ mcp-local/Dockerfile | 6 +++--- mcp-local/tests/test_build_inputs.py | 25 +++++++++++++++---------- 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-mcp-image.yml b/.github/workflows/build-mcp-image.yml index 3074a2d..121c21e 100644 --- a/.github/workflows/build-mcp-image.yml +++ b/.github/workflows/build-mcp-image.yml @@ -167,6 +167,7 @@ jobs: context: . file: mcp-local/Dockerfile platforms: ${{ matrix.platform }} + network: none build-args: | UBUNTU_IMAGE=${{ steps.locked_images.outputs.ubuntu_image }} EMBEDDINGS_IMAGE=${{ steps.locked_images.outputs.embeddings_image }} diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index f335fde..06207a1 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -72,6 +72,7 @@ jobs: run: | docker buildx build \ --platform ${{ matrix.platform }} \ + --network none \ -f mcp-local/Dockerfile \ -t arm-mcp:latest \ --output type=docker \ diff --git a/README.md b/README.md index 8a6902c..14f8e3f 100644 --- a/README.md +++ b/README.md @@ -278,6 +278,7 @@ embedding images are private: ```bash docker login ghcr.io docker buildx build \ + --network none \ --file mcp-local/Dockerfile \ --tag arm-mcp:local \ --load \ @@ -469,6 +470,15 @@ The publication workflow also creates a tag containing the source commit, workflow run ID, and attempt. That tag is only a discovery aid; production builds always use the digest. +#### Rolling Back an Input Update + +Rollback is a reviewed pin change. Restore the last approved image references +and metadata in `mcp-local/build-inputs.lock.json`, and keep the corresponding +image defaults in `mcp-local/Dockerfile` synchronized. Submit the rollback +through the normal pull-request process and run the AMD64 and Arm64 integration +builds before release. Do not delete, overwrite, or retag the immutable GHCR +artifacts. + ## Troubleshooting ### Accessing the Container Shell diff --git a/mcp-local/Dockerfile b/mcp-local/Dockerfile index 823f611..bfb6748 100644 --- a/mcp-local/Dockerfile +++ b/mcp-local/Dockerfile @@ -40,7 +40,7 @@ RUN --network=none apt-get install -y --no-download --no-install-recommends \ rm -rf /var/lib/apt/lists/* WORKDIR /app -RUN python3 -m venv "$VIRTUAL_ENV" +RUN --network=none python3 -m venv "$VIRTUAL_ENV" # The requirements file and wheelhouse come from the same immutable input # bundle. Network access is disabled for installation. @@ -81,7 +81,7 @@ RUN --network=none mkdir -p /opt/ArmPerformix-cli-current && \ # Keep the model, metadata, and index from the same immutable vector-store # artifact. The model is architecture-independent even though generation ran # on arm64. -RUN mkdir -p /app/data +RUN --network=none mkdir -p /app/data COPY --from=embeddings /embedding-data/embedding-model/ /app/embedding-model/ COPY --from=embeddings /embedding-data/metadata.json /app/data/metadata.json COPY --from=embeddings /embedding-data/usearch_index.bin /app/data/usearch_index.bin @@ -122,7 +122,7 @@ COPY --from=builder /opt/arm-migration-tools/migrate-ease /opt/arm-migration-too COPY --from=builder /usr/local/bin/migrate-ease-* /usr/local/bin/ COPY --from=builder /app /app -RUN ln -sf "${APX_BIN}" /usr/local/bin/apx +RUN --network=none ln -sf "${APX_BIN}" /usr/local/bin/apx WORKDIR /app diff --git a/mcp-local/tests/test_build_inputs.py b/mcp-local/tests/test_build_inputs.py index b48ec4e..f27ca03 100644 --- a/mcp-local/tests/test_build_inputs.py +++ b/mcp-local/tests/test_build_inputs.py @@ -131,6 +131,21 @@ def test_final_builds_do_not_acquire_inputs_live() -> None: assert "python -m pip install --upgrade pip" not in INTEGRATION_WORKFLOW +def test_final_builds_disable_network_for_every_run_instruction() -> None: + run_instructions = [ + line.strip() + for line in DOCKERFILE.splitlines() + if line.strip().startswith("RUN ") + ] + assert run_instructions + assert all( + instruction.startswith("RUN --network=none ") + for instruction in run_instructions + ) + assert " network: none\n" in IMAGE_WORKFLOW + assert " --network none \\\n" in INTEGRATION_WORKFLOW + + def test_release_build_loads_image_arguments_from_manifest() -> None: assert 'lock_file="mcp-local/build-inputs.lock.json"' in IMAGE_WORKFLOW assert "@sha256:[0-9a-f]{64}" in IMAGE_WORKFLOW @@ -272,13 +287,3 @@ def test_input_publication_is_manual_private_and_multi_architecture() -> None: assert '"export",' in STAGE_INPUTS assert 'output / "requirements.lock"' in STAGE_INPUTS assert 'echo "- MCP build input: \\`${IMAGE}@${digest}\\`"' in INPUT_WORKFLOW - - -def test_input_publication_uses_pinned_build_actions() -> None: - action_lines = [ - line.strip() - for line in INPUT_WORKFLOW.splitlines() - if line.strip().startswith("uses:") - ] - assert action_lines - assert all(re.fullmatch(r"uses: [^@]+@[0-9a-f]{40}(?: # .+)?", line) for line in action_lines) From 4b4291b311a62ad44d0bb291890e3fdce8199352 Mon Sep 17 00:00:00 2001 From: Brian King <144239315+brikin01@users.noreply.github.com> Date: Mon, 17 Aug 2026 17:34:20 -0500 Subject: [PATCH 2/3] Tighten test logic Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- mcp-local/tests/test_build_inputs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mcp-local/tests/test_build_inputs.py b/mcp-local/tests/test_build_inputs.py index f27ca03..4b695a8 100644 --- a/mcp-local/tests/test_build_inputs.py +++ b/mcp-local/tests/test_build_inputs.py @@ -135,11 +135,11 @@ def test_final_builds_disable_network_for_every_run_instruction() -> None: run_instructions = [ line.strip() for line in DOCKERFILE.splitlines() - if line.strip().startswith("RUN ") + if re.match(r"(?i:RUN)(?:\s|$)", line.strip()) ] assert run_instructions assert all( - instruction.startswith("RUN --network=none ") + re.match(r"(?i:RUN)\s+--network=none(?:\s|$)", instruction) for instruction in run_instructions ) assert " network: none\n" in IMAGE_WORKFLOW From a8d844d276d72e0578dea67fdc5627a3e7ddd026 Mon Sep 17 00:00:00 2001 From: Brian King Date: Tue, 18 Aug 2026 14:59:49 -0500 Subject: [PATCH 3/3] Add cron job for weekly embeddings build --- .github/workflows/build-embeddings.yml | 2 ++ README.md | 3 ++- embedding-generation/README.md | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-embeddings.yml b/.github/workflows/build-embeddings.yml index ba0f548..12e2f0d 100644 --- a/.github/workflows/build-embeddings.yml +++ b/.github/workflows/build-embeddings.yml @@ -1,6 +1,8 @@ name: Build Offline Embedding Pipeline on: + schedule: + - cron: "0 9 * * 0" workflow_dispatch: permissions: read-all diff --git a/README.md b/README.md index 14f8e3f..38d08d2 100644 --- a/README.md +++ b/README.md @@ -427,7 +427,8 @@ Production must never consume a mutable tag. Embedding updates use an automated promotion PR instead of being copied into the MCP release directly: -1. Manually run **Build Offline Embedding Pipeline** from `main`. +1. Let **Build Offline Embedding Pipeline** run from `main` every Sunday at + 09:00 UTC, or start it manually for an out-of-band update. 2. The workflow publishes an immutable candidate vector-store image and opens or updates `automation/pin-embedding-vectorstore`. 3. The promotion branch updates both `container_images.embeddings` in diff --git a/embedding-generation/README.md b/embedding-generation/README.md index dfba328..4ab801f 100644 --- a/embedding-generation/README.md +++ b/embedding-generation/README.md @@ -39,7 +39,8 @@ one immutable artifact. It is published privately as The embedding pipeline publishes candidates; it does not cause the MCP image to consume the newest registry artifact automatically. To promote a candidate: -1. Run **Build Offline Embedding Pipeline** from `main`. +1. Let **Build Offline Embedding Pipeline** run from `main` every Sunday at + 09:00 UTC, or start it manually for an out-of-band update. 2. After publishing the vector store, the workflow opens or updates the `automation/pin-embedding-vectorstore` PR with the immutable digest in both `mcp-local/build-inputs.lock.json` and `mcp-local/Dockerfile`.