Changed-Only Diffs

Multi-Application diffs use changed-only selection by default. drydock maps changed files to Application inputs and renders only the affected Applications when ownership is clear.

If any changed file is unowned or ambiguous, non-strict mode warns and renders all Applications (diff.changed-only-incomplete). This preserves correctness at the cost of a broader diff.

drydock also compares the resolved Argo CD settings of the two sides directly, before path ownership is evaluated, so a settings change counts even when the changed settings file is excluded by --changed-only-ignore, as long as another changed path survives the filters. What it selects depends on what reads the setting.

A setting that every render or diff reads renders all Applications, with its own diagnostic (diff.changed-only-settings): the tracking method, instance label, or installation ID, Kustomize build options, Helm value file schemes, a CMP definition, resource exclusions or inclusions, compare options, a resource customization a diff or health check reads (ignoreDifferences, knownTypeFields, health Lua, useOpenLibs), or which repository Secrets enable OCI for an OCI repository URL. Unlike an unowned-path fallback, this render-all is exempt from --strict-changed-only and --strict: rendering every Application is the complete answer to a settings change, not a gap in ownership.

A repository or cluster Secret that is added, removed, or changed reaches Applications only through project validation, so it selects only the Applications that use it, reported with diff.changed-only-settings-scoped (also strict-exempt). A repository Secret selects every Application with a source whose repoURL matches its URL, chart and $ref sources included, and a project-scoped deny pattern (!url) every Application in its project. A deny pattern scoped to default reaches every Application: with no AppProject declared, each one validates against the implicit default project, whatever project it names. A cluster Secret selects every Application whose destination names the cluster or points at its server, and a project-scoped cluster Secret every Application in that project. Such a change does not excuse an unowned changed path: that still renders all Applications with diff.changed-only-incomplete, and --strict-changed-only still fails.

Settings that no build, diff, or test reads select nothing: resource.ignoreResourceUpdatesEnabled, resource.customizations.ignoreResourceUpdates.*, and action Lua (resource.customizations.actions.*). They do not excuse an unowned changed path either.

A changed AppProject selects every Application in that project, not just the Application that owns the AppProject file: tightening sourceRepos, destinations, or resource lists re-validates each member, so changed-only reports the same project diagnostics as a full diff. Declaring the first AppProject or removing the last renders all Applications instead, with its own diagnostic (diff.changed-only-projects): with no AppProject every Application validates against the implicit default project, whatever project it names. Like diff.changed-only-settings, this render-all is exempt from --strict-changed-only and --strict. With --project-diagnostics off, AppProject changes select nothing extra.

Commands

Use the default behavior:

drydock diff apps --repo . --ref HEAD --ref-orig main

Render everything explicitly:

drydock diff apps --repo . --ref HEAD --ref-orig main --changed-only=false

Fail when ownership cannot be proven:

drydock diff apps --repo . --ref HEAD --ref-orig main --strict-changed-only

Scope the changed paths considered by ownership mapping:

drydock diff apps \
  --repo . \
  --ref HEAD \
  --ref-orig main \
  --changed-only-include 'apps/**' \
  --changed-only-ignore '.github/**'

Include and ignore globs are repository-relative. If no include globs are set, all changed paths are considered. Ignore globs are applied after includes, so ignore wins. When every changed path is filtered out, drydock reports an empty diff instead of rendering the fleet.

diff app selects one requested Application directly and does not use changed-only Git path filtering.

Changed-Only Ignore vs Discover Ignore

--changed-only-ignore and --discover-ignore share glob syntax but act at different stages. --changed-only-ignore filters which changed paths are considered for changed-only ownership; the files themselves are still discovered and decoded. --discover-ignore removes matching files from repository discovery before decoding, on every command that discovers Applications.

The two flag families stay independent under --strict-changed-only: a discover-ignored file that changes still surfaces as an unowned changed path unless it is also --changed-only-ignored. Repositories that commit non-deployable YAML usually want the same glob in both flags:

drydock diff apps \
  --repo . \
  --ref HEAD \
  --ref-orig main \
  --strict-changed-only \
  --discover-ignore 'templates/**' \
  --changed-only-ignore 'templates/**'

Mental Model

Changed-only behavior is Argo Application-aware. Shared resources from different Applications remain separate diff identities; drydock does not collapse overlapping Applications into one owner.

Ownership follows the local Kustomize input graph, not only spec.source.path. An Application whose path is an overlay such as apps/demo/overlays/staging also owns the files that overlay reaches through resources, bases, components, helmCharts.valuesFile, helmCharts.additionalValuesFiles, and local patch, generator, and transformer paths, plus the components and patch files the Application adds through spec.source.kustomize.components and spec.source.kustomize.patches. Editing apps/demo/base/helm-release/values.yaml therefore selects every overlay that includes that base. Only sources that render from the local checkout contribute: sources fetched from another repository, repo-mapped to another checkout, or stored in OCI do not. Plugin sources contribute when their path holds a kustomization, because AVP and native Kustomize plugin compatibility build it with Kustomize; explicit Helm and Directory sources do not.

A Helm source also owns each helm.valueFiles and helm.fileParameters[].path entry it resolves, even when the resolved path escapes spec.source.path, bound to the repository root. A glob value file owns its static leading directory, not the files the glob expands to. Entries that use environment substitution ($) own nothing. A $ref value file or file parameter owns its resolved path when the ref root is the local tree: a self-repository ref, a ref --repo-mapped to the repository root, or one sharing the repository and revision of a source whose path is in the checkout. A ref drydock fetches, including a pinned commit SHA or OCI, owns nothing.

An Application’s argocd.argoproj.io/manifest-generate-paths annotation adds ownership that drydock cannot infer, such as the files an exec or container plugin reads. Entries use Argo CD’s syntax and never own the repository root. A declared path counts as owned like any other, so every plugin Application that reads it must declare it. See Declare plugin inputs .

An Application rendered by a parent Application (app-of-apps, including Applications generated by an ApplicationSet the parent renders) is selected whenever its parent is, transitively, so any change the parent owns selects every child it renders. Editing a Helm app-of-apps parent’s values file, for example, can change a child’s spec.source.path without touching any file under the child’s own source path; drydock still selects the child because it selects the parent. An Application declared by a --discover-kustomize directory or a policy-bootstrap entrypoint also owns that directory’s Kustomize graph.

If the Kustomize graph cannot be read, for example because a kustomization escapes the repository or fails to parse, the Application keeps only its spec.source.path ownership. A change only that graph would have owned then stays unowned and falls back to rendering all Applications. A change another Application’s graph also reaches selects only that Application.

Path filters are explicit command or workflow policy. Keep filters narrow in repositories that use plugins or unusual generation paths, because an ignored file is no longer eligible to trigger a render.

For diff outputs and exit codes, see Output controls and Compatibility .