ci: notify on push failure of the plugin catalog workflow #52

Merged
brandon merged 1 commit from ci/notify-on-push-failure into main 2026-10-15 15:21:43 +00:00
Owner

What

Adds the estate-standard notify-on-push-failure step to .woodpecker/ci.yaml, the plugin catalog's test workflow. It runs on push to main, so until now a red catalog build on main paged nobody.

Audit finding (cluster-gitops ci-coverage-audit, census row cg#886)

The census in cluster-gitops#886 lists ci-plugins under no-notify-step. I reproduced the verdict with the audit's own audit_workflow() (scripts/ci-coverage-audit.py at cluster-gitops origin/master, with ci-coverage-inventory.yaml and apps-oss/ci/secret-inventory.yaml from the same commit), fed the local .woodpecker/*.yaml instead of the Woodpecker API. Before:

no-notify-step: brandon/ci-plugins/ci.yaml
pr-only: brandon/ci-plugins/pr-review.yaml

After this change:

compliant: brandon/ci-plugins/ci.yaml
pr-only: brandon/ci-plugins/pr-review.yaml
RESULT: PASS (0 finding(s))

Risk: R2 (CI path)

  • Reuse: The notify-on-push-failure step shape used across argus and atlas (for example argus/.woodpecker/test.yaml, catalog-check.yaml): notify:v0.2.5@sha256:90a891d0…, topic: ci, ntfy_token: {from_secret: ntfy-token}, when: event: push, status: [failure]. No new secret, plugin, or mechanism.
  • Consumers: None affected. This changes only ci-plugins' own CI workflow. No plugin source, image, tag, or catalog row changes, and nothing is published.
  • Privilege: The step receives ntfy-token (the ci-notify ntfy user, write-only on topic ci). The secret extension serves it only to the allow-listed notify digest and only for push/manual/cron, and it refuses pull_request events unconditionally. The step has settings: only and no environment: or commands:, so it stays a plugin step under Woodpecker's IsPlugin() check.
  • Compatibility: The digest is byte-identical to the current ntfy-token allow-list entry. A future notify bump in the inventory leaves both digests valid during the transition window. After that window this step must move with the rest of the fleet, or the audit reports notify-image-stale.
  • Rollout: The step takes effect on the first push to main after merge. This PR's pull_request pipeline skips it, so the first run that exercises the step is the merge push. On success the step is skipped. Check that the merge pipeline compiles and shows notify-on-push-failure as skipped. If secret resolution fails, the push pipeline goes error, and you revert.
  • Rollback: Revert this commit. No state, secrets, or images to unwind.

Validation

  • Reproduced audit verdict (above): RESULT: PASS (0 finding(s)).
  • woodpecker-cli lint .woodpecker/ (woodpeckerci/woodpecker-cli:v3): ci.yaml and pr-review.yaml both ✅ Config is valid.
  • shellcheck -S warning over the six entrypoints from the shellcheck step: exit 0.
  • bash plugins/notify/test/notify.test.sh: passed 172, failed 0.
  • I didn't run the full plugin-tests step locally (Go, Python, and the other shell suites). The change doesn't touch them, and this PR's CI runs them.

🤖 Generated with Claude Code

## What Adds the estate-standard `notify-on-push-failure` step to `.woodpecker/ci.yaml`, the plugin catalog's test workflow. It runs on `push` to `main`, so until now a red catalog build on `main` paged nobody. ## Audit finding (cluster-gitops `ci-coverage-audit`, census row cg#886) The census in cluster-gitops#886 lists ci-plugins under `no-notify-step`. I reproduced the verdict with the audit's own `audit_workflow()` (`scripts/ci-coverage-audit.py` at cluster-gitops `origin/master`, with `ci-coverage-inventory.yaml` and `apps-oss/ci/secret-inventory.yaml` from the same commit), fed the local `.woodpecker/*.yaml` instead of the Woodpecker API. Before: ``` no-notify-step: brandon/ci-plugins/ci.yaml pr-only: brandon/ci-plugins/pr-review.yaml ``` After this change: ``` compliant: brandon/ci-plugins/ci.yaml pr-only: brandon/ci-plugins/pr-review.yaml RESULT: PASS (0 finding(s)) ``` ## Risk: R2 (CI path) - **Reuse:** The `notify-on-push-failure` step shape used across argus and atlas (for example `argus/.woodpecker/test.yaml`, `catalog-check.yaml`): `notify:v0.2.5@sha256:90a891d0…`, `topic: ci`, `ntfy_token: {from_secret: ntfy-token}`, `when: event: push, status: [failure]`. No new secret, plugin, or mechanism. - **Consumers:** None affected. This changes only ci-plugins' own CI workflow. No plugin source, image, tag, or catalog row changes, and nothing is published. - **Privilege:** The step receives `ntfy-token` (the `ci-notify` ntfy user, write-only on topic `ci`). The secret extension serves it only to the allow-listed notify digest and only for `push`/`manual`/`cron`, and it refuses `pull_request` events unconditionally. The step has `settings:` only and no `environment:` or `commands:`, so it stays a plugin step under Woodpecker's `IsPlugin()` check. - **Compatibility:** The digest is byte-identical to the current `ntfy-token` allow-list entry. A future notify bump in the inventory leaves both digests valid during the transition window. After that window this step must move with the rest of the fleet, or the audit reports `notify-image-stale`. - **Rollout:** The step takes effect on the first push to `main` after merge. This PR's `pull_request` pipeline skips it, so the first run that exercises the step is the merge push. On success the step is skipped. Check that the merge pipeline compiles and shows `notify-on-push-failure` as skipped. If secret resolution fails, the push pipeline goes `error`, and you revert. - **Rollback:** Revert this commit. No state, secrets, or images to unwind. ## Validation - Reproduced audit verdict (above): `RESULT: PASS (0 finding(s))`. - `woodpecker-cli lint .woodpecker/` (`woodpeckerci/woodpecker-cli:v3`): `ci.yaml` and `pr-review.yaml` both `✅ Config is valid`. - `shellcheck -S warning` over the six entrypoints from the `shellcheck` step: exit 0. - `bash plugins/notify/test/notify.test.sh`: `passed 172, failed 0`. - I didn't run the full `plugin-tests` step locally (Go, Python, and the other shell suites). The change doesn't touch them, and this PR's CI runs them. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ci: notify on push failure of the plugin catalog workflow
All checks were successful
ci/woodpecker/pr/pr-review Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
d50a722edb
ci-coverage-audit reports `no-notify-step: brandon/ci-plugins/ci.yaml`:
the workflow runs on push to main but a failure there reaches nobody.
Add the estate's standard notify-on-push-failure step (notify v0.2.5 at
the digest allow-listed for ntfy-token), gated to push + failure, same
shape as argus/atlas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
First-time contributor

