Skip to content

feat: add --fail-on-unused-images to fail instead of warn when a built image isn't deployed - #10169

Open
ljluestc wants to merge 1 commit into
GoogleContainerTools:mainfrom
ljluestc:fail-on-unused-images-3769
Open

ljluestc wants to merge 1 commit into
GoogleContainerTools:mainfrom
ljluestc:fail-on-unused-images-3769

Conversation

@ljluestc

@ljluestc ljluestc commented Sep 1, 2026

Copy link
Copy Markdown

Fixes: #3769

Description

Today, when Skaffold builds an image that the deployed Helm manifests never reference, the Helm deployer prints a warning and carries on:

WARN[0050] image [gcr.io/tejal-test/gcr.io/kritis-project/kritis-server:v0.2.2-43-g7e4e3d341-dirty@sha256:9af7dad...] is not used.
WARN[0050] image [gcr.io/kritis-project/kritis-server] is used instead.

This almost always means the chart's values.yaml isn't wired up to the tag Skaffold produced, so the deployment silently runs a different image than the one that was just built. In skaffold dev / skaffold run the warning is immediately scrolled off screen by the container logs that follow, and users have to scroll back up to find it — which is exactly what #3769 reports. The result is a confusing debugging session where code changes appear to have no effect.

This PR adds an opt-in --fail-on-unused-images flag that turns that warning into an actionable error and aborts the deployment, so the problem surfaces immediately instead of scrolling away.

Implementation

  • cmd/skaffold/app/cmd/flags.go — new --fail-on-unused-images bool flag (default false), defined on dev, run, debug, and deploy. As with every registered flag it is also settable via SKAFFOLD_FAIL_ON_UNUSED_IMAGES.
  • pkg/skaffold/config/options.go — new SkaffoldOptions.FailOnUnusedImages field.
  • pkg/skaffold/runner/runcontext/context.go — RunContext.FailOnUnusedImages() accessor, alongside the existing ForceDeploy().
  • pkg/skaffold/deploy/helm/helm.go — the Helm Config interface gains FailOnUnusedImages() bool, and the deployer stores it. warnAboutUnusedImages becomes checkUnusedImages, which now collects all unused tags first and then either warns (default, byte-for-byte the same messages as before) or returns an error. Deploy propagates that error. The existing multi-config carve-out is preserved unchanged: multi-config projects can legitimately have artifacts that no single deployer consumes, so they are still skipped entirely.
  • pkg/skaffold/helm/errors.go — new UnusedImagesErr, built with sErrors.NewErrorWithStatusCode like the other Helm errors, so the failure flows through Skaffold's structured error reporting rather than being an opaque fmt.Errorf. It carries StatusCode_DEPLOY_HELM_USER_ERR and a FIX_SKAFFOLD_CONFIG_HELM_ARTIFACT_OVERRIDES suggestion pointing at the Helm image-configuration docs.

The default code path is unchanged: without the flag, users get exactly the same warnings they get today.

User facing changes

New flag on dev, run, debug, and deploy:

--fail-on-unused-images=false:
    Fail the deployment if an image built by Skaffold is not used by the deployed
    manifests, instead of only printing a warning

Before (and still the default, with no flag):

$ skaffold run
...
WARN[0050] image [gcr.io/k8s-skaffold/skaffold-helm:v1.0.0@sha256:9af7dad...] is not used.
WARN[0050] See helm documentation on how to replace image names with their actual tags: https://skaffold.dev/docs/pipeline-stages/deployers/helm/#image-configuration
... (deployment proceeds, warning scrolls off screen behind container logs)

After, with the flag:

$ skaffold run --fail-on-unused-images
...
images built by skaffold are not used by the deployed manifests: gcr.io/k8s-skaffold/skaffold-helm:v1.0.0@sha256:9af7dad...
Suggestion: Set the image in your chart values so that the built tag is used, see
https://skaffold.dev/docs/pipeline-stages/deployers/helm/#image-configuration.
Otherwise, remove the artifact from your skaffold config or drop `--fail-on-unused-images`

Because it is opt-in and defaults to false, there is no behavior change for existing users; CI pipelines that want the stricter contract can turn it on (or export SKAFFOLD_FAIL_ON_UNUSED_IMAGES=true).

The generated CLI reference (docs-v2/content/en/docs/references/cli/_index.md) is regenerated via ./hack/generate-man.sh and included in this PR.

Testing

Three cases added to TestHelmDeploy in pkg/skaffold/deploy/helm/helm_test.go, driven by a new testBuildsUnused artifact that validDeployYaml deliberately does not reference:

  1. unused image only warns by default — asserts the exact warning strings are still emitted and the deploy succeeds, pinning the unchanged default behavior.
  2. unused image fails the deployment with --fail-on-unused-images — asserts the deploy errors out.
  3. used image does not fail the deployment with --fail-on-unused-images — guards against false positives when every built image is referenced.

The test's helmConfig fake implements the new FailOnUnusedImages() method.

$ go build ./...
$ go test ./pkg/skaffold/deploy/helm/... ./pkg/skaffold/helm/... ./cmd/skaffold/app/cmd/...
ok   github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/deploy/helm
ok   github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/helm
ok   github.com/GoogleContainerTools/skaffold/v2/cmd/skaffold/app/cmd

Follow-up Work

  • The flag is wired into the Helm deployer only, because that is where the "image is not used" warning in dev/run should consider exiting instead of warning when built image is not used in deploy. #3769 originates. If maintainers want the same strictness for the kubectl/kustomize paths, the plumbing (option → run context → deployer Config) is already in place and the equivalent check can be added there in a follow-up.
  • Multi-config projects are still skipped rather than checked per-deployer; making the check config-aware so it can also apply there would be a natural next step.
  • If maintainers would prefer this be configurable in skaffold.yaml (e.g. under deploy.helm) rather than — or in addition to — a CLI flag, happy to move it; the flag was chosen to match how --force and the other deploy-behavior toggles are exposed.

🤖 Generated with Claude Code

https://claude.ai/code/session_017RHHxazDH38gSMesDJHg9C

…uilt images

When a Helm chart doesn't wire a built image into its values, skaffold only
prints a warning. In `dev`/`run` that warning is quickly scrolled off screen by
the container logs that follow, so users never notice their image wasn't
deployed.

Add a `--fail-on-unused-images` flag (dev, run, debug, deploy) that turns the
warning into an actionable error which aborts the deployment. Default behavior
is unchanged.

Fixes GoogleContainerTools#3769

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WtfcVvV8XXi4Tn9s2bE3mm
@ljluestc
ljluestc requested a review from a team as a code owner September 1, 2026 06:14
@google-cla

google-cla Bot commented Sep 1, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dev/run should consider exiting instead of warning when built image is not used in deploy.

1 participant