Skip to content

Commit 19a68ae

Browse files
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) <noreply@anthropic.com>
1 parent 2b7649c commit 19a68ae

3 files changed

Lines changed: 21 additions & 1 deletion

File tree

.github/workflows/build-and-push-tutorial-agent.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,15 @@ jobs:
185185
if [ "${{ github.event_name }}" = "push" ] || [ "${{ inputs.rebuild_all }}" = "true" ]; then
186186
SHOULD_PUSH=true
187187
VERSION_TAG="latest"
188+
# ':latest' is a moving tag, so build without cache: a stale cached layer
189+
# must never silently ship source that differs from the checkout.
190+
CACHE_FLAG="--no-cache"
188191
echo "🚀 Building agent (will push after validation): ${{ matrix.agent_path }}"
189192
else
190193
SHOULD_PUSH=false
191194
VERSION_TAG="${{ github.sha }}"
195+
# SHA-tagged validation build is immutable, so the cache is safe (and faster).
196+
CACHE_FLAG="--cache"
192197
echo "🔍 Building agent for validation: ${{ matrix.agent_path }}"
193198
# Set full image name for validation step (local build)
194199
echo "FULL_IMAGE=${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}" >> $GITHUB_ENV
@@ -197,7 +202,7 @@ jobs:
197202
fi
198203
199204
# Always build locally first (without push)
200-
BUILD_ARGS="--manifest ${{ matrix.agent_path }}/manifest.yaml --registry ${REGISTRY} --tag ${VERSION_TAG} --platforms linux/amd64,linux/arm64 --repository-name ${REPOSITORY_NAME}"
205+
BUILD_ARGS="--manifest ${{ matrix.agent_path }}/manifest.yaml --registry ${REGISTRY} --tag ${VERSION_TAG} --platforms linux/amd64,linux/arm64 --repository-name ${REPOSITORY_NAME} ${CACHE_FLAG}"
201206
202207
agentex agents build $BUILD_ARGS
203208
echo "✅ Successfully built: ${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}"

src/agentex/lib/cli/commands/agents.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ def build(
127127
None,
128128
help="Docker build argument in the format 'KEY=VALUE' (can be used multiple times)",
129129
),
130+
cache: bool = typer.Option(
131+
True,
132+
"--cache/--no-cache",
133+
help="Use the build cache (default). Pass --no-cache when republishing a moving "
134+
"tag like ':latest' so a stale cached layer can't ship outdated source.",
135+
),
130136
):
131137
"""
132138
Build an agent image locally from the given manifest.
@@ -155,6 +161,7 @@ def build(
155161
secret=secret or "", # Provide default empty string
156162
tag=tag or "latest", # Provide default
157163
build_args=build_arg or [], # Provide default empty list
164+
cache=cache,
158165
)
159166
if image_url:
160167
typer.echo(f"Successfully built image: {image_url}")

src/agentex/lib/cli/handlers/agent_handlers.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def build_agent(
3939
secret: str | None = None,
4040
tag: str | None = None,
4141
build_args: list[str] | None = None,
42+
cache: bool = True,
4243
) -> str:
4344
"""Build the agent locally and optionally push to registry
4445
@@ -49,6 +50,10 @@ def build_agent(
4950
secret: Docker build secret in format 'id=secret-id,src=path-to-secret-file'
5051
tag: Image tag to use (defaults to 'latest')
5152
build_args: List of Docker build arguments in format 'KEY=VALUE'
53+
cache: Whether to use the build cache. Defaults to True. Set to False (passes
54+
--no-cache to buildx) when republishing a moving tag like ':latest' so a
55+
stale cached layer can't silently ship source that no longer matches the
56+
checkout.
5257
5358
Returns:
5459
The image URL
@@ -85,7 +90,10 @@ def build_agent(
8590
"file": str(build_context.path / build_context.dockerfile_path), # type: ignore[operator]
8691
"tags": [image_name],
8792
"platforms": platforms,
93+
"cache": cache, # cache=False -> `docker buildx build --no-cache`
8894
}
95+
if not cache:
96+
logger.info("Build cache disabled (--no-cache)")
8997

9098
# Add Docker build args if provided
9199
if build_args:

0 commit comments

Comments
 (0)