From d01a32a3f6b419388c1aa100cfeef2c3c2595132 Mon Sep 17 00:00:00 2001 From: UttyWotty <105616324+UttyWotty@users.noreply.github.com> Date: Mon, 3 Aug 2026 17:59:04 +0900 Subject: [PATCH 1/2] ci: verify the repo brew and clone install from [v1.8.4] This repository is what `brew install opsentry` and `git clone` pull from, and nothing verified it -- there were no workflows at all, only issue and PR templates. Every push and pull request now runs the 168-assertion hook suite, lints the Python, and syntax-checks install.sh plus all eight guardrail hooks so a broken installer cannot ship. The workflow is self-contained rather than calling the organisation's shared one, which lives in a private repository. A contributor opening a pull request must be able to read every step that gates it, and a private workflow would be invisible to them. --- .github/workflows/ci.yml | 53 ++++++++++++++++++++++++++++++++++++++++ CHANGELOG.md | 14 +++++++++++ VERSION | 2 +- 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9e08395 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +# CI for the public OpSentry repository. +# +# Self-contained on purpose. The rest of the org calls a reusable workflow that +# lives in a private repository; this one cannot, because a public repo should +# not depend on a private repo's workflow to verify itself -- a contributor +# opening a PR must be able to read every step that gates it, and the private +# workflow would be invisible to them. +# +# This repository is what `brew install opsentry` and `git clone` actually pull +# from, so until now the two most-used install paths had nothing checking them. + +name: ci + +on: + pull_request: + push: + branches: [develop, main] + +jobs: + test: + name: hooks + lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Install jq + # The hooks parse Claude Code's PreToolUse JSON with jq; the suite + # cannot run without it. Ubuntu runners ship it, but pinning the install + # keeps the requirement visible rather than inherited. + run: sudo apt-get install -y jq + + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Hook test suite + # 168 assertions across the 8 guardrail hooks, simulating the JSON + # Claude Code sends on PreToolUse. + run: bash test.sh + + - name: Lint + run: | + pip install ruff + ruff check opsentry + + - name: Verify the installer is self-consistent + # install.sh is the git-clone install path. A broken shebang or syntax + # error here breaks the documented install for everyone who does not use + # brew or pip, and no test would otherwise catch it. + run: | + bash -n install.sh + bash -n opsentry/install.sh + for hook in opsentry/claude/hooks/*.sh; do bash -n "$hook"; done diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d54ebb..4aa3f2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.8.4] - 2026-08-03 + +### Added +- **CI.** This repository is what `brew install opsentry` and `git clone` pull + from, and until now nothing verified it — there were no workflows at all, only + issue and PR templates. Every push and pull request now runs the 168-assertion + hook suite, lints the Python, and syntax-checks `install.sh` and all eight + guardrail hooks so a broken installer cannot ship. + + The workflow is **self-contained** rather than calling the organisation's + shared one, which lives in a private repository: a contributor opening a pull + request must be able to read every step that gates it, and a private workflow + would be invisible to them. + ## [1.8.3] - 2026-08-03 ### Changed - **Version aligned with the OpSentry release line.** This repository had been diff --git a/VERSION b/VERSION index a7ee35a..bfa363e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.8.3 +1.8.4 From 3ef0f538fad75bd0628d86db23555d8d2ee5bd30 Mon Sep 17 00:00:00 2001 From: UttyWotty <105616324+UttyWotty@users.noreply.github.com> Date: Mon, 3 Aug 2026 18:01:51 +0900 Subject: [PATCH 2/2] fix(ci): make the shebanged scripts executable [v1.8.4] CI caught it on its first run: baseline.py and blocklog_audit.py carry #!/usr/bin/env python3 but were tracked 100644, so neither could be run directly despite advertising that it could. Both are invoked via python3 today, so the bit was cosmetic -- but a shebang that does not work is a claim the file does not honour, and fixing it is better than suppressing the check that found it. ruff is pinned in CI because its default rule set grows between releases, and an unpinned upgrade would fail a commit that changed nothing. --- .github/workflows/ci.yml | 4 +++- CHANGELOG.md | 5 +++++ opsentry/baseline.py | 0 opsentry/blocklog_audit.py | 0 4 files changed, 8 insertions(+), 1 deletion(-) mode change 100644 => 100755 opsentry/baseline.py mode change 100644 => 100755 opsentry/blocklog_audit.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e08395..8b4051a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,9 @@ jobs: - name: Lint run: | - pip install ruff + # Pinned: ruff's default rule set grows between releases, and an + # unpinned upgrade would fail CI on a commit that changed nothing. + pip install ruff==0.16.1 ruff check opsentry - name: Verify the installer is self-consistent diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aa3f2c..dc4be91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 request must be able to read every step that gates it, and a private workflow would be invisible to them. +### Fixed +- `baseline.py` and `blocklog_audit.py` carried `#!/usr/bin/env python3` but were + tracked non-executable, so neither could be run directly despite advertising + that it could. Found by CI on its first run. + ## [1.8.3] - 2026-08-03 ### Changed - **Version aligned with the OpSentry release line.** This repository had been diff --git a/opsentry/baseline.py b/opsentry/baseline.py old mode 100644 new mode 100755 diff --git a/opsentry/blocklog_audit.py b/opsentry/blocklog_audit.py old mode 100644 new mode 100755