Documentation
Usage
drydock is a runtime-offline CLI for Argo CD GitOps repositories. Default
commands render and compare desired state from checked-out files plus explicit
local caches. They do not contact a Kubernetes cluster or Argo CD server, and
they do not require kubectl, argocd, Helm CLI, or Kustomize CLI.
Declared Git, HTTP Helm, OCI Helm, and remote Kustomize sources may be fetched
into drydock caches unless --offline is set. Use --offline when the command
must be cache-only.
Application Discovery
List discovered Application CRs, supported generated ApplicationSet
Applications, and rendered child Applications from app-of-apps bootstrap
sources:
drydock get apps --path .
get apps defaults to table output and supports -o table, -o name,
-o json, and -o yaml. Use -l/--selector with Kubernetes label selector
syntax to match Application.metadata.labels:
drydock get apps --path . -l 'env in (prod,stage),tier!=test'
List rendered image references from Applications:
drydock get images --path . -o name
get images uses the same output formats as get apps. Diagnostics are
printed to stderr for both commands.
By default, fleet commands recursively render discovered Applications to find
additional Argo CD Application, ApplicationSet, AppProject, and settings
objects in their desired output. This lets repositories that commit a bootstrap
Application or Helm app-of-apps chart behave like repositories that commit the
inflated child objects. Discovery stops when it converges, with a default
maximum depth of 4.
Use --discovery-mode static to disable recursive rendered fleet discovery.
Committed ApplicationSet objects still expand through supported offline
generators. Use --max-discovery-depth 0 to keep normal static discovery and
explicit --discover-kustomize rendering while disabling recursive rendered
fleet discovery.
Static committed objects take precedence over default rendered fleet objects with the same API version, kind, namespace, and name. Rendered duplicates emit diagnostics instead of silently replacing committed intent.
Supported local ApplicationSet generators are Git directories, Git files,
list, matrix, merge, and explicit fixture-backed provider generators.
Unsupported generators emit diagnostics; non-strict commands keep supported
Applications, while --strict promotes the diagnostics to errors.
For generator details, fixture schemas, and stable template parameters, see
applicationsets.md
.
Committed and rendered AppProject manifests are discovered for offline
diagnostics only. Discovery does not contact a Kubernetes cluster or Argo CD
server.
Some repositories commit Argo CD bootstrap inputs as Kustomize bases and
overlays rather than committing the fully inflated Argo CD objects. Add
--discover-kustomize PATH to render one or more local Kustomize entrypoints
with drydock’s native renderer, then discover Application, ApplicationSet,
AppProject, and Argo CD settings objects from the rendered output:
drydock get apps --path . --discover-kustomize argocd/overlays/prod
drydock test apps --path . --discover-kustomize clusters/prod/argocd
The path must be relative to --path, must not escape through .., and must
not include symlinked path components. Rendered discovery is additive: normal
repository scanning still runs, and explicitly rendered Kustomize objects take
precedence over committed duplicates with the same identity.
Rendering
Build every discovered Application:
drydock build apps --path .
Build exactly one Application by metadata.name:
drydock build app renovate --path .
Use NAMESPACE/NAME when a name appears in multiple namespaces:
drydock build app argocd/my-app --path .
build app errors when no discovered Application matches. The unqualified
NAME form must identify exactly one Application.
When one selected Application fails to render, embedding callers receive a
partial BuildResult containing successful manifests, diagnostics, and
per-Application statuses. CLI build commands keep stdout parseable: on render
failure, diagnostics go to stderr and invalid partial manifest streams are not
mixed into stdout.
Use --parallelism N on render-backed commands to render up to N
Applications concurrently. Multi-Application test and diff commands default
to bounded host CPU parallelism; single-Application commands default to 1.
Parallel rendering preserves selected Application order for manifests,
statuses, diagnostics, cache events, and structured output.
Rendering supports directory sources, directory Jsonnet, Kustomize sources,
local Helm charts, Kustomize helmCharts, remote Kustomize HTTP(S) files and
Git refs, and Argo CD chart-only remote Helm sources. Path-based Git sources
use the local --path tree when the source path exists there. Use
--repo-map URL=PATH to force a source repository URL to a local checkout.
Repositories tagged with argocd or gitops are not always Argo CD
Application fleet repositories. drydock test apps reports zero applications
when no Application or supported ApplicationSet objects are discovered.
For acquisition, cache, auth, and remote source details, see
source-acquisition.md
.
Cache Lifecycle
Print resolved cache roots:
drydock cache path
List recognized Git, chart, and remote Kustomize cache entries:
drydock cache list
drydock cache list --source chart -o json
Report stale entries without deleting them:
drydock cache prune --older-than 720h --dry-run
Delete a specific entry or all selected entries:
drydock cache delete --source git --key HASH --yes
drydock cache delete --source remote --all --dry-run
cache prune and cache delete require --yes for non-dry-run deletion.
Dry-runs never require confirmation and leave cache files in place.
Cache lifecycle commands are local filesystem operations only. They do not render Applications, clone/fetch Git repositories, fetch Helm charts, fetch remote Kustomize resources, or read credential flags. They operate only on recognized drydock cache entry roots and reject cache roots that resolve inside the current working directory, selected repository roots, Git repository trees, or symlink-resolved equivalents.
Go API
Use github.com/sholdee/drydock/pkg/drydock when embedding the renderer:
client := drydock.NewClient(drydock.Config{
Path: ".",
RepoMaps: []drydock.RepoMap{
{URL: "https://github.com/example/repo", Path: "/work/repo"},
},
})
result, err := client.Render(ctx)
Package-level Render, ListApplications, DiffApplications, and
DiffImages functions use the same default network and cache behavior as the
CLI. NewClient accepts public Git, chart, remote-resource, and plugin
renderer interfaces so tests and embedding callers can provide deterministic
fakes without importing internal packages.
Public render results include Applications, manifests, diagnostics, and
per-Application statuses. If one selected Application fails, Render returns
the error and still returns partial successful manifests, stable diagnostics,
and statuses. SkipKinds, SkipCRDs, and SkipSecrets apply the same
rendered-resource filters exposed by the CLI.
Config management plugin sources are explicit. The CLI and default Go client do
not execute plugin commands by default. Discovered Argo CD CMP definitions that
normalize to a safe kustomize build command are interpreted through drydock’s
native Kustomize renderer. Other plugin sources fail closed with
plugin.unsupported unless an embedding caller supplies
drydock.Config.PluginRenderer or a trusted drydock plugin policy matches the
plugin name. Exec policy requires trusted provenance and --enable-plugins;
native policy engines such as avp-compat and native-kustomize do not
execute plugin commands. Embedders can pass a renderer directly or use
drydock.NewPluginRegistry(map[string]drydock.PluginRenderer{...}) to dispatch
in-process renderers by plugin.name. The public plugin request includes the
resolved source, $ref roots and source metadata, kube version, and API
versions.
See plugin-policy.md
for --plugin-policy-path,
--plugin-policy-ref, --plugin-policy-repo, --disable-plugin-policy, the
policy schema, CMP compatibility behavior, and exec security model.
Render Tests
Test every discovered Application without printing manifest bodies:
drydock test apps --path .
Test exactly one Application:
drydock test app renovate --path .
drydock test app argocd/my-app --path .
Default text output prints one status line per selected Application:
PASS renovate
FAIL argocd/broken Application argocd/broken source[0] path="..." ...
SKIPPED argocd/skipped unsupported ApplicationSet generator ...
When test apps discovers no Applications, text output prints
No Applications discovered. and structured JSON output prints [].
Status values are PASS for Applications that rendered successfully, FAIL
for render or planning failures, and SKIPPED when discovery or expansion
preconditions prevented safe rendering. test apps and test app return exit
code 0 only when every selected Application passes. Any FAIL, SKIPPED,
or runtime failure returns exit code 2.
drydock test apps and drydock test app validate configured custom Argo CD
health Lua by default. Validation is offline and runs against rendered desired
manifests, not live Argo CD Application health aggregation. Use
--skip-lua-health to isolate render failures or benchmark render-only
behavior:
drydock test apps --path . --skip-lua-health
When test apps writes text output to a terminal, statuses stream as each
Application completes. Status labels are colored, a single stderr progress
counter updates in place when stderr is also a terminal, and a final summary is
printed after the status lines. Redirected text output stays plain and
buffered.
Structured status output is available with -o json and -o yaml:
drydock test apps --path . -o json
drydock test apps --path . -o yaml
Manifest Diffs
Diff all affected Applications between two repository trees:
drydock diff apps --path ./current --path-orig ../base
diff apps renders the baseline and current trees, then prints
desired-vs-desired manifest diffs. It uses changed-only selection by default:
if changed files can be mapped to Application inputs, only affected
Applications render; if any changed file is unowned, non-strict mode warns and
renders all Applications.
Use --changed-only=false to render all Applications explicitly, or
--strict-changed-only to fail on incomplete input ownership.
Use repeatable --changed-only-include GLOB and --changed-only-ignore GLOB
to scope the changed paths considered by changed-only selection before
Application ownership is evaluated:
drydock diff apps \
--path . \
--ref-orig main \
--changed-only-include 'apps/**' \
--changed-only-ignore 'apps/**/README.md'
Globs are repository-relative and slash-normalized. When no include globs are
provided, every changed path is considered. Ignore globs remove paths after
include filtering, so ignore wins. If filtering removes every changed path, the
diff is empty and no Applications render. --strict-changed-only applies only
to the remaining considered paths. Broad ignores can hide real render inputs in
plugin-heavy or unconventional repositories, so prefer narrow patterns owned by
the workflow.
Git Ref Diffs
Diff commands can compare local Git refs without creating a separate baseline
worktree. --ref-orig replaces --path-orig with a temporary snapshot of the
baseline ref. --ref replaces --path with a temporary snapshot of the current
ref. --repo selects the local Git repository used to resolve refs and
defaults to --path.
drydock diff apps --path . --ref-orig main
drydock diff apps --repo . --ref feature --ref-orig main
--path . --ref-orig main compares the current working tree, including tracked
uncommitted changes, against committed main. --repo . --ref feature --ref-orig main compares committed refs only. Top-level remote --repo URLs
are not supported yet; clone the repository locally and pass the local path.
Manifest diffs default to unified diff output. diff apps and diff app also
support -o markdown, -o json, and -o yaml. Markdown output is intended for
review comments: it includes a summary plus expandable per-Application rendered
manifest patches, and embeds successful diagnostics in the document. JSON and
YAML serialize the structured []diff.Result payload. Diagnostics remain on
stderr for unified, JSON, and YAML output so stdout stays parseable. -o name
is not supported for manifest diffs.
drydock diff apps --path ./current --path-orig ../base -o markdown
Unified diff output supports git-style ANSI color with
--color=auto|always|never. The default, auto, colors only when writing to a
terminal. always forces ANSI color for text diff output, while never
disables ANSI color for text diff output. Diagnostics keep their existing
stderr terminal-color behavior. Structured JSON and YAML payloads never contain
ANSI escapes.
Manifest diffs hide common Helm-rendered metadata noise by default:
metadata.labels.helm.sh/chartmetadata.labels.chartmetadata.labels.app.kubernetes.io/versionspec.template.metadata.labels.helm.sh/chartspec.template.metadata.labels.chartspec.template.metadata.labels.app.kubernetes.io/versionspec.template.metadata.annotations.checksum/*
Use --show-ignored-fields on diff apps or diff app to include these
drydock-default ignored fields again. This flag does not disable Argo CD
ignoreDifferences, compare options, or explicit --strip-attr filters.
Use repeatable --strip-attr KEY to remove matching keys from
metadata.labels and metadata.annotations before comparing rendered
manifests:
drydock diff apps \
--path-orig ../base \
--path ./current \
--strip-attr helm.sh/chart \
--strip-attr app.kubernetes.io/version
Application-level spec.ignoreDifferences[] rules and global
resource.customizations.ignoreDifferences.* settings are honored for rendered
resource diffs. Supported ignore fields are jsonPointers,
jqPathExpressions, and managedFieldsManagers. jqPathExpressions are
evaluated as Argo CD-style del(<expression>) delete filters. When a matching
resource exists on both sides, drydock applies the union of matching
Application-local and global settings from the baseline and current trees so a
new ignore rule can suppress intended PR noise immediately.
By default status is ignored for all resources. Use
ignoreResourceStatusField: none, off, or false in discovered Argo CD
compare options when rendered status fields should remain visible in PR diffs.
Rendered-resource filters run before diff comparison. Argo CD core exclusions
and discovered resource.exclusions/resource.inclusions are applied
automatically. Omit CRDs and Secrets from a pull request diff with:
drydock diff apps \
--path-orig ../base \
--path ./current \
--skip-crds \
--skip-secrets
Diff one requested Application by metadata.name:
drydock diff app renovate --path-orig ../base --path .
drydock diff app argocd/my-app --path-orig ../base --path .
diff app selects the requested Application directly in each tree and does not
use changed-only Git path filtering. If the Application exists only in current,
the diff shows additions; if it exists only in baseline, the diff shows
deletions.
For local inspection, keep the command successful even when a diff exists:
drydock diff apps \
--path-orig ../base \
--path ./current \
--exit-code=false
Image Diffs
Diff rendered image references from rendered manifests:
drydock diff images --path ./current --path-orig ../base
This projection includes PodSpec container images plus scalar manifest fields
whose key is exactly image. It does not scan arbitrary string content, Secret
manifests, top-level metadata/status, or ConfigMap data payloads. Use -o name
to print current-only added image references, one per line. Removed-only image
changes print no names but still return the diff exit code unless
--exit-code=false is set. Use -o markdown for pull request comments, or
-o json / -o yaml for machine-readable added, removed, and unchanged
image lists. Diagnostics remain on stderr so stdout stays valid JSON or YAML.
Text image diffs use the same --color=auto|always|never behavior as manifest
diffs.
diff images uses the same changed-only defaults and
--changed-only-include / --changed-only-ignore path filters as diff apps.
Diagnostics
Run repository diagnostics without printing rendered manifests:
drydock diag --path .
By default, diag uses static repository discovery, ApplicationSet expansion,
and settings metadata without rendering Applications. It prints diagnostics to
stderr and returns an error when runtime failures or error-severity diagnostics
are found. Use --strict to promote warnings to errors.
diag refuses broad roots such as the filesystem root or the current user’s
home directory. Run it from the GitOps repository root or pass --path to that
repository.
Use --render when the diagnostic report should include render-backed
diagnostics. --cache-events and --plugin-executions also opt into the
render-backed path so source-acquisition and plugin execution metadata reflect
Application rendering.
Structured diagnostic output can include a redacted settings summary:
drydock diag --path . --settings -o json
drydock diag --path . --settings -o yaml
The settings summary is CLI-only. It reports parsed resource-customization
metadata such as action names, useOpenLibs, and SHA-256 hashes for
health/action Lua. It does not print raw Lua bodies, embedded secret-looking
strings, or any live-cluster state.
When local AppProject manifests are present, build, test, diff, and the
Go API report source repository and destination validation diagnostics from
those manifests. diag --render includes the same render-backed project
diagnostics; default diag reports project and repository metadata discovered
without rendering every Application. RBAC roles and policies are parsed and
reported as metadata only; Argo CD authorization is not simulated. Repository
credential matching diagnostics use discovered repository Secret metadata only
and never read secret credential fields. Cluster Secret diagnostics likewise use
only name, server, namespaces, clusterResources, and project metadata;
credential/config fields are not decoded, retained, or printed.
argocd-cmd-params-cm settings are parsed as runtime-boundary metadata when
they imply live repo-server, controller, or ApplicationSet controller behavior.
They may produce diagnostics and settings summaries, but they do not mutate
drydock render behavior.
Local Verification And Benchmarks
Run the normal local verification suite before merging:
go test ./...
go vet ./...
golangci-lint run --allow-parallel-runners
git diff --check main..HEAD
Run render and ApplicationSet benchmarks when changing discovery, rendering, ApplicationSet expansion, cache event recording, or diagnostics on hot paths:
go test ./internal/app -run '^$' -bench 'BenchmarkOrchestrator(BuildManyLocalApplications|ExpandApplicationSetList)' -benchmem -count=1
Benchmark numbers are trend signals, not hard pass/fail thresholds.
Advanced profiling flags are available in release binaries and go run builds
for maintainers diagnosing real repository performance:
drydock --profile cpu --profile-out ./drydock-profiles test apps --path .
drydock --profile trace --profile-out ./drydock-profiles diff apps --path . --ref-orig main
drydock --profile mem --profile-out ./drydock-profiles get images --path .
--profile accepts cpu, mem, block, mutex, or trace. Profile metadata
is written to stderr, and profile artifacts are written under --profile-out.
Normal stdout output remains unchanged for text, JSON, YAML, and diff formats.
The mem mode writes a heap profile at command completion; it is not a memory
timeline.
Inspect pprof profiles with:
go tool pprof ./drydock-profiles/drydock-test-apps-*.cpu.pprof
Inspect traces with:
go tool trace ./drydock-profiles/drydock-diff-apps-*.trace.out
Profiles may include local paths, command arguments, symbols, and sampled in-memory strings. Review profile files before sharing them publicly.
Use the optional maintainer script for repeated real-repository smokes:
scripts/profile-smoke.sh ~/git/home-ops --binary ./dist/drydock --profile cpu --warm-runs 3
The script is not a CI gate. It accepts explicit --ref and --ref-orig
values, and otherwise tries to detect the baseline branch from origin/HEAD,
main, or master.
Runtime-Boundary Commands And Sources
These source paths are outside the current default runtime contract:
- Live provider API calls for cluster, clusterDecisionResource, SCM provider, pull-request, and plugin ApplicationSet generators.
- Arbitrary CLI config management plugin execution outside trusted exec policy
plus
--enable-plugins, Argo CD repo-server sidecar plugin discovery, ambient plugin configuration, ambient plugin environment loading, and plugin credential injection. - Live cluster and Argo CD API sources.
- Live destination cluster existence, sync windows, source integrity verification, project-scoped cluster Secret enforcement beyond discovered metadata, and full RBAC simulation.
See plugin-policy.md
for the supported trusted CMP
compatibility path.
See
reports/live-integration-design-gate.md
before proposing live-runtime features.
Optional Home-Ops Smoke
Run the optional home-ops Renovate smoke:
RENOVATE_CHART_TO=4.8.2 scripts/home-ops-renovate-smoke.sh
The smoke script is optional, targets maintainers with a local home-ops
checkout, uses temporary worktrees, and does not mutate the real checkout.
Committed tests and portable fixtures do not depend on home-ops.
Run the optional home-ops pattern smoke:
scripts/home-ops-pattern-smoke.sh
The pattern smoke applies small synthetic changes across representative
home-ops app patterns in temporary worktrees. It is optional, may fetch public
charts, and accepts RENOVATE_CHART_TO or EXTERNAL_SECRETS_CHART_TO when
maintainers want to choose explicit chart target versions.