Skip to main content

playcheck

CI PyPI Python ansible-core License: MIT

Readable previews for ansible-playbook --check --diff.

Ansible's check mode tells you what a playbook would change — but the raw output is an unstructured scroll, and tasks that can't be simulated (shell, command, raw, …) are silently skipped with the same skipping: line as an ordinary conditional skip. People read "no diff shown" as "no change", and that's false.

playcheck runs the check for you and reformats the result so it's readable in ten seconds:

  • Per-host grouping — web-01: 4 changes · 2 not previewable
  • Colored add/remove diffs instead of raw unified-diff walls
  • Explicit flagging of every task that could not be previewed, including tasks with check_mode: false that executed for real during the check
  • Top-line summary — hosts affected, tasks that would change, tasks that could not be simulated
$ playcheck run site.yml -i inventory.ini

web-01  4 changes · 2 not previewable
  ~ Write nginx config (copy)
      +server {
      +  listen 80;
      +}
  ! Restart nginx (shell)  NOT PREVIEWED — Command would have run if not in check mode
  ~ Write API token (copy)  [diff censored (no_log)]

SUMMARY
  hosts: 2 of 2 would change
  tasks: 5 would change (2 with hidden diffs)
  ⚠ 3 tasks were NOT simulated (module does not support check mode) — the real run may change more than shown.

Install

pipx install playcheck   # or: pip install playcheck

Requires Python ≥ 3.9 and an existing ansible-playbook on PATH (ansible-core ≥ 2.9). No other dependencies.

Usage

playcheck run <playbook> -i <inventory> [-l LIMIT] [-t TAGS] [--no-color] [--quiet]

Anything after -- is passed to ansible-playbook unchanged:

playcheck run site.yml -i prod.ini -- -e env=prod --vault-password-file .vault

playcheck adds --check --diff itself — it never applies changes. The exit code mirrors ansible-playbook's (0 on success even when changes are pending; non-zero on task failures or unreachable hosts).

For CI gating there are two opt-in exit codes, checked in this order after a clean ansible run:

flag exit code meaning
--fail-on-changes 3 at least one task would change something
--fail-on-unpreviewable 4 at least one task could not be simulated

--format markdown emits a GitHub-flavored report (collapsible per-host sections, fenced diffs) suitable for PR comments and $GITHUB_STEP_SUMMARY.

GitHub Action

Post the preview as a PR comment, terraform plan-style. The comment is updated in place on subsequent pushes instead of spamming the thread:

on: pull_request

jobs:
  preview:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - uses: Cjayy77/Playcheck@v0
        with:
          playbook: site.yml
          inventory: inventory/prod.ini
          ansible-core-version: "2.17"   # optional
          extra-args: "-l web -e env=prod"

How it works

playcheck doesn't scrape ansible's human-readable output (which is ambiguous — an unsupported-check-mode skip and a when:-conditional skip print identically). Instead it ships a tiny stdout callback plugin and invokes ansible-playbook with ANSIBLE_STDOUT_CALLBACK=playcheck_jsonl, receiving one structured JSON event per task result, including diffs, skip reasons, and check-mode metadata.

Safety notes:

  • no_log results are censored by Ansible before they reach any callback; playcheck never sees the secret.
  • diff: false tasks arrive with an empty diff; playcheck marks them [diff hidden by task setting] rather than pretending nothing changed.
  • Any skip that can't be positively attributed to a when: condition is flagged as not previewed. Over-flagging is a feature: silently missing an unsimulated task is the exact failure this tool exists to prevent.

Status

Alpha. CLI formatter and GitHub Action work; GitLab CI wrapper is planned.

Verified against ansible-core 2.15, 2.17, 2.19, and 2.21 with real --check --diff runs — the test suite replays against fresh captures from each version (scripts/version_matrix.sh, also run in CI), not hand-written fixtures. Classification never depends on exact skip-message text: any skip that can't be positively attributed to a when: conditional is flagged, which is what catches raw (skipped with no message at all) and whatever future ansible versions do differently.

Development

pip install -e . pytest
pytest

# regenerate fixtures from a real run (Linux/WSL, needs ansible-core):
ANSIBLE_STDOUT_CALLBACK=playcheck_jsonl \
ANSIBLE_CALLBACK_PLUGINS=src/playcheck/_ansible/callback_plugins \
ansible-playbook testdata/site.yml -i testdata/inventory.ini --check --diff \
  > tests/fixtures/jsonl_output.jsonl

License

MIT

Metadata

Release files for playcheck 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for playcheck 0.1.0
File Size Uploaded
playcheck-0.1.0.tar.gz 19.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for playcheck 0.1.0
File Interpreter ABI Platform
playcheck-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 37.4 kB

Release files / playcheck-0.1.0.tar.gz

Download URL playcheck-0.1.0.tar.gz
Size 19.6 kB
Tags Source
SHA-256 checksum
How to use checksums
87a4c3303ec52d64831bc253b2abcc7a1f38d01ccf41883c03391d60042e6852
BLAKE2b-256 checksum
How to use checksums
056977da6c35a68be3e43697b154f5cb1843ad5a158b684ca3f8e28b600eb4e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 3, 2026.

Transparency log

Release files / playcheck-0.1.0-py3-none-any.whl

Download URL playcheck-0.1.0-py3-none-any.whl
Size 17.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b54abcb5eccd5df2acd80e1b8c1ad55557994e2a9bcfb41c87daac2a066d71f2
BLAKE2b-256 checksum
How to use checksums
5826862e33b48983c4ac3e560f5d3684e792d944b513beb1106a9889a216c6a2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 3, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page