testguard-cli 0.10.1__tar.gz → 0.10.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- testguard_cli-0.10.2/.github/scripts/publish-homebrew-tap.sh +68 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/workflows/release.yml +25 -13
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/workflows/scheduled-release.yml +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/AGENTS.md +4 -2
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/CHANGELOG.md +24 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/CONTRIBUTING.md +7 -3
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/PKG-INFO +5 -5
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/README.md +4 -4
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/SECURITY.md +6 -4
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/action.yml +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/docs/testguard-explained.html +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/docs/testguard-explained.pdf +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/docs-canonical/ARCHITECTURE.md +5 -5
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/docs-canonical/SECURITY.md +14 -5
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/docs-canonical/TEST-SPEC.md +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/package-lock.json +2 -2
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/package.json +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/packaging/homebrew/testguard.rb +2 -2
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/pyproject.toml +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/release-sync.test.mjs +74 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/testguard.claims.json +76 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/testguard.cost-budget.json +19 -14
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.agents/skills/testguard/SKILL.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.codex/hooks.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.docguard.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.docguardignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.gitattributes +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/dependabot.yml +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/scripts/approve-held-runs.sh +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/scripts/build-one-pager.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/scripts/check-cost-budget.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/scripts/check-python-fixture.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/scripts/install-smoke.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/scripts/sync-release-version.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/workflows/auto-merge.yml +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/workflows/ci.yml +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.github/workflows/supply-chain.yml +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.gitignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.npmignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.npmrc +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.pre-commit-config.yaml +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/.pre-commit-hooks.yaml +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/CLAUDE.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/CODE_OF_CONDUCT.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/DRIFT-LOG.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/LICENSE +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/PRIVACY.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/SUPPORT.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/bench/README.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/cli/testguard.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/docs-canonical/DATA-MODEL.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/docs-canonical/ENVIRONMENT.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/docs-canonical/REQUIREMENTS.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer/.gitignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer/README.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer/expected.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer/package.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer/src/export.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer/src/redact.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer/test/flaky.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer/test/redact.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer/testguard.claims.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer/vitest.config.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-jest/.gitignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-jest/README.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-jest/expected.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-jest/jest.config.js +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-jest/package.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-jest/src/export.js +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-jest/src/redact.js +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-jest/test/redact.test.js +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-jest/testguard.claims.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/.gitignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/README.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/expected.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/package-lock.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/package.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/test-results/.last-run.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/.gitignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/README.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/demo/__init__.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/demo/export.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/demo/orphan.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/demo/pipeline.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/demo/redact.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/expected.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/testguard.claims.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/tests/test_patched.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/fixtures/known-answer-python/tests/test_redact.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/GATE-SEMANTICS.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/README.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/baseline.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/brief.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/calibration-websec.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/calibration.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/claims-methods.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/claims.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/evidence-negative-control.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/evidence-provisional.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/evidence-python.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/evidence-scan-method.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/evidence.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/gate.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/ignore.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/replay.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/examples/status.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/baseline.zero-count.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.absolute-path.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.noop-fault.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/ignore.short-reason.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/status.claim-without-file.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/conformance/schemas.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/lib/fingerprint.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/lib/validate.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/lib/wilson.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/schemas/baseline.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/schemas/brief.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/schemas/calibration.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/schemas/claims.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/schemas/common.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/schemas/evidence.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/schemas/gate.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/schemas/ignore.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/schemas/replay.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/spec/schemas/status.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/admit/admit.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/baseline/baseline.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/brief/brief.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/claims/anchors.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/claims/annotations.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/claims/load.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/claims/removed.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/cli.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/commands/admit.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/commands/baseline.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/commands/brief.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/commands/claims.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/commands/gate.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/commands/init.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/commands/mcp.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/commands/probe.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/commands/replay.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/commands/scaffold.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/commands/status.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/evidence/writer.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/gate/changed.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/git.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/init/init.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/init/templates/SKILL.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/mcp/server.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/mcp/tools.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/attribution.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/classify.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/contention.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/cost.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/discover.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/independence.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/inject.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/mocks.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/probe.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/progress.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/pyimports.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/rank.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/runners/index.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/runners/jest.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/runners/playwright.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/runners/python/_testguard_report.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/runners/python.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/runners/shared.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/runners/vitest.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/probe/worktree.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/render.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/replay/label.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/replay/replay.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/scaffold/producers.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/scaffold/producers.python.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/scaffold/scaffold.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/status/status.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/status/surface.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/util/glob.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/src/util/hash.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/admit.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/annotations.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/attribution.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/baseline.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/brief.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/calibration.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/claim-selection.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/claims-anchors.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/claims-since.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/classify.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/contention.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/cost.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/discover.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/gate.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/gitlab-template.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/glob.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/independence.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/init.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/inject.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/label.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/mcp.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/mocks.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/negative-control.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/one-pager.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/probe-error.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/probe-preconditions.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/probe.fixture.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/probe.jest.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/probe.playwright.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/probe.python.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/progress.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/pyimports.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/rank-aliases.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/release-approval.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/replay-verdict.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/replay.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/runner-argv.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/runner-command.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/runner-playwright.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/runner-python.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/runner-vitest.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/samples/playwright-report.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/samples/python-report-collect-error.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/samples/python-report.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/scaffold.python.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/scaffold.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/snapshot.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/status.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/surface.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/vitest-config.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/worktree.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/test/writer.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/testguard.ignore.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/testguard_cli/__init__.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/testguard_cli/wrapper.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.2}/vitest.config.mjs +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
fail() {
|
|
6
|
+
echo "::error::$*" >&2
|
|
7
|
+
exit 1
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
VERSION="${1:-}"
|
|
11
|
+
[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || fail "usage: publish-homebrew-tap.sh <stable-semver>"
|
|
12
|
+
|
|
13
|
+
SOURCE_FORMULA="${HOMEBREW_TAP_SOURCE:-packaging/homebrew/testguard.rb}"
|
|
14
|
+
TAP_REMOTE="${HOMEBREW_TAP_REMOTE:-git@github.com:raccioly/homebrew-tap.git}"
|
|
15
|
+
[[ -f "$SOURCE_FORMULA" ]] || fail "source formula not found: $SOURCE_FORMULA"
|
|
16
|
+
|
|
17
|
+
FORMULA_VERSION=$(sed -nE 's#^[[:space:]]*url ".*testguard-cli-([0-9]+\.[0-9]+\.[0-9]+)\.tgz"#\1#p' "$SOURCE_FORMULA")
|
|
18
|
+
FORMULA_SHA=$(sed -nE 's/^[[:space:]]*sha256 "([0-9a-f]+)"/\1/p' "$SOURCE_FORMULA")
|
|
19
|
+
[[ "$FORMULA_VERSION" = "$VERSION" ]] || fail "formula version $FORMULA_VERSION does not match release $VERSION"
|
|
20
|
+
[[ "$FORMULA_SHA" =~ ^[0-9a-f]{64}$ ]] || fail "formula sha256 is not 64 lowercase hex characters"
|
|
21
|
+
|
|
22
|
+
TEMP_ROOT="${RUNNER_TEMP:-${TMPDIR:-/tmp}}"
|
|
23
|
+
[[ -d "$TEMP_ROOT" ]] || fail "temporary root does not exist: $TEMP_ROOT"
|
|
24
|
+
WORK_DIR=$(mktemp -d "$TEMP_ROOT/testguard-homebrew-tap.XXXXXX")
|
|
25
|
+
|
|
26
|
+
cleanup() {
|
|
27
|
+
case "$WORK_DIR" in
|
|
28
|
+
"$TEMP_ROOT"/testguard-homebrew-tap.*) rm -rf -- "$WORK_DIR" ;;
|
|
29
|
+
*) echo "::warning::refusing to remove unexpected temporary path: $WORK_DIR" >&2 ;;
|
|
30
|
+
esac
|
|
31
|
+
}
|
|
32
|
+
trap cleanup EXIT
|
|
33
|
+
|
|
34
|
+
if [[ "$TAP_REMOTE" = git@github.com:* ]]; then
|
|
35
|
+
[[ -n "${HOMEBREW_TAP_DEPLOY_KEY:-}" ]] || fail "HOMEBREW_TAP_DEPLOY_KEY is required for the GitHub tap"
|
|
36
|
+
command -v gh >/dev/null || fail "gh is required to verify GitHub's SSH host keys"
|
|
37
|
+
KEY_FILE="$WORK_DIR/deploy_key"
|
|
38
|
+
KNOWN_HOSTS="$WORK_DIR/known_hosts"
|
|
39
|
+
umask 077
|
|
40
|
+
printf '%s\n' "$HOMEBREW_TAP_DEPLOY_KEY" > "$KEY_FILE"
|
|
41
|
+
gh api meta --jq '.ssh_keys[] | "github.com " + .' > "$KNOWN_HOSTS"
|
|
42
|
+
[[ -s "$KNOWN_HOSTS" ]] || fail "GitHub API returned no SSH host keys"
|
|
43
|
+
export GIT_SSH_COMMAND="ssh -i \"$KEY_FILE\" -o IdentitiesOnly=yes -o UserKnownHostsFile=\"$KNOWN_HOSTS\" -o StrictHostKeyChecking=yes"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
TAP_DIR="$WORK_DIR/tap"
|
|
47
|
+
git clone --quiet --depth 1 "$TAP_REMOTE" "$TAP_DIR"
|
|
48
|
+
TAP_FORMULA="$TAP_DIR/Formula/testguard.rb"
|
|
49
|
+
[[ -f "$TAP_FORMULA" ]] || fail "tap is missing Formula/testguard.rb"
|
|
50
|
+
|
|
51
|
+
if cmp -s "$SOURCE_FORMULA" "$TAP_FORMULA"; then
|
|
52
|
+
echo "✅ raccioly/tap already carries TestGuard v$VERSION ($FORMULA_SHA)"
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
install -m 0644 "$SOURCE_FORMULA" "$TAP_FORMULA"
|
|
57
|
+
ruby -c "$TAP_FORMULA" >/dev/null
|
|
58
|
+
git -C "$TAP_DIR" diff --check
|
|
59
|
+
git -C "$TAP_DIR" config user.name "github-actions[bot]"
|
|
60
|
+
git -C "$TAP_DIR" config user.email "github-actions[bot]@users.noreply.github.com"
|
|
61
|
+
git -C "$TAP_DIR" add Formula/testguard.rb
|
|
62
|
+
git -C "$TAP_DIR" commit --quiet -m "testguard $VERSION" -m "Published from raccioly/testguard's release workflow."
|
|
63
|
+
git -C "$TAP_DIR" push --quiet origin HEAD:main
|
|
64
|
+
git -C "$TAP_DIR" fetch --quiet origin main
|
|
65
|
+
cmp "$SOURCE_FORMULA" <(git -C "$TAP_DIR" show origin/main:Formula/testguard.rb) \
|
|
66
|
+
|| fail "live tap does not match the validated source formula after push"
|
|
67
|
+
|
|
68
|
+
echo "✅ published TestGuard v$VERSION to raccioly/tap ($FORMULA_SHA)"
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
name: Auto Release —
|
|
1
|
+
name: Auto Release — GitHub, npm, PyPI, Homebrew
|
|
2
2
|
|
|
3
3
|
# TAG-DRIVEN and idempotent: a version needs releasing iff no tag v<version>
|
|
4
|
-
# exists. Re-runs never double-publish.
|
|
5
|
-
#
|
|
4
|
+
# exists. Re-runs never double-publish. npm and PyPI authenticate with OIDC
|
|
5
|
+
# Trusted Publishing. Homebrew uses one long-lived SSH deploy key scoped only
|
|
6
|
+
# to the distribution tap; it cannot access this repository or any other one.
|
|
6
7
|
#
|
|
7
8
|
# ONE-TIME SETUP (maintainer, cannot be automated):
|
|
8
9
|
# npm: the first `npm publish` of testguard-cli is manual (Trusted Publishing
|
|
@@ -12,6 +13,9 @@ name: Auto Release — Tag, GitHub Release, npm, PyPI
|
|
|
12
13
|
# PyPI: pypi.org → Your account → Publishing → add a PENDING publisher for
|
|
13
14
|
# project testguard-cli: owner raccioly, repo testguard, workflow
|
|
14
15
|
# release.yml, environment pypi. The first release creates the project.
|
|
16
|
+
# Brew: add a write deploy key named testguard-release-homebrew-tap to
|
|
17
|
+
# raccioly/homebrew-tap and store its private half here as the Actions
|
|
18
|
+
# secret HOMEBREW_TAP_DEPLOY_KEY.
|
|
15
19
|
|
|
16
20
|
on:
|
|
17
21
|
workflow_dispatch: # manual recovery
|
|
@@ -231,15 +235,17 @@ jobs:
|
|
|
231
235
|
# for byte from `npm pack` — after npm serves it (a publish is `PUT 202`;
|
|
232
236
|
# the file can take minutes to appear, so the script polls, bounded).
|
|
233
237
|
#
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
#
|
|
239
|
-
#
|
|
240
|
-
#
|
|
241
|
-
#
|
|
242
|
-
#
|
|
238
|
+
# The validated formula is also copied to the live raccioly/homebrew-tap
|
|
239
|
+
# repository with a write deploy key scoped only to that tap. The source
|
|
240
|
+
# copy cannot commit to main directly: branch protection requires the
|
|
241
|
+
# three CI legs on every commit and github-actions[bot] is no admin. So
|
|
242
|
+
# that one-line change lands as a bot PR with native auto-merge armed, and
|
|
243
|
+
# this job dispatches ci.yml on the branch itself — a PR opened with
|
|
244
|
+
# GITHUB_TOKEN gets no pull_request run, but workflow_dispatch is the
|
|
245
|
+
# documented exception. The PR's own runs may still appear held for
|
|
246
|
+
# first-time-contributor approval, so this job clears those holds before
|
|
247
|
+
# returning. That also means the merge triggers nothing here; the hourly
|
|
248
|
+
# sweep observes the merged source formula and finds brew_missing=false.
|
|
243
249
|
#
|
|
244
250
|
# Idempotent: an existing branch is reused, an existing open PR is left
|
|
245
251
|
# alone (CI re-dispatched only if its head has no check runs), a closed
|
|
@@ -265,9 +271,10 @@ jobs:
|
|
|
265
271
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
266
272
|
with:
|
|
267
273
|
node-version: 24
|
|
268
|
-
- name: Set sha256
|
|
274
|
+
- name: Set sha256, publish the live tap, and land the source formula PR
|
|
269
275
|
env:
|
|
270
276
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
277
|
+
HOMEBREW_TAP_DEPLOY_KEY: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }}
|
|
271
278
|
run: |
|
|
272
279
|
set -euo pipefail
|
|
273
280
|
VERSION="${{ needs.detect-version.outputs.version }}"
|
|
@@ -290,6 +297,11 @@ jobs:
|
|
|
290
297
|
# other surface drifted. Nothing is pushed unless this passes.
|
|
291
298
|
node .github/scripts/sync-release-version.mjs --check --online
|
|
292
299
|
|
|
300
|
+
# Homebrew users install from raccioly/homebrew-tap, not this staged
|
|
301
|
+
# copy. Publish the exact formula that passed the online check. The
|
|
302
|
+
# helper is idempotent and fails closed if the tap cannot be updated.
|
|
303
|
+
.github/scripts/publish-homebrew-tap.sh "$VERSION"
|
|
304
|
+
|
|
293
305
|
if git diff --quiet -- packaging/homebrew/testguard.rb; then
|
|
294
306
|
echo "✅ $BRANCH already carries the published sha256"
|
|
295
307
|
else
|
|
@@ -148,7 +148,7 @@ jobs:
|
|
|
148
148
|
git add -A
|
|
149
149
|
git commit -m "release: v${NEW_VERSION} — automated weekly batch"
|
|
150
150
|
git push origin "$BRANCH"
|
|
151
|
-
printf 'Automated weekly release. Version bump + CHANGELOG entry + synced version surfaces. Merging tags and publishes to npm and
|
|
151
|
+
printf 'Automated weekly release. Version bump + CHANGELOG entry + synced version surfaces. Merging tags and publishes to GitHub, npm, PyPI, and Homebrew via release.yml.\n\nTwo things a GITHUB_TOKEN PR needs done for it, both handled by this workflow: it raises no `pull_request` event, so ci.yml was dispatched on `%s` itself; and the `pull_request` runs it does create are held at `action_required` (github-actions[bot] is permanently a first-time contributor), so those were approved too. Auto-merge is armed and completes once the three Node legs pass.\n\nIf it sits anyway: `gh workflow run ci.yml --ref %s` for missing checks, or check for runs still awaiting approval on the head sha.\n' "$BRANCH" "$BRANCH" > /tmp/pr-body.md
|
|
152
152
|
# A bare `gh pr create` failure here reads as a GraphQL error with no
|
|
153
153
|
# hint of the cause, which is how v0.8.1 lost its release PR after
|
|
154
154
|
# everything else in the release had succeeded.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AI Agent Instructions — TestGuard
|
|
2
2
|
|
|
3
|
-
<!-- docguard:last-reviewed 2026-09-
|
|
3
|
+
<!-- docguard:last-reviewed 2026-09-19 -->
|
|
4
4
|
|
|
5
5
|
Read this before changing anything. It is short on purpose.
|
|
6
6
|
|
|
@@ -63,7 +63,9 @@ node cli/testguard.mjs scaffold src/probe/classify.mjs --json # what the produ
|
|
|
63
63
|
|
|
64
64
|
Dependabot minor/patch PRs and automated release PRs auto-merge when the exact
|
|
65
65
|
CI run is green on all three Node legs. Majors and anything else wait for a
|
|
66
|
-
human.
|
|
66
|
+
human. npm and PyPI publishing is OIDC-only; Homebrew uses a dedicated SSH
|
|
67
|
+
deploy key scoped only to `raccioly/homebrew-tap`. There are no personal or
|
|
68
|
+
registry tokens in this repo.
|
|
67
69
|
|
|
68
70
|
<!-- testguard:begin -->
|
|
69
71
|
## TestGuard
|
|
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.10.2] - 2026-09-20
|
|
11
|
+
|
|
12
|
+
Automated weekly release — everything merged since `v0.10.1`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- fix(release): publish the live Homebrew tap (#142)
|
|
17
|
+
- chore(homebrew): sha256 for v0.10.1 (#141)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Releases now publish the registry-verified Homebrew formula to the live
|
|
23
|
+
`raccioly/tap` repository with a dedicated, tap-scoped deploy key. The
|
|
24
|
+
release fails closed if that copy cannot be made, instead of updating only
|
|
25
|
+
the staging formula in this repository while users remain on an old version.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- The self-probe cost budget is 1440 s, from 1230. The cold v0.10.1 release
|
|
30
|
+
measured 1210 s before the four-fault live-tap claim added about 53 s; 1440
|
|
31
|
+
restores the established ~14% CI headroom. The 100 s per-claim ceiling is
|
|
32
|
+
unchanged, so an individually expensive claim still fails the gate.
|
|
33
|
+
|
|
10
34
|
## [0.10.1] - 2026-09-19
|
|
11
35
|
|
|
12
36
|
Automated weekly release — everything merged since `v0.10.0`.
|
|
@@ -79,12 +79,16 @@ on the fault.
|
|
|
79
79
|
`package.json` is the single source of truth for the version;
|
|
80
80
|
`.github/scripts/sync-release-version.mjs` propagates it. A weekly workflow
|
|
81
81
|
opens a release PR; merging it tags and publishes to npm and PyPI via OIDC
|
|
82
|
-
Trusted Publishing. There are no tokens to rotate.
|
|
82
|
+
Trusted Publishing. There are no npm or PyPI tokens to rotate. Homebrew uses a
|
|
83
|
+
dedicated SSH deploy key that can write only to `raccioly/homebrew-tap`; its
|
|
84
|
+
private half is stored as the `HOMEBREW_TAP_DEPLOY_KEY` Actions secret.
|
|
83
85
|
|
|
84
86
|
The Homebrew formula's `sha256` is the one surface that cannot be known before
|
|
85
87
|
publishing: `release.yml` sets it after `npm publish`, from the registry
|
|
86
|
-
tarball (`sync-release-version.mjs --sha256`),
|
|
87
|
-
|
|
88
|
+
tarball (`sync-release-version.mjs --sha256`), copies that verified formula
|
|
89
|
+
byte-for-byte to the live tap, and lands the source copy as a bot PR with
|
|
90
|
+
auto-merge armed — this repository's `main` is protected, so no job commits to
|
|
91
|
+
it directly.
|
|
88
92
|
`--check` (offline) refuses a placeholder hash; `--check --online` compares
|
|
89
93
|
against the tarball and is what decides, on every hourly sweep, whether the
|
|
90
94
|
formula is still owed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: testguard-cli
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.2
|
|
4
4
|
Summary: Breaks your code on purpose and reports every promise your tests did not notice breaking. Mutation testing bound to the claims a project makes — vitest, jest, Playwright, pytest and unittest. Ships the Node.js CLI (requires Node.js 20+).
|
|
5
5
|
Project-URL: Homepage, https://github.com/raccioly/testguard
|
|
6
6
|
Project-URL: Documentation, https://github.com/raccioly/testguard#readme
|
|
@@ -151,9 +151,9 @@ raises coverage. TestGuard admits it because it fails when the claim is false.
|
|
|
151
151
|
| npm | `npm i -D testguard-cli` then `npx testguard probe` |
|
|
152
152
|
| pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
|
|
153
153
|
| Homebrew | `brew tap raccioly/tap && brew install testguard` |
|
|
154
|
-
| GitHub Action | `uses: raccioly/testguard@v0.10.
|
|
154
|
+
| GitHub Action | `uses: raccioly/testguard@v0.10.2` — see [`action.yml`](./action.yml) |
|
|
155
155
|
| pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
|
|
156
|
-
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.10.
|
|
156
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.10.2/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
|
|
157
157
|
|
|
158
158
|
Projects that set `min-release-age` in `.npmrc` cannot see a version published
|
|
159
159
|
less than that many days ago (`ENOVERSIONS`); install that one with
|
|
@@ -497,12 +497,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
497
497
|
# GitHub Actions
|
|
498
498
|
- uses: actions/checkout@v4
|
|
499
499
|
with: { fetch-depth: 0 }
|
|
500
|
-
- uses: raccioly/testguard@v0.10.
|
|
500
|
+
- uses: raccioly/testguard@v0.10.2
|
|
501
501
|
with: { command: gate }
|
|
502
502
|
|
|
503
503
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
504
504
|
include:
|
|
505
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.
|
|
505
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.2/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
506
506
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
507
507
|
```
|
|
508
508
|
|
|
@@ -127,9 +127,9 @@ raises coverage. TestGuard admits it because it fails when the claim is false.
|
|
|
127
127
|
| npm | `npm i -D testguard-cli` then `npx testguard probe` |
|
|
128
128
|
| pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
|
|
129
129
|
| Homebrew | `brew tap raccioly/tap && brew install testguard` |
|
|
130
|
-
| GitHub Action | `uses: raccioly/testguard@v0.10.
|
|
130
|
+
| GitHub Action | `uses: raccioly/testguard@v0.10.2` — see [`action.yml`](./action.yml) |
|
|
131
131
|
| pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
|
|
132
|
-
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.10.
|
|
132
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.10.2/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
|
|
133
133
|
|
|
134
134
|
Projects that set `min-release-age` in `.npmrc` cannot see a version published
|
|
135
135
|
less than that many days ago (`ENOVERSIONS`); install that one with
|
|
@@ -473,12 +473,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
473
473
|
# GitHub Actions
|
|
474
474
|
- uses: actions/checkout@v4
|
|
475
475
|
with: { fetch-depth: 0 }
|
|
476
|
-
- uses: raccioly/testguard@v0.10.
|
|
476
|
+
- uses: raccioly/testguard@v0.10.2
|
|
477
477
|
with: { command: gate }
|
|
478
478
|
|
|
479
479
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
480
480
|
include:
|
|
481
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.
|
|
481
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.2/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
482
482
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
483
483
|
```
|
|
484
484
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
| Version | Supported |
|
|
6
6
|
|---------|-----------|
|
|
7
|
-
| 0.
|
|
7
|
+
| 0.10.x | ✅ current |
|
|
8
8
|
|
|
9
9
|
## Reporting a vulnerability
|
|
10
10
|
|
|
@@ -27,9 +27,11 @@ TestGuard is a local CLI with a deliberately small surface:
|
|
|
27
27
|
scratch git worktrees under the system temp directory, which are removed
|
|
28
28
|
after each run. With `--in-place`, the mutated files are restored — on
|
|
29
29
|
success, on failure, and on `SIGINT`/`SIGTERM`.
|
|
30
|
-
- **Publishing uses OIDC Trusted Publishing** to npm and PyPI
|
|
31
|
-
pipeline holds no
|
|
32
|
-
generated automatically.
|
|
30
|
+
- **Publishing uses OIDC Trusted Publishing** to npm and PyPI, so the release
|
|
31
|
+
pipeline holds no registry tokens and npm provenance attestations are
|
|
32
|
+
generated automatically. Homebrew publishing uses a dedicated SSH deploy
|
|
33
|
+
key scoped only to `raccioly/homebrew-tap`; it cannot access this repository
|
|
34
|
+
or any other repository.
|
|
33
35
|
|
|
34
36
|
## The claims file is code
|
|
35
37
|
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
<div class="page">
|
|
145
145
|
<div class="masthead">
|
|
146
146
|
<div class="wordmark">Test<span class="mark">Guard</span></div>
|
|
147
|
-
<div class="meta">Technical Brief · v0.10.
|
|
147
|
+
<div class="meta">Technical Brief · v0.10.2 · MIT</div>
|
|
148
148
|
</div>
|
|
149
149
|
|
|
150
150
|
<h1>Your tests pass. That is not the same as your tests working.</h1>
|
|
Binary file
|
|
@@ -85,7 +85,7 @@ other tools, and it must remain portable out of this repository.
|
|
|
85
85
|
| Supported runners | vitest, jest (project runners); Playwright (per file) | They share a JSON report shape; Playwright is selected per file, not per project |
|
|
86
86
|
| Schemas | JSON Schema 2020-12 | Readable without the tool; portable to other languages |
|
|
87
87
|
| Distribution | npm, PyPI, Homebrew, GitHub Action, pre-commit, GitLab component | Meet teams in the pipeline they already run |
|
|
88
|
-
| Publishing | OIDC
|
|
88
|
+
| Publishing | OIDC for npm/PyPI; tap-scoped SSH deploy key for Homebrew | Registry credentials remain tokenless; the only stored publishing secret cannot access any repository except the tap |
|
|
89
89
|
|
|
90
90
|
## Configuration Files
|
|
91
91
|
|
|
@@ -101,7 +101,7 @@ appears here but not on disk, or on disk but not here, is drift.
|
|
|
101
101
|
| `.pre-commit-hooks.yaml` | Hook definitions **we publish for consumers** of TestGuard — a different file for a different audience | consumers' `.pre-commit-config.yaml` |
|
|
102
102
|
| `action.yml` | The GitHub Action surface; `version` input synced from `package.json` | consumers' workflows |
|
|
103
103
|
| `packaging/gitlab/testguard.gitlab-ci.yml` | The GitLab component; tag and `TESTGUARD_VERSION` synced | consumers' pipelines |
|
|
104
|
-
| `packaging/homebrew/testguard.rb` | The Homebrew formula; tarball URL synced | `raccioly/homebrew-tap` |
|
|
104
|
+
| `packaging/homebrew/testguard.rb` | The staged Homebrew formula; tarball URL and registry-derived hash synced, then copied byte-for-byte | `raccioly/homebrew-tap` |
|
|
105
105
|
| `.docguard.json`, `.docguardignore` | Which canonical documents exist and which validators run | DocGuard, in CI and pre-commit |
|
|
106
106
|
| `.npmrc`, `.npmignore`, `.gitattributes` | Packaging and line-ending hygiene | npm, git |
|
|
107
107
|
|
|
@@ -115,7 +115,7 @@ and no license server.
|
|
|
115
115
|
| `ajv` (npm, bundled) | Validate every document against the spec | none — local | Install-time only; the install smoke gate catches a missing runtime dep |
|
|
116
116
|
| `git` (system binary) | Worktree isolation, diffs, snapshots, patch ids | none — local | Precondition failure with a stated reason, exit 2 |
|
|
117
117
|
| The project's test runner | Runs the defenders | none — local | Precondition failure, exit 2; never a verdict about a claim |
|
|
118
|
-
| npm / PyPI registries | Distribution only | build time | A publish fails; the tool itself never calls them at run time |
|
|
118
|
+
| npm / PyPI registries and Homebrew tap | Distribution only | build time | A publish fails; the tool itself never calls them at run time |
|
|
119
119
|
|
|
120
120
|
## Infrastructure (IaC)
|
|
121
121
|
|
|
@@ -133,8 +133,8 @@ to every other surface and `--check` fails the build on drift.
|
|
|
133
133
|
|---|---|---|
|
|
134
134
|
| Pull request | `ci.yml` | Suite on Node 20/22/24, syntax check of every source, install-from-tarball smoke, Python wrapper import, fixture oracles for vitest and jest, Playwright job, change gate, removed-claim check, anchor/replacement preflight before self-probe |
|
|
135
135
|
| Release gate | `ci.yml` | TestGuard probes its own claims; every fault must be killed |
|
|
136
|
-
| Merge to main | `release.yml` | A version with no tag is tagged, released, and published to npm and PyPI over OIDC |
|
|
137
|
-
| Recovery | `release.yml` | Hourly sweep; each registry
|
|
136
|
+
| Merge to main | `release.yml` | A version with no tag is tagged, released, and published to npm and PyPI over OIDC; the verified formula is pushed to the live Homebrew tap with its repository-scoped deploy key |
|
|
137
|
+
| Recovery | `release.yml` | Hourly sweep; each registry and the formula hash are gated on their own state, so a partial publish is fixed by re-running |
|
|
138
138
|
|
|
139
139
|
## Diagrams
|
|
140
140
|
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- docguard:version 1.0.0 -->
|
|
4
4
|
<!-- docguard:status approved -->
|
|
5
|
-
<!-- docguard:last-reviewed 2026-09-
|
|
5
|
+
<!-- docguard:last-reviewed 2026-09-19 -->
|
|
6
6
|
<!-- docguard:owner @raccioly -->
|
|
7
|
-
<!-- docguard:quality negation-load off — a security posture is a list of things that must not happen: no network, no secrets, no tokens, no telemetry. Stating them positively would weaken them. -->
|
|
7
|
+
<!-- docguard:quality negation-load off — a security posture is a list of things that must not happen: no runtime network, no CLI secrets, no registry tokens, no telemetry. Stating them positively would weaken them. -->
|
|
8
8
|
<!-- docguard:quality passive-voice off — threat and control statements name what is protected, not who protects it; the actor is the system throughout. -->
|
|
9
9
|
|
|
10
10
|
> Canonical. Code that contradicts this document is drift.
|
|
@@ -27,8 +27,14 @@ happens in CI, never on a developer machine:
|
|
|
27
27
|
| npm | OIDC Trusted Publishing | none |
|
|
28
28
|
| PyPI | OIDC Trusted Publishing (environment `pypi`) | none |
|
|
29
29
|
| GitHub releases | `GITHUB_TOKEN`, scoped to the run | none stored |
|
|
30
|
+
| Homebrew tap | SSH deploy key scoped to `raccioly/homebrew-tap` | `HOMEBREW_TAP_DEPLOY_KEY` |
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
npm and PyPI have no registry tokens to rotate or leak. Homebrew publishing
|
|
33
|
+
uses one stored credential: a dedicated Ed25519 deploy key whose public half
|
|
34
|
+
has write access only to `raccioly/homebrew-tap` and whose private half is the
|
|
35
|
+
`HOMEBREW_TAP_DEPLOY_KEY` Actions secret in this repository. It is not a
|
|
36
|
+
personal key and cannot access another repository. Rotate it by replacing both
|
|
37
|
+
the tap deploy key and the Actions secret.
|
|
32
38
|
|
|
33
39
|
## Authorization
|
|
34
40
|
|
|
@@ -53,8 +59,11 @@ guarantees are about **what the tool is allowed to touch**:
|
|
|
53
59
|
|
|
54
60
|
## Secrets Management
|
|
55
61
|
|
|
56
|
-
TestGuard neither reads nor stores secrets. It has no credential file,
|
|
57
|
-
keychain access and no environment variable holding a secret.
|
|
62
|
+
The TestGuard CLI neither reads nor stores secrets. It has no credential file,
|
|
63
|
+
no keychain access and no environment variable holding a secret. The release
|
|
64
|
+
workflow is the deliberate exception: its Homebrew step receives the
|
|
65
|
+
tap-scoped deploy key only while it copies a registry-verified formula to the
|
|
66
|
+
distribution repository. npm and PyPI publishing remain OIDC-only.
|
|
58
67
|
|
|
59
68
|
Two consequences worth stating for reviewers:
|
|
60
69
|
|
|
@@ -21,7 +21,7 @@ the test that defends it, and CI fails if any injected fault survives.
|
|
|
21
21
|
| Conformance | `spec/conformance/` | Every valid example validates; every must-reject document is rejected for the defect its filename names | ~1 s |
|
|
22
22
|
| Anchor preflight | `test/claims-anchors.test.mjs` | Exact anchors still locate, supported replacements parse, the check stays below one second, and CI reaches it before self-probe | <1 s |
|
|
23
23
|
| Fixture acceptance | `test/probe.fixture.test.mjs`, `test/probe.jest.test.mjs`, `test/probe.playwright.test.mjs` | The whole pipeline reproduces a known-answer oracle, one per runner | 10–50 s each |
|
|
24
|
-
| Self-verification | `testguard.claims.json`, run by `npm run self:probe` | The tool's own invariants survive fault injection | **
|
|
24
|
+
| Self-verification | `testguard.claims.json`, run by `npm run self:probe` | The tool's own invariants survive fault injection | **20.2 min** cold (measured by the v0.10.1 release job on 2026-09-19 at `70e9d61`: 154 faults, 924 defender runs, 1210 s); seconds when verdicts are reused, which is why PR CI restores previous evidence from cache. Budgeted at 1440 s in `testguard.cost-budget.json` for the additional four-fault live-tap claim and **gated** in CI — cost growth is reviewed instead of disappearing into a passing log |
|
|
25
25
|
| Install smoke | `.github/scripts/install-smoke.mjs` | The **packed tarball** runs with production dependencies only | ~20 s |
|
|
26
26
|
| Runtime budget | `ci.yml` | The suite has not silently started walking the wrong tree | gate, not a test |
|
|
27
27
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testguard-cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "testguard-cli",
|
|
9
|
-
"version": "0.10.
|
|
9
|
+
"version": "0.10.2",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"ajv": "8.20.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testguard-cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "Breaks your code on purpose and reports every promise your tests did not notice breaking. Mutation testing bound to the claims a project makes — vitest, jest, Playwright, pytest and unittest.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Include it from a project's .gitlab-ci.yml, pinned to a release:
|
|
4
4
|
#
|
|
5
5
|
# include:
|
|
6
|
-
# - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.
|
|
6
|
+
# - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.2/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
7
7
|
# inputs:
|
|
8
8
|
# dir: backend # where testguard.claims.json lives (default ".")
|
|
9
9
|
# post_note: true # post the brief as a merge-request note (needs TESTGUARD_GITLAB_TOKEN, api scope)
|
|
@@ -29,7 +29,7 @@ spec:
|
|
|
29
29
|
inputs:
|
|
30
30
|
version:
|
|
31
31
|
description: testguard-cli version to run
|
|
32
|
-
default: "0.10.
|
|
32
|
+
default: "0.10.2"
|
|
33
33
|
dir:
|
|
34
34
|
description: directory whose test runner is invoked (where testguard.claims.json lives)
|
|
35
35
|
default: "."
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
class Testguard < Formula
|
|
16
16
|
desc "Proves a test suite defends the claims a project makes"
|
|
17
17
|
homepage "https://github.com/raccioly/testguard"
|
|
18
|
-
url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.10.
|
|
19
|
-
sha256 "
|
|
18
|
+
url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.10.2.tgz"
|
|
19
|
+
sha256 "dae61a90d2eb57f116597f3bc7734d3bb9759cdcfbf00a6447b9607b87933e88"
|
|
20
20
|
license "MIT"
|
|
21
21
|
|
|
22
22
|
depends_on "node"
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "testguard-cli"
|
|
7
|
-
version = "0.10.
|
|
7
|
+
version = "0.10.2"
|
|
8
8
|
description = "Breaks your code on purpose and reports every promise your tests did not notice breaking. Mutation testing bound to the claims a project makes — vitest, jest, Playwright, pytest and unittest. Ships the Node.js CLI (requires Node.js 20+)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -320,3 +320,77 @@ describe('sync-release-version: the release workflows allow exactly what a relea
|
|
|
320
320
|
expect(text).not.toMatch(/case "\$f" in package\.json\|/);
|
|
321
321
|
});
|
|
322
322
|
});
|
|
323
|
+
|
|
324
|
+
describe('release.yml: the verified formula is deployed, not merely staged', () => {
|
|
325
|
+
it('publishes to the live tap before the source formula PR can complete', () => {
|
|
326
|
+
const text = readFileSync(join(ROOT, '.github', 'workflows', 'release.yml'), 'utf8');
|
|
327
|
+
expect(text).toContain('HOMEBREW_TAP_DEPLOY_KEY: $' + '{{ secrets.HOMEBREW_TAP_DEPLOY_KEY }}');
|
|
328
|
+
const verified = text.indexOf('sync-release-version.mjs --check --online');
|
|
329
|
+
const published = text.indexOf('publish-homebrew-tap.sh "$VERSION"');
|
|
330
|
+
const sourceCommit = text.indexOf('git add packaging/homebrew/testguard.rb');
|
|
331
|
+
expect(verified).toBeGreaterThan(-1);
|
|
332
|
+
expect(published).toBeGreaterThan(verified);
|
|
333
|
+
expect(sourceCommit).toBeGreaterThan(published);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
function checked(command, args, options = {}) {
|
|
338
|
+
const result = spawnSync(command, args, { encoding: 'utf8', ...options });
|
|
339
|
+
expect(result.status, command + ' ' + args.join(' ') + '\n' + result.stderr).toBe(0);
|
|
340
|
+
return result;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function tapRemote() {
|
|
344
|
+
const dir = mkdtempSync(join(tmpdir(), 'tg-homebrew-tap-'));
|
|
345
|
+
const remote = join(dir, 'tap.git');
|
|
346
|
+
const seed = join(dir, 'seed');
|
|
347
|
+
checked('git', ['init', '--bare', '--initial-branch=main', remote]);
|
|
348
|
+
checked('git', ['init', '--initial-branch=main', seed]);
|
|
349
|
+
checked('git', ['-C', seed, 'config', 'user.name', 'fixture']);
|
|
350
|
+
checked('git', ['-C', seed, 'config', 'user.email', 'fixture@example.test']);
|
|
351
|
+
mkdirSync(join(seed, 'Formula'), { recursive: true });
|
|
352
|
+
const current = readFileSync(join(ROOT, 'packaging', 'homebrew', 'testguard.rb'), 'utf8');
|
|
353
|
+
const stale = current
|
|
354
|
+
.replace(/testguard-cli-[0-9]+\.[0-9]+\.[0-9]+\.tgz/, 'testguard-cli-0.8.0.tgz')
|
|
355
|
+
.replace(/^(\s*sha256 ")[0-9a-f]{64}(")/m, '$1' + 'a'.repeat(64) + '$2');
|
|
356
|
+
writeFileSync(join(seed, 'Formula', 'testguard.rb'), stale);
|
|
357
|
+
checked('git', ['-C', seed, 'add', 'Formula/testguard.rb']);
|
|
358
|
+
checked('git', ['-C', seed, 'commit', '-m', 'stale testguard formula']);
|
|
359
|
+
checked('git', ['-C', seed, 'remote', 'add', 'origin', remote]);
|
|
360
|
+
checked('git', ['-C', seed, 'push', '-u', 'origin', 'main']);
|
|
361
|
+
return { remote, original: stale };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
describe('publish-homebrew-tap: the validated source formula reaches the actual tap', () => {
|
|
365
|
+
const script = join(ROOT, '.github', 'scripts', 'publish-homebrew-tap.sh');
|
|
366
|
+
const source = join(ROOT, 'packaging', 'homebrew', 'testguard.rb');
|
|
367
|
+
const version = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8')).version;
|
|
368
|
+
const remoteFormula = (remote) => checked('git', ['--git-dir', remote, 'show', 'main:Formula/testguard.rb']).stdout;
|
|
369
|
+
const commitCount = (remote) => Number(checked('git', ['--git-dir', remote, 'rev-list', '--count', 'main']).stdout.trim());
|
|
370
|
+
|
|
371
|
+
it('pushes the exact formula once and is idempotent on a retry', () => {
|
|
372
|
+
const { remote, original } = tapRemote();
|
|
373
|
+
const env = { ...process.env, HOMEBREW_TAP_REMOTE: remote, HOMEBREW_TAP_SOURCE: source };
|
|
374
|
+
expect(remoteFormula(remote)).toBe(original);
|
|
375
|
+
|
|
376
|
+
const first = spawnSync(script, [version], { cwd: ROOT, env, encoding: 'utf8' });
|
|
377
|
+
expect(first.status, first.stderr).toBe(0);
|
|
378
|
+
expect(first.stdout).toContain('published TestGuard v' + version);
|
|
379
|
+
expect(remoteFormula(remote)).toBe(readFileSync(source, 'utf8'));
|
|
380
|
+
const afterFirst = commitCount(remote);
|
|
381
|
+
|
|
382
|
+
const retry = spawnSync(script, [version], { cwd: ROOT, env, encoding: 'utf8' });
|
|
383
|
+
expect(retry.status, retry.stderr).toBe(0);
|
|
384
|
+
expect(retry.stdout).toContain('already carries TestGuard v' + version);
|
|
385
|
+
expect(commitCount(remote)).toBe(afterFirst);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('refuses to publish when the requested release and formula version disagree', () => {
|
|
389
|
+
const { remote, original } = tapRemote();
|
|
390
|
+
const env = { ...process.env, HOMEBREW_TAP_REMOTE: remote, HOMEBREW_TAP_SOURCE: source };
|
|
391
|
+
const result = spawnSync(script, ['9.8.7'], { cwd: ROOT, env, encoding: 'utf8' });
|
|
392
|
+
expect(result.status).not.toBe(0);
|
|
393
|
+
expect(result.stderr).toContain('does not match release 9.8.7');
|
|
394
|
+
expect(remoteFormula(remote)).toBe(original);
|
|
395
|
+
});
|
|
396
|
+
});
|