drydock
Inspect your Argo CD fleet without getting wet

drydock is a fast, single static Go binary and embeddable library for
runtime-offline Argo CD desired-state analysis. It discovers, renders, tests,
diffs, and diagnoses GitOps Applications without requiring a live Argo CD
instance or Kubernetes cluster.
It is built for operators who want quick, deterministic feedback before a
change reaches the cluster. Pull request diffing is a key workflow, but
the same native engine also supports render validation, image inventory,
repository diagnostics, cache inspection, and Go API embedding.
Full documentation: sholdee.github.io/drydock.
Core properties:
- Single static Go binary for local use and CI.
- Native Go renderers and public Go APIs; no repo-server wrapper.
- No default shellouts to
kubectl, argocd, Helm, Kustomize, or config
management plugin commands.
- Runtime-offline analysis: no running cluster or Argo CD server is required.
- Cache-backed source acquisition for fast repeatable validation.
Runtime-offline does not mean network-disconnected. Declared Git, HTTP Helm,
OCI Helm, and remote Kustomize sources may be fetched into explicit drydock
caches when needed. Use --offline to require local files, repo maps, local
charts, or existing cache hits only.
Install
Install the latest Linux/macOS release with Homebrew:
brew install sholdee/tap/drydock
Homebrew installs shell completions automatically.
Install Script
curl -fsSL https://raw.githubusercontent.com/sholdee/drydock/main/scripts/install-drydock.sh -o install-drydock.sh
bash install-drydock.sh --yes
The script verifies release checksums, verifies Sigstore bundles when
available, installs the drydock binary, and attempts shell completion
installation. Pin a release with --version vX.Y.Z.
Pipe form:
curl -fsSL https://raw.githubusercontent.com/sholdee/drydock/main/scripts/install-drydock.sh | bash -s -- --yes
Pinned pipe form:
curl -fsSL https://raw.githubusercontent.com/sholdee/drydock/main/scripts/install-drydock.sh | bash -s -- --version vX.Y.Z --yes
Use --no-completions when completions should be installed manually.
For GitOps repository and CI pinning, use mise with the GitHub backend:
[tools]
"github:sholdee/drydock[exe=drydock]" = "vX.Y.Z"
GitHub Actions
Workflows that install a released binary can use the setup action:
- uses: sholdee/drydock/setup-action@main
with:
version: vX.Y.Z
For pull request validation, the PR action wraps render tests, manifest diffs,
image diff reports, source caches, artifacts, and sticky PR comments:
name: drydock
on:
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: write
jobs:
drydock:
runs-on: ubuntu-latest
steps:
- uses: sholdee/drydock/pr-action@main
with:
version: vX.Y.Z
The setup action accepts latest, vX.Y.Z, or bare X.Y.Z and verifies the
selected archive with the release checksum manifest by default.
See the GitHub Actions reference
for full action inputs, GitHub App token support, cache behavior, comments,
artifacts, and outputs.
Download A Binary
Download Linux and macOS amd64 or arm64 archives from the
latest release. Verify
the archive with checksums.txt before installing the drydock binary.
Docker / GHCR
Release containers are published to GHCR for Linux amd64 and arm64:
docker run --rm -v "$PWD:/workspace:ro" ghcr.io/sholdee/drydock:latest test apps --path /workspace
For repeatable automation, pin ghcr.io/sholdee/drydock:vX.Y.Z.
Go Install
Build from source with Go:
go install github.com/sholdee/drydock/cmd/drydock@latest
Manual binary installs can generate shell completions with:
drydock completion zsh
drydock completion bash
drydock completion fish
Quick Start
Run drydock from the root of an Argo CD GitOps repository.
Test every discovered Application without printing rendered manifests:
drydock test apps --path .
Example text output:
PASS renovate
PASS cert-manager
FAIL argocd/broken Application argocd/broken source[0] path="..." ...
Compare a pull request checkout against a baseline tree:
git worktree add ../baseline main
drydock diff apps --path . --path-orig ../baseline
Diff commands use changed-only selection by default. Use
--changed-only=false when you want to render and compare every discovered
Application. Use repeatable --changed-only-include and
--changed-only-ignore globs when CI should ignore known non-GitOps paths
before changed-only ownership is evaluated.
You can also compare against committed Git refs without creating a baseline
worktree:
drydock diff apps --path . --ref-orig main
drydock diff apps --repo . --ref feature --ref-orig main
Inspect image changes in a machine-readable form:
drydock diff images --path . --path-orig ../baseline -o json
For CI jobs that have already populated drydock's source caches, require a
cache-only run:
drydock test apps --path . --offline
drydock diff apps --path . --path-orig ../baseline --offline
Common Workflows
| Goal |
Command |
| List Applications |
drydock get apps --path . |
| List rendered image references |
drydock get images --path . -o name |
| Render all Applications |
drydock build apps --path . |
| Render one Application |
drydock build app renovate --path . |
| Test renderability |
drydock test apps --path . |
| Diff rendered manifests |
drydock diff apps --path . --path-orig ../baseline |
| Diff one Application |
drydock diff app renovate --path . --path-orig ../baseline |
| Diff rendered image references |
drydock diff images --path . --path-orig ../baseline -o json |
| Inspect repository diagnostics |
drydock diag --path . |
| Inspect redacted settings |
drydock diag --path . --settings -o json |
| Inspect cache roots |
drydock cache path |
| List cache entries |
drydock cache list -o json |
drydock <command> --help lists command-specific flags. See
the CLI usage guide for the
full command reference.
What It Supports
drydock discovers and renders local Argo CD desired state, including:
- Static
Application resources, supported ApplicationSet generators, and
rendered child Application, ApplicationSet, AppProject, and settings
objects from app-of-apps/bootstrap sources.
- Optional additional rendered discovery from explicit local Kustomize
entrypoints with
--discover-kustomize.
- Single-source and multi-source Applications.
- Directory, Kustomize, local Helm chart, remote Helm chart, and remote
Kustomize sources.
- Discovered safe Kustomize build config management plugins rendered through
drydock's native Kustomize adapter, without executing plugin commands.
- Trusted plugin policy entries for native
avp-compat placeholder redaction
and native plugin overrides.
- Explicit trusted exec plugin policy support with
--enable-plugins for
operators who need shellout CMP compatibility, including policy-defined
post-renderer chains.
- Declared Git, HTTP Helm, OCI Helm, and remote Kustomize source acquisition
into local caches.
- Fast cache-backed repeated runs for local development and CI.
- Repository maps with
--repo-map URL=PATH for adjacent local checkouts.
- Changed-only desired-vs-desired PR diffs, with strict diagnostics available
when a safe ownership decision cannot be made.
- Default diff noise filtering for common Helm chart/version labels and
pod-template checksum annotations, with
--show-ignored-fields when those
fields need inspection.
- Argo CD diff customizations such as
ignoreDifferences,
knownTypeFields, selected compare options, and resource filters.
- Per-Application render test status as
PASS, FAIL, or SKIPPED, including
structured JSON and YAML output.
- Offline validation of configured custom health Lua during render tests.
- Redacted diagnostics for settings, source repositories, AppProjects, and
cache acquisition events.
- Cache lifecycle commands for Git, chart, and remote Kustomize caches.
See the compatibility notes
for the detailed Argo CD support matrix. See
ApplicationSets for
generator details and
source acquisition
for remote source, cache, and auth behavior.
Offline Runtime Model
drydock is desired-vs-desired analysis. It renders the desired Kubernetes
manifests from a current tree and, for diff commands, a baseline tree. It does
not ask a live cluster or Argo CD server what is currently running. Network
source acquisition, when enabled, is limited to populating explicit drydock
caches for declared repository, chart, and remote Kustomize inputs.
Default commands do not reproduce:
- Kubernetes API defaulting or admission mutation.
- Argo CD server-side diff.
- Live Argo CD Application health aggregation.
- Live-only managed-field ownership.
- Full Argo CD RBAC authorization.
- CLI config management plugin execution or shellout plugin adapters unless an
explicit trusted exec plugin policy is enabled.
Plugin command execution fails closed unless an embedding caller injects an
in-process renderer or a trusted drydock exec plugin policy matches the plugin
name. Discovered safe Kustomize build CMP definitions and native policy
engines do not execute plugin commands. Exec policy requires trusted
provenance and --enable-plugins. See
the plugin policy guide
for the policy schema, provenance rules, CMP compatibility model, and exec
security controls.
These behaviors are not silently approximated. The no-live-runtime boundary is
an intentional product decision so the default cache-backed workflow stays
deterministic and safe for CI.
Structured outputs keep stdout machine-parseable. Diagnostics and failure
summaries are written to stderr where appropriate, and drydock avoids printing
Secret values, repository credentials, tokens, SSH private keys, passphrases,
registry credentials, or credential-bearing URLs. Repository and cluster Secret
diagnostics use non-sensitive metadata only, and argocd-cmd-params-cm is
reported as runtime-boundary metadata rather than a render-behavior override.
How It Works
flowchart TD
current[Current tree]
baseline[Baseline tree]
current --> discover
baseline --> discover
discover[Discover static and rendered Argo CD fleet objects]
discover --> plan[Plan sources, resolve repo maps, use caches]
plan --> render[Render desired manifests with Go libraries]
render --> normalize[Apply Argo-aware filters and diff normalization]
normalize --> outputs[Test statuses, manifest diffs, image diffs, diagnostics]
The render path imports Argo CD API types and selected reusable helpers, but
drydock owns offline orchestration. See the
design notes for the
architecture and behavior model.
Go API
Embedding callers can use github.com/sholdee/drydock/pkg/drydock to list,
render, and diff Applications without shelling out:
result, err := drydock.Render(ctx, drydock.Config{Path: "."})
drydock.NewClient accepts public Git, chart, and remote-resource acquirer
interfaces, plus a public config management plugin renderer hook. Embedders can
use those interfaces for tests, offline fixtures, and custom source handling.
When one selected Application fails, the result still includes successful
manifests, diagnostics, and per-Application statuses from the partial build.
drydock is independently implemented, but its offline GitOps desired-state workflow
was inspired by home-operations/flate
and the home-operations community.
Join the home-operations Discord at https://discord.gg/home-operations.
Documentation
- Documentation site: curated operator
docs and full reference pages.
- Getting started: first
local render test and comparison commands.
- GitHub Actions:
setup action, PR action, comments, artifacts, and caches.
- CLI usage: command
workflows, outputs, diagnostics, and diff flags.
- Compatibility: supported
Argo CD behavior and intentional runtime boundaries.
- Plugin policy: trusted
policy engines, schema, CMP compatibility, and exec security.
- Source acquisition:
Git, Helm, remote Kustomize, cache, and auth behavior.
- Release notes: release
and Argo CD dependency upgrade notes.
License
Apache-2.0. See LICENSE.