Skip to Content
Atlassian Jiraajatv1.0.xDiff & Drift

Compare Snapshots

ajat diff compares two on-disk rule trees produced by ajat export. It is purely local — no Jira credentials, no API call, no network — which makes it cheap to run in CI, on an air-gapped review machine, or against an archived snapshot you no longer have credentials for.

The pairing is the point: an export taken yesterday, plus an export taken today, plus ajat diff, is an auditable change history for your automation estate — without storing any additional state.

diff reads two directories that already exist on disk. Produce them first with ajat export; keep timestamped output directories so you always have a “before” and an “after” to compare.

Usage

ajat diff <left-dir> <right-dir> [--format table|json] [--check]
  • <left-dir> is the “before” snapshot.
  • <right-dir> is the “after” snapshot.
  • --format table (default) renders a human-readable table on stdout.
  • --format json emits a single JSON object — script-friendly.
  • --check returns a non-zero exit code when any difference is detected — the CI drift gate.

diff is a read-only, credential-free command. It never contacts Jira and never mutates anything — it only reads the JSON files in the two directories.

Change buckets

Three categories are surfaced:

BucketMeaning
addedUUIDs present in <right-dir> only.
removedUUIDs present in <left-dir> only.
changedUUIDs present in both with at least one watched-field delta.

Watched fields

A rule lands in the changed bucket when any of these fields differ between the two snapshots:

  • name
  • state (ENABLED ↔ DISABLED)
  • description
  • actor (actor.accountId)
  • trigger (the rule’s trigger component type)
  • labels (order-insensitive set diff)
  • scope (the ruleScopeARIs set; an empty list is rendered as global)
  • conditions, actions, branches — by component count

Deeper structural changes — the schema of one specific component, for example — do not produce a per-field delta. If you need that level of detail, the rule JSON files on disk are the source of truth; diff them with your tool of choice.

Table and JSON output

--format table (the default) prints a readable summary on stdout, grouped by bucket:

ajat diff ./snapshots/2026-05-18 ./snapshots/2026-05-19

--format json emits a single JSON object with added, removed, and changed arrays — ideal for scripting and dashboards:

# List the names of every rule that changed between two snapshots ajat diff ./baseline ./current --format json | jq '.changed[] | .name'

Because the diff is fully local, you can run it as often as you like without touching the Automation API rate budget.

Examples

# What changed in the last 24 hours? ajat export --output-dir ./snapshots/2026-05-19 ajat diff ./snapshots/2026-05-18 ./snapshots/2026-05-19 # Treat the diff as a CI gate. ajat diff ./baseline ./current --check # Machine-readable output. ajat diff ./baseline ./current --format json | jq '.changed[] | .name' # What did the import job actually apply? ajat export --output-dir ./pre-import-snapshot ajat import --input-dir ./source-snapshot --yes ajat export --output-dir ./post-import-snapshot ajat diff ./pre-import-snapshot ./post-import-snapshot

Exit codes

CodeMeaning
0The run completed; --check was off, or --check found no differences.
1Bootstrap failure (unreadable directory, bad --format), or --check detected at least one diff.
130Interrupted (SIGINT / SIGTERM).

CI drift gate

--check turns diff into a governance guardrail: keep a committed baseline snapshot, export the current estate on every run, and fail the pipeline when the two diverge. This catches automation added, removed, or edited outside your change process.

# .github/workflows/automation-drift-gate.yaml on: schedule: [{ cron: "0 6 * * 1" }] # Monday 06:00 UTC workflow_dispatch: jobs: drift-gate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 # repo holds ./baseline from the last approved review - run: | # Download URL comes from your Climakers order email; store it as a CI secret. curl -sL "${{ secrets.AJAT_DOWNLOAD_URL }}" -o ajat.zip unzip ajat.zip && chmod +x ajat && sudo mv ajat /usr/local/bin/ - run: ajat export --output-dir ./current --no-progress env: AJAT_JIRA_DOMAIN: ${{ secrets.AJAT_JIRA_DOMAIN }} AJAT_JIRA_USERNAME: ${{ secrets.AJAT_JIRA_USERNAME }} AJAT_JIRA_API_TOKEN: ${{ secrets.AJAT_JIRA_API_TOKEN }} # Fails the job (exit 1) the moment the live estate drifts from the baseline. - run: ajat diff ./baseline ./current --check

When the gate fires, re-run the same command without --check (or with --format json) to see exactly what drifted, then either investigate the change or promote ./current to the new approved baseline. Use the CI/CD Automation license edition for scheduled runs — see Licensing.

Pairing with the state file

ajat export writes .ajat_state.json next to each export directory. The incremental state already answers “which rules changed since the last successful export” — and ajat diff answers “what changed about them” by comparing the on-disk JSON. Keeping both around makes drift questions answerable without re-running export.

  • Export the Estate — produces the on-disk tree that diff consumes.
  • HTML Reportsreport consistency analyzes a single snapshot for label hygiene and naming drift; complementary to two-snapshot diff.
  • Search the Estate — query live, summary-level rule state when you do not need full bodies or a second snapshot.
  • Import and Restore — export, import, then diff to confirm exactly what an import applied.
Last updated on