diff-tree
Compare the current desired tree with the published destination revision:
nyl diff-tree --target production --against publishednyl diff-tree --target production --against published --refreshEvery indexed file of the published target prefix must match its recorded
digest. A prefix without an ownership index owns nothing yet, for example when
another tool has committed fromPublication state before the first
publication. It may then hold only files the target declares as
fromPublication state paths; any other file fails the comparison, which
usually means pathPrefix names a directory that belongs to something else.
Unindexed repository content is never treated as target-owned baseline data. --offline reads fromPublication state at the
cached branch head, as for render-tree, and compares against the published
tree at that same commit, or at the cached publication branch head, without
contacting the publication repository. With --against source, --source-ref
resolves against the cached refs too. Helm charts from Git come from the
vendor lock or the local source cache when they are there; only a chart in
neither, or --refresh, fetches.
Compare with a render from a source revision:
nyl diff-tree \ --target production \ --against source \ --source-ref origin/mainThe baseline checkout may hold the project somewhere else, for example while a
pull request moves nyl.toml into nyl/. Nyl looks for it in this order and
skips every candidate that holds no nyl.toml:
- the directory that matches the current project;
- each
--source-project-path <PATH>, relative to the worktree root with an optional leading/(repeatable); - each entry of
[project] previous_pathsin the currentnyl.toml.
Nyl never guesses another location, so in a repository with several projects a new project is never compared against a different one; when none of these holds a project, the command fails and lists every path it tried.
Because missing candidates are skipped, record a move in previous_paths in
the commit that performs it: the pull request’s own diff finds the old
location, and after the merge the entry no longer matters. A
--source-project-path left in a CI job is equally harmless.
When the baseline’s target publishes to a different repository, branch, or
path prefix, its fromPublication inputs read that publication, not the
desired one, and the report carries a publication_moved warning. Nyl does not
move state or clean up the old prefix; follow
Move a publication.
The command identifies the desired source commit and exact baseline repository,
revision, resolved commit, project directory, path, selected view, and output
destination on stderr. By default, stdout contains only multi-file unified diff bytes; a
comparison with no differences produces no stdout. Use --output to write the same bytes
atomically to a file instead:
nyl diff-tree --target production --output rendered.diffnyl diff-tree --target production --output rendered.diff --fail-on-diffA successful comparison with no differences creates an empty output file.
Rendering or comparison errors leave existing output files untouched.
--fail-on-diff writes the complete diff and all requested reports before
returning a non-zero status when changes exist.
YAML files (.yaml and .yml) are parsed and serialized on both sides before
comparison. Mapping keys are sorted recursively; comments (including provenance),
quoting, indentation, and empty or null documents do not produce differences.
Document order, list order, scalar types, and string contents remain significant.
Embedded configuration strings are compared as text, with multiline values shown
as literal blocks when they can preserve the contents safely.
The patch, statistics, and --fail-on-diff all use this normalized comparison.
Other files are compared as raw bytes. If either side of a YAML file cannot be
normalized, both sides of that file are compared as raw bytes as well.
Use --raw to include formatting and comment changes and produce a text patch
applicable to the original files. Normalized patches describe normalized content;
their line numbers and blob hashes refer to that content, not the stored files.
nyl diff-tree --target production --raw --output rendered.diffStatistics and report exports
Section titled “Statistics and report exports”The combined report includes validation results, comparison context, file and line counts, and render/cache/source statistics. Validation checks the complete desired target; diff counts respect the selected catalog or Application view. The report does not attribute validation failures to the PR’s changes.
--stats-files includes a path-sorted table with each file’s status and
added/removed line counts in human-readable reports:
nyl diff-tree --target production --stats-filesLine counts use the same line comparisons as the patch, excluding patch headers and context. Replacements count as deletions plus insertions. Comments count as ordinary lines in raw comparisons. Counts respect the selected catalog or Application view. Empty-file additions and deletions count as file changes with zero changed lines. Invalid UTF-8 or NUL-containing files are binary: their changes produce patch notices, and their line counts are unavailable. Aggregate line counts cover text changes only. File moves count as additions and deletions.
Use repeatable --stats-output FORMAT:PATH options to export text, markdown,
and/or json reports independently of the patch. A report sent to stdout
(PATH=-) suppresses the automatic stderr report. File exports retain the
stderr report unless --no-stats-stderr is set. Validation findings appear once
in the combined report. Progress and errors remain on stderr; --progress off
disables render progress. Formatter preferences are command-line options only.
For example, prepare a Markdown PR comment and JSON for subsequent processing:
nyl diff-tree --target production \ --output artifacts/rendered.diff \ --stats-files \ --stats-output markdown:artifacts/comment.md \ --stats-output json:artifacts/stats.json \ --no-stats-stderrNyl writes these artifacts locally. Post the Markdown as a sticky PR/MR comment
with nyl comment upsert:
nyl comment upsert --key gitops/production --body-file artifacts/comment.mdValidation failures do not prevent comparison or report exports. Nyl attempts validation and comparison independently after desired rendering succeeds, writes available artifacts, and then returns a failing status. Apply and publication remain blocked by unsuccessful validation.
Discovery, rendering, schema/validator, and baseline/comparison errors appear in reports with explicit unavailable or not-run results. Invalid resources do not make a completed validation run incomplete. Disabled validation is reported as not run; skipped and unchecked resource counts remain visible. All-skipped or empty validation says “No resources validated.”
Add --fail-on-diff if the job should also fail when changes exist; artifacts are
written before returning that failure. Separate --validation-output text:PATH
and json:PATH exports remain available. diff-tree suppresses the standalone
validation stream automatically, so --no-validation-stderr is unnecessary.
--no-stats-stderr suppresses the combined terminal report, including validation
findings, without changing exported reports or validation’s exit status.
Markdown PR/MR comments
Section titled “Markdown PR/MR comments”The title identifies the deployment check, target, and outcome, for example “Nyl deployment check · prod-eu-1 — validation failed.” The summary leads with invalid and valid counts; zero counts are omitted. Disabled or incomplete validation remains explicit. Source and baseline revisions and dirty state are visible. Scope information appears beside the summary when validation and diff scopes differ, and is always available in the comparison context.
Validation findings use one bullet per failing resource: kind and monospace namespace/name, failing field and message, then the source file and document. Multiple findings use nested bullets with one shared source location. The first two resources are visible; additional resources share a single collapsed list. A separate “Validation details” disclosure groups rendered locations, API versions, destinations, validators, expansion traces, and readable schema origins by resource. Full schema digests remain in JSON.
Changed-file tables are collapsed when they contain more than two entries.
--stats-patch adds an optional collapsed unified-patch preview independently of
--stats-files. Full comparison metadata and render statistics are collapsed
beneath the findings and diff sections. Render statistics use an aligned text
code block inside their disclosure. The complete patch is written to
--output; the Markdown preview is not a substitute for that artifact.
Markdown is limited to 60,000 UTF-8 bytes, including markup and omission notices, leaving room for the sticky-comment marker. Outcomes and totals have priority, followed by operational errors and validation findings. File tables use at most 10,000 bytes and patch previews at most 20,000 bytes; validation can consume their available space. Extended provenance, context, and render statistics use remaining space. These limits apply to Markdown only.
Tables truncate at complete rows. Patch previews retain complete hunks with file headers, or complete binary/metadata-only entries. Oversized hunks are omitted. Notices count displayed versus total entries and identify omitted findings. Displayed metadata and diagnostic values are bounded to 2,048 UTF-8 bytes, including escaping, with explicit truncation markers. Text, JSON, and standalone patches retain complete evidence.
--stats-artifacts-url URL links the report to a CI artifacts page. Supply an
absolute HTTP(S) URL without credentials, at most 2,048 bytes. Nyl does not fetch
it or infer the CI provider. Without this option, the report names requested
output files; local paths are not artifact download links.
Use a fresh artifact directory for each invocation. This example preserves the diff command’s status, posts its report when available, and also fails if posting fails:
artifacts=$(mktemp -d)status=0nyl diff-tree --target production \ --output "$artifacts/rendered.diff" \ --stats-files --stats-patch \ --stats-output "markdown:$artifacts/comment.md" \ --stats-output "json:$artifacts/report.json" \ --no-stats-stderr || status=$?
comment_status=0if [ -f "$artifacts/comment.md" ]; then nyl comment upsert --key gitops/production \ --body-file "$artifacts/comment.md" || comment_status=$?fiif [ "$status" -ne 0 ]; then exit "$status"; fiexit "$comment_status"Configure the CI runner to upload the complete report and patch artifacts. Reports are attempted after CLI parsing and output-path preflight. Invalid CLI arguments, unsafe/conflicting output paths, process termination, and unwritable destinations cannot guarantee a report. A failed comparison does not write a patch; use its explicit report state rather than treating an absent or stale patch as an empty diff.
Paths resolve against the invocation directory. PATH=- selects stdout, so
redirect the diff to a file when exporting a report to stdout:
nyl diff-tree --output rendered.diff --stats-output json:-On Unix, /dev/null discards an output. For example, pipe the Markdown report
into VS Code while discarding the patch:
nyl diff-tree --stats-output markdown:- --output /dev/null | code -Multiple outputs may use /dev/null, but cannot share stdout or the same regular
file. Nyl creates missing parent directories and atomically replaces each output
file. A write failure
returns an error, and Nyl continues attempting independent exports. Multiple
output files are not a single transaction, so files already written remain
available. Delivery failures appear on stderr and in the exit status; report
contents describe evaluation and cannot certify their own successful delivery.
A successful zero-change comparison writes an empty patch and a complete report with zero counts and an empty file list.
Text reports on stderr follow --color auto|always|never: automatic color on a
TTY or in CI, respecting NO_COLOR, CLICOLOR, CLICOLOR_FORCE, and TERM.
Added lines are green and removed lines red. Text file exports are plain in
auto mode; --color always retains ANSI styling. Markdown and JSON never contain
formatter ANSI sequences.
JSON report contract
Section titled “JSON report contract”JSON always includes every changed file, independently of --stats-files. The
report has schema_version: 2 and these top-level fields:
request: invocationpath, nullable explicittarget,against(publishedorsource), nullablesource_ref, and nullable sanitizedsource_repository.stages:discovery,render(desired rendering), andcomparison, eachcompleted,failed, ornot_run.comparison:mode(normalizedorraw),target,selection,desired_source,baseline,desired_publication, anddiff_output(-means stdout).diff:has_changes,files_changed,files_added,files_modified,files_deleted,binary_files,lines_added,lines_removed, andfiles. It isnullwhen comparison is unavailable. Each file haspath,status(added,modified, ordeleted),binary,lines_added, andlines_removed. Binary line counts arenull.render:layers,target_reuse,release_helm_renders_avoided, andsources. Statistics aggregate the complete invocation, including both renders for a source comparison. It isnullif collection never initialized and may contain partial statistics after a failure.validation:status(valid,invalid,error,disabled, ornot_run), nullable explanatoryreason,scope: "desired_target", and nullablereport.reportembeds the complete version-one validation JSON:version,status,complete,summary,destinations,resources, andoperationErrors. This nested contract uses camelCase. Each resource retains its validator, destination, identity, status, findings, rendered location, provenance, and schema origin, including valid/skipped resources.invalidmeans resource violations;errormeans validation errors or incomplete evidence.disabledmeans project/invocation policy did not enable validation;not_runmeans desired rendering did not complete. A setup error can producestatus: "error"withreport: null.errors: operational failures withstage(discovery,render,validation, orcomparison) and descriptivemessage. Resource findings belong in the nested validation report.fail_on_diff: whether differences are configured to fail the invocation.diff_policy_failed: true only when a completed comparison found differences andfail_on_diffis true.
selection.mode is tree, catalog, or applications; the last includes an
applications array of explicit selectors, empty for all workload Applications.
comparison.target, desired_source, baseline, and desired_publication are
nullable until resolved. Unavailable results never use zero counts or fabricated
commits. A completed zero-change comparison has a non-null diff with zero
counts and an empty file list.
desired_source contains nullable repository and commit, plus dirty.
baseline.mode is published or source; both include the resolved commit
and a publication. A source baseline also includes repository and revision.
Publication objects contain cluster, sanitized repository, nullable
publish_url, revision, and path_prefix (empty means repository root).
JSON contains comparison metadata, counts, and complete validation evidence; it
does not embed the unified patch. Markdown preview flags and truncation do not
change JSON data. Validation messages can contain resource values.
Render layers keys are target, release, and helm; each entry has outcomes
and bypass_reasons count maps. Outcomes are hit, miss, invalidated,
bypassed, refreshed, stored, and corrupt. target_reuse is nullable; when
present it contains releases and helm_renders avoided by target reuse.
release_helm_renders_avoided counts Helm renders avoided by release reuse.
sources is a count map with snake_case operation keys: remote_manifest_download,
remote_manifest_reuse, helm_chart_pull, helm_chart_reuse, git_source_reuse,
vendor_artifact_reuse, git_repository_clone, git_repository_reuse,
git_ref_refresh, git_worktree_create, and git_worktree_reuse. Absent count-map
entries mean zero. Bypass-reason strings are descriptive, not stable identifiers.
Consumers should ignore additional fields; incompatible changes require a new
schema version.
Selecting files
Section titled “Selecting files”Limit the comparison to the generated catalog, all workload Applications, or specific Argo CD Application identities:
nyl diff-tree --target production --catalognyl diff-tree --target production --applicationsnyl diff-tree --target production \ --application argocd/rise \ --application argocd/lokiAn Application view contains the generated Application manifest and its plain
directory payload. Nyl derives these views from the generated Applications, so
custom ApplicationGroup.spec.outputPath values remain supported without
additional ownership-index metadata. The parent catalog Application is omitted
from --applications but can be selected explicitly. --catalog conflicts
with the Application filters.
Source-derived whole-tree diffs also compare the cluster, repository, revision,
and path prefix through a synthetic _nyl/publication.json diff entry. Scoped
views leave those coordinates in the comparison report. Mutable comparison refs
must refresh successfully; cached refs are not accepted as current state.
See the options shared by the tree commands for target selection, caching, secret inputs, progress, and validation.