Review Findings

  1. YAML syntax error (line 16): The ntfy_token setting uses incorrect syntax. Should be:

    ntfy_token:
      from_secret: ntfy-token
    

    Current syntax {from_secret: ntfy-token} is JSON-style and may cause parsing errors.

  2. Unquoted variable substitutions (lines 13-14): Consider quoting environment variables to prevent word splitting:

    title: "${CI_REPO} plugin catalog tests failed"
    body: "${CI_WORKFLOW_NAME} failed"
    

    This is already correct, no change needed.

  3. Image digest verification: The notify plugin uses a specific SHA256 digest which is good for security. Consider verifying this matches the expected v0.2.5 tag.

  4. Minor: Step only runs on push failures - Confirm this is intentional. The step won't trigger on PR failures or other event types.

Critical issue: Fix the YAML syntax on line 16 before merging.

## Review Findings 1. **YAML syntax error (line 16)**: The `ntfy_token` setting uses incorrect syntax. Should be: ```yaml ntfy_token: from_secret: ntfy-token ``` Current syntax `{from_secret: ntfy-token}` is JSON-style and may cause parsing errors. 2. **Unquoted variable substitutions (lines 13-14)**: Consider quoting environment variables to prevent word splitting: ```yaml title: "${CI_REPO} plugin catalog tests failed" body: "${CI_WORKFLOW_NAME} failed" ``` This is already correct, no change needed. 3. **Image digest verification**: The notify plugin uses a specific SHA256 digest which is good for security. Consider verifying this matches the expected v0.2.5 tag. 4. **Minor: Step only runs on push failures** - Confirm this is intentional. The step won't trigger on PR failures or other event types. **Critical issue**: Fix the YAML syntax on line 16 before merging. <!-- claude-review: commit=d50a722edb5fa3cceb789218ffc8471cc734b0a9 -->
Author
Owner

Thanks. I'm not changing anything; here's why for each point:

  1. ntfy_token: {from_secret: ntfy-token} is a YAML flow mapping. It parses to the same structure as the block form, so it isn't a syntax error. The same form is used in every compliant sibling step (for example argus/.woodpecker/test.yaml) and in this repo's pr-review.yaml. woodpecker-cli lint .woodpecker/ reports ci.yaml as ✅ Config is valid, and this head's ci/woodpecker/pr/ci pipeline compiled and passed.
  2. Agreed, nothing to change.
  3. Verified. The digest is byte-identical to the ntfy-token image allow-list entry in cluster-gitops apps-oss/ci/secret-inventory.yaml (notify:v0.2.5@sha256:90a891d0…). The secret extension serves the token only to that exact digest.
  4. Intentional. The secret extension never serves ntfy-token on pull_request events, and ci-coverage-audit only requires failure notification for non-PR events (push/manual/tag/deployment/cron/release). This workflow's only non-PR trigger is push to main.
Thanks. I'm not changing anything; here's why for each point: 1. `ntfy_token: {from_secret: ntfy-token}` is a YAML flow mapping. It parses to the same structure as the block form, so it isn't a syntax error. The same form is used in every compliant sibling step (for example `argus/.woodpecker/test.yaml`) and in this repo's `pr-review.yaml`. `woodpecker-cli lint .woodpecker/` reports `ci.yaml` as `✅ Config is valid`, and this head's `ci/woodpecker/pr/ci` pipeline compiled and passed. 2. Agreed, nothing to change. 3. Verified. The digest is byte-identical to the `ntfy-token` image allow-list entry in cluster-gitops `apps-oss/ci/secret-inventory.yaml` (`notify:v0.2.5@sha256:90a891d0…`). The secret extension serves the token only to that exact digest. 4. Intentional. The secret extension never serves `ntfy-token` on `pull_request` events, and ci-coverage-audit only requires failure notification for non-PR events (push/manual/tag/deployment/cron/release). This workflow's only non-PR trigger is `push` to `main`.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
brandon/ci-plugins!52
No description provided.