From 5932d9c3b4616af588376c783320fb5755167b95 Mon Sep 17 00:00:00 2001 From: Deepthi Rao Date: Wed, 29 Jul 2026 15:53:09 -0400 Subject: [PATCH 1/3] fix(lib): build ':latest' tutorial agents with --no-cache to stop shipping stale source The tutorial-agent build/publish pipeline could silently republish a stale image to the moving ':latest' tag. 'agentex agents build' invoked 'docker.buildx.build' with no cache control, so a cached layer could ship source that no longer matched the checkout -- e.g. the merged 'mcp<2' pin for the 020_state_machine agent never reached ':latest', leaving integration tests pulling a months-old image and failing on the mcp 2.0.0 'McpError' rename. - add a 'cache' param to build_agent() -> passes cache=False (buildx --no-cache) through to the build - expose '--cache/--no-cache' on 'agentex agents build' (default: cache on, so local dev and immutable SHA builds stay fast) - build-and-push-tutorial-agent.yml uses --no-cache only for the ':latest' publish path; SHA-tagged validation builds keep the cache Related: build-provenance work (#454) records a working-tree hash and could later provide a more surgical cache-key-based fix; this is the immediate, guaranteed prevention. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/build-and-push-tutorial-agent.yml | 7 ++++++- src/agentex/lib/cli/commands/agents.py | 7 +++++++ src/agentex/lib/cli/handlers/agent_handlers.py | 8 ++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-and-push-tutorial-agent.yml b/.github/workflows/build-and-push-tutorial-agent.yml index 1b9cda3eb..c9e62cc89 100644 --- a/.github/workflows/build-and-push-tutorial-agent.yml +++ b/.github/workflows/build-and-push-tutorial-agent.yml @@ -185,10 +185,15 @@ jobs: if [ "${{ github.event_name }}" = "push" ] || [ "${{ inputs.rebuild_all }}" = "true" ]; then SHOULD_PUSH=true VERSION_TAG="latest" + # ':latest' is a moving tag, so build without cache: a stale cached layer + # must never silently ship source that differs from the checkout. + CACHE_FLAG="--no-cache" echo "🚀 Building agent (will push after validation): ${{ matrix.agent_path }}" else SHOULD_PUSH=false VERSION_TAG="${{ github.sha }}" + # SHA-tagged validation build is immutable, so the cache is safe (and faster). + CACHE_FLAG="--cache" echo "🔍 Building agent for validation: ${{ matrix.agent_path }}" # Set full image name for validation step (local build) echo "FULL_IMAGE=${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}" >> $GITHUB_ENV @@ -197,7 +202,7 @@ jobs: fi # Always build locally first (without push) - BUILD_ARGS="--manifest ${{ matrix.agent_path }}/manifest.yaml --registry ${REGISTRY} --tag ${VERSION_TAG} --platforms linux/amd64,linux/arm64 --repository-name ${REPOSITORY_NAME}" + BUILD_ARGS="--manifest ${{ matrix.agent_path }}/manifest.yaml --registry ${REGISTRY} --tag ${VERSION_TAG} --platforms linux/amd64,linux/arm64 --repository-name ${REPOSITORY_NAME} ${CACHE_FLAG}" agentex agents build $BUILD_ARGS echo "✅ Successfully built: ${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}" diff --git a/src/agentex/lib/cli/commands/agents.py b/src/agentex/lib/cli/commands/agents.py index c4b49a15e..fc3b9fa52 100644 --- a/src/agentex/lib/cli/commands/agents.py +++ b/src/agentex/lib/cli/commands/agents.py @@ -127,6 +127,12 @@ def build( None, help="Docker build argument in the format 'KEY=VALUE' (can be used multiple times)", ), + cache: bool = typer.Option( + True, + "--cache/--no-cache", + help="Use the build cache (default). Pass --no-cache when republishing a moving " + "tag like ':latest' so a stale cached layer can't ship outdated source.", + ), ): """ Build an agent image locally from the given manifest. @@ -155,6 +161,7 @@ def build( secret=secret or "", # Provide default empty string tag=tag or "latest", # Provide default build_args=build_arg or [], # Provide default empty list + cache=cache, ) if image_url: typer.echo(f"Successfully built image: {image_url}") diff --git a/src/agentex/lib/cli/handlers/agent_handlers.py b/src/agentex/lib/cli/handlers/agent_handlers.py index 1f2ccc7ef..ece3a788a 100644 --- a/src/agentex/lib/cli/handlers/agent_handlers.py +++ b/src/agentex/lib/cli/handlers/agent_handlers.py @@ -39,6 +39,7 @@ def build_agent( secret: str | None = None, tag: str | None = None, build_args: list[str] | None = None, + cache: bool = True, ) -> str: """Build the agent locally and optionally push to registry @@ -49,6 +50,10 @@ def build_agent( secret: Docker build secret in format 'id=secret-id,src=path-to-secret-file' tag: Image tag to use (defaults to 'latest') build_args: List of Docker build arguments in format 'KEY=VALUE' + cache: Whether to use the build cache. Defaults to True. Set to False (passes + --no-cache to buildx) when republishing a moving tag like ':latest' so a + stale cached layer can't silently ship source that no longer matches the + checkout. Returns: The image URL @@ -85,7 +90,10 @@ def build_agent( "file": str(build_context.path / build_context.dockerfile_path), # type: ignore[operator] "tags": [image_name], "platforms": platforms, + "cache": cache, # cache=False -> `docker buildx build --no-cache` } + if not cache: + logger.info("Build cache disabled (--no-cache)") # Add Docker build args if provided if build_args: From 459ddac33841ac0967b12ce0b88762d27c065072 Mon Sep 17 00:00:00 2001 From: Deepthi Rao Date: Wed, 29 Jul 2026 16:09:51 -0400 Subject: [PATCH 2/3] chore(ci): always build tutorial agents with --no-cache Force --no-cache on every build in the publish workflow (both the ':latest' push path and SHA-tagged validation builds), not just the mutable-tag path. Simpler and removes any chance of a stale cached layer shipping outdated source; costs only a few minutes of build time. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/build-and-push-tutorial-agent.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-and-push-tutorial-agent.yml b/.github/workflows/build-and-push-tutorial-agent.yml index c9e62cc89..450c1e74f 100644 --- a/.github/workflows/build-and-push-tutorial-agent.yml +++ b/.github/workflows/build-and-push-tutorial-agent.yml @@ -185,15 +185,10 @@ jobs: if [ "${{ github.event_name }}" = "push" ] || [ "${{ inputs.rebuild_all }}" = "true" ]; then SHOULD_PUSH=true VERSION_TAG="latest" - # ':latest' is a moving tag, so build without cache: a stale cached layer - # must never silently ship source that differs from the checkout. - CACHE_FLAG="--no-cache" echo "🚀 Building agent (will push after validation): ${{ matrix.agent_path }}" else SHOULD_PUSH=false VERSION_TAG="${{ github.sha }}" - # SHA-tagged validation build is immutable, so the cache is safe (and faster). - CACHE_FLAG="--cache" echo "🔍 Building agent for validation: ${{ matrix.agent_path }}" # Set full image name for validation step (local build) echo "FULL_IMAGE=${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}" >> $GITHUB_ENV @@ -201,8 +196,10 @@ jobs: echo "SKIP_VALIDATION=true" >> $GITHUB_ENV fi - # Always build locally first (without push) - BUILD_ARGS="--manifest ${{ matrix.agent_path }}/manifest.yaml --registry ${REGISTRY} --tag ${VERSION_TAG} --platforms linux/amd64,linux/arm64 --repository-name ${REPOSITORY_NAME} ${CACHE_FLAG}" + # Always build without cache: a stale cached layer must never silently ship + # source that differs from the checkout (this is how a merged fix once failed + # to reach ':latest'). Correctness over a few minutes of build time. + BUILD_ARGS="--manifest ${{ matrix.agent_path }}/manifest.yaml --registry ${REGISTRY} --tag ${VERSION_TAG} --platforms linux/amd64,linux/arm64 --repository-name ${REPOSITORY_NAME} --no-cache" agentex agents build $BUILD_ARGS echo "✅ Successfully built: ${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}" From 951e7dc7a42646f1b941ad724f196d52e240583d Mon Sep 17 00:00:00 2001 From: Deepthi Rao Date: Wed, 29 Jul 2026 16:16:51 -0400 Subject: [PATCH 3/3] make no-cache the default and drop the workflow flag Default 'agentex agents build' to --no-cache instead of forcing it in the publish workflow. A bare 'agentex agents build' is now cache-free, so the build-and-push workflow needs no change and the diff shrinks to the CLI. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/build-and-push-tutorial-agent.yml | 6 ++---- src/agentex/lib/cli/commands/agents.py | 7 ++++--- src/agentex/lib/cli/handlers/agent_handlers.py | 10 +++++----- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-and-push-tutorial-agent.yml b/.github/workflows/build-and-push-tutorial-agent.yml index 450c1e74f..1b9cda3eb 100644 --- a/.github/workflows/build-and-push-tutorial-agent.yml +++ b/.github/workflows/build-and-push-tutorial-agent.yml @@ -196,10 +196,8 @@ jobs: echo "SKIP_VALIDATION=true" >> $GITHUB_ENV fi - # Always build without cache: a stale cached layer must never silently ship - # source that differs from the checkout (this is how a merged fix once failed - # to reach ':latest'). Correctness over a few minutes of build time. - BUILD_ARGS="--manifest ${{ matrix.agent_path }}/manifest.yaml --registry ${REGISTRY} --tag ${VERSION_TAG} --platforms linux/amd64,linux/arm64 --repository-name ${REPOSITORY_NAME} --no-cache" + # Always build locally first (without push) + BUILD_ARGS="--manifest ${{ matrix.agent_path }}/manifest.yaml --registry ${REGISTRY} --tag ${VERSION_TAG} --platforms linux/amd64,linux/arm64 --repository-name ${REPOSITORY_NAME}" agentex agents build $BUILD_ARGS echo "✅ Successfully built: ${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}" diff --git a/src/agentex/lib/cli/commands/agents.py b/src/agentex/lib/cli/commands/agents.py index fc3b9fa52..801fda350 100644 --- a/src/agentex/lib/cli/commands/agents.py +++ b/src/agentex/lib/cli/commands/agents.py @@ -128,10 +128,11 @@ def build( help="Docker build argument in the format 'KEY=VALUE' (can be used multiple times)", ), cache: bool = typer.Option( - True, + False, "--cache/--no-cache", - help="Use the build cache (default). Pass --no-cache when republishing a moving " - "tag like ':latest' so a stale cached layer can't ship outdated source.", + help="Whether to use the build cache. Defaults to off so a stale cached layer " + "can't silently ship source that no longer matches the checkout (notably when " + "republishing a moving tag like ':latest'). Pass --cache to opt back in.", ), ): """ diff --git a/src/agentex/lib/cli/handlers/agent_handlers.py b/src/agentex/lib/cli/handlers/agent_handlers.py index ece3a788a..322154b92 100644 --- a/src/agentex/lib/cli/handlers/agent_handlers.py +++ b/src/agentex/lib/cli/handlers/agent_handlers.py @@ -39,7 +39,7 @@ def build_agent( secret: str | None = None, tag: str | None = None, build_args: list[str] | None = None, - cache: bool = True, + cache: bool = False, ) -> str: """Build the agent locally and optionally push to registry @@ -50,10 +50,10 @@ def build_agent( secret: Docker build secret in format 'id=secret-id,src=path-to-secret-file' tag: Image tag to use (defaults to 'latest') build_args: List of Docker build arguments in format 'KEY=VALUE' - cache: Whether to use the build cache. Defaults to True. Set to False (passes - --no-cache to buildx) when republishing a moving tag like ':latest' so a - stale cached layer can't silently ship source that no longer matches the - checkout. + cache: Whether to use the build cache. Defaults to False (passes --no-cache to + buildx) so a stale cached layer can't silently ship source that no longer + matches the checkout, notably when republishing a moving tag like ':latest'. + Pass True to opt back in for faster local rebuilds. Returns: The image URL