mirror of
https://github.com/conorbronsdon/avoid-ai-writing.git
synced 2026-09-19 01:32:11 +08:00
ci: harden release and review boundaries (#306)
Co-authored-by: Conor Bronsdon <conorbronsdon@users.noreply.github.com> Co-authored-by: Sharad <sharadvc@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
@@ -20,25 +20,9 @@ on:
|
||||
- 'bin/**'
|
||||
- 'action.yml'
|
||||
- '.pre-commit-hooks.yaml'
|
||||
# This job is a required-check candidate, so it must report on every PR.
|
||||
# Workflow-level path filters would leave unrelated PRs pending forever.
|
||||
pull_request:
|
||||
paths:
|
||||
- "detector/**"
|
||||
- "scripts/**"
|
||||
- "examples/**"
|
||||
- "evals/**"
|
||||
- "package.json"
|
||||
- ".github/workflows/detector-test.yml"
|
||||
- "README.md"
|
||||
- "SKILL.md"
|
||||
- "SKILL.full.md"
|
||||
- "references/**"
|
||||
- "dist/**"
|
||||
- "CHANGELOG.md"
|
||||
- "CONTRIBUTING.md"
|
||||
- "PROOF.md"
|
||||
- 'bin/**'
|
||||
- 'action.yml'
|
||||
- '.pre-commit-hooks.yaml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -35,6 +35,8 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# ssot-check resolves `../<repo>/...` copies against real directories on
|
||||
# disk, so the surfaces have to be cloned as siblings of this checkout.
|
||||
|
||||
@@ -5,6 +5,8 @@ on:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "CHANGELOG.md"
|
||||
# Recovery dispatches must target main. The release environment enforces
|
||||
# that boundary again outside this editable workflow file.
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
@@ -14,36 +16,68 @@ permissions:
|
||||
# `gh release view` before `gh release create`, and publish-npm reads
|
||||
# `npm view` before `npm publish`. Overlapping runs can both read "not there
|
||||
# yet" and then collide. Two runs carrying the same version turn one of them
|
||||
# red; two runs carrying different versions can finish out of order and leave
|
||||
# npm's `latest` pointing at the older one. Serializing puts the second run's
|
||||
# checks after the first run's writes, so its `exists` guards see the truth and
|
||||
# skip — the guards and this block do the job together, neither one alone.
|
||||
# red; two runs carrying different versions can otherwise race their shared
|
||||
# tags and registry state. Serializing puts each run's checks after the prior
|
||||
# run's writes, so its `exists` guards see the truth. `queue: max` matters too:
|
||||
# without it, a newer trigger replaces an already-pending release even when
|
||||
# `cancel-in-progress` is false.
|
||||
#
|
||||
# The group omits `github.ref` on purpose: the registry and the release list are
|
||||
# repo-global, so a workflow_dispatch has to queue behind a main push too.
|
||||
# Never cancel — a cancelled run can die between its check and its publish, or
|
||||
# leave a GitHub release standing with nothing published against it.
|
||||
# repo-global. Never cancel — a cancelled run can die between its check and its
|
||||
# publish, or leave a GitHub release standing with nothing published against it.
|
||||
concurrency:
|
||||
group: release
|
||||
queue: max
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
release:
|
||||
# Validate the exact release commit without a write token or OIDC access.
|
||||
# Nothing public exists until this job has proved the version, tests, and
|
||||
# package payload all succeed.
|
||||
preflight:
|
||||
if: github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
version: ${{ steps.changelog.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Parse latest version from CHANGELOG.md
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version: 24
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Verify release versions
|
||||
id: changelog
|
||||
run: node scripts/verify-release-versions.js --github-output "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Test the release commit
|
||||
run: npm test
|
||||
|
||||
- name: Verify the npm package can be assembled
|
||||
run: npm pack --dry-run --json
|
||||
|
||||
release:
|
||||
needs: preflight
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: release
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Check if the release tag already exists
|
||||
id: tagcheck
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
TAG="v${{ steps.changelog.outputs.version }}"
|
||||
TAG="v${{ needs.preflight.outputs.version }}"
|
||||
if gh release view "$TAG" >/dev/null 2>&1; then
|
||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Release $TAG already exists — nothing to do."
|
||||
@@ -54,7 +88,7 @@ jobs:
|
||||
- name: Extract this version's changelog section
|
||||
if: steps.tagcheck.outputs.exists == 'false'
|
||||
run: |
|
||||
VERSION="${{ steps.changelog.outputs.version }}"
|
||||
VERSION="${{ needs.preflight.outputs.version }}"
|
||||
awk -v heading="## [$VERSION]" '
|
||||
$0 ~ /^## \[/ {
|
||||
if (found) exit
|
||||
@@ -73,7 +107,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
VERSION="${{ steps.changelog.outputs.version }}"
|
||||
VERSION="${{ needs.preflight.outputs.version }}"
|
||||
gh release create "v$VERSION" \
|
||||
--target "$GITHUB_SHA" \
|
||||
--title "v$VERSION" \
|
||||
@@ -87,14 +121,17 @@ jobs:
|
||||
# the CHANGELOG heading the release job used — the same version guard as the
|
||||
# release job must block a publish, not ride into the registry.
|
||||
publish-npm:
|
||||
needs: release
|
||||
needs: [preflight, release]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: release
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # npm Trusted Publishing (OIDC) + --provenance
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
@@ -165,10 +202,6 @@ jobs:
|
||||
fi
|
||||
echo "HEAD is the commit tagged $TAG ($TAG_SHA) — safe to publish."
|
||||
|
||||
- name: Test before publish
|
||||
if: steps.npmcheck.outputs.exists == 'false'
|
||||
run: npm test
|
||||
|
||||
- name: Publish with provenance
|
||||
if: steps.npmcheck.outputs.exists == 'false'
|
||||
run: npm publish --provenance --access public
|
||||
|
||||
@@ -177,3 +177,6 @@ writing rule needs a minor version bump. Exempt changes need no version bump;
|
||||
leave published release entries intact.
|
||||
|
||||
After changing either canonical file, run `bash scripts/sync-plugin-skill.sh && bash scripts/sync-cursor-rules.sh`. This regenerates both bundles, `SKILL.full.md`, and the portable paste/Cursor artifacts; CI checks parity. Do not edit generated copies.
|
||||
|
||||
Maintainers should follow [the release recovery procedure](docs/releasing.md)
|
||||
instead of moving a tag or reusing a published version after a failed run.
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# Security policy
|
||||
|
||||
## Supported versions
|
||||
|
||||
Security fixes target the current `main` branch and the latest
|
||||
[release](https://github.com/conorbronsdon/avoid-ai-writing/releases). Older
|
||||
releases are not routinely patched.
|
||||
|
||||
## Report a vulnerability
|
||||
|
||||
Do not file a public issue for an undisclosed security problem. Use this
|
||||
repository's
|
||||
[private vulnerability reporting](https://github.com/conorbronsdon/avoid-ai-writing/security/advisories/new)
|
||||
form instead.
|
||||
|
||||
Include the affected version or commit, reproduction steps, realistic impact,
|
||||
and any suggested mitigation. Reports about code execution, path traversal,
|
||||
credential exposure, or release and package integrity are especially useful.
|
||||
@@ -5,3 +5,6 @@ For bugs, routing problems, false positives, packaging issues, or ChatGPT and Co
|
||||
https://github.com/conorbronsdon/avoid-ai-writing/issues
|
||||
|
||||
Include the plugin version, the host surface you used, the expected behavior, and a minimal reproduction. Do not post confidential drafts or personal information in a public issue.
|
||||
|
||||
For security vulnerabilities, do not use the public issue tracker. Follow
|
||||
[SECURITY.md](SECURITY.md) and use GitHub private vulnerability reporting.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Release recovery
|
||||
|
||||
The release workflow runs only after a version change reaches `main`. Its
|
||||
read-only preflight checks the version agreement, test suite, and npm package
|
||||
payload before either publishing job receives write authority.
|
||||
|
||||
GitHub serializes release runs. Keep `queue: max`: `cancel-in-progress: false`
|
||||
protects the running job but does not retain more than one pending run by
|
||||
itself.
|
||||
|
||||
## Recover a failed npm publication
|
||||
|
||||
If the GitHub release exists but npm publication fails, rerun the failed jobs
|
||||
from the original workflow run. A rerun keeps the original commit SHA, sees the
|
||||
existing GitHub release, verifies that its tag still names that SHA, and retries
|
||||
only the missing npm version. Do not merge another version bump as a recovery
|
||||
mechanism, move the release tag, or reuse a version that reached the registry.
|
||||
|
||||
If preflight fails, no release or package exists. Fix the failure through a new
|
||||
pull request, keep the intended version unchanged, then manually dispatch the
|
||||
release workflow from `main`. The release environment rejects another ref.
|
||||
Reference in New Issue
Block a user