Files
openconcho/scripts/pr-evidence.sh

117 lines
4.0 KiB
Bash
Raw Normal View History

chore(hooks): pre-push gate, secret scan, pr:evidence drafter Phase A of the quality-gate rollout — local + pre-push gates that stop slop before it leaves the laptop. Preventative controls, per the architect playbook. - .husky/pre-commit — adds scripts/secret-scan.sh as the first check before the existing Biome format/lint. Blocks the commit when a likely secret is found in staged additions. Existing Biome behaviour is preserved. - .husky/pre-push (new) — runs `pnpm check` (lint + typecheck + test) before the branch leaves the laptop. Mirrors the `check` job in .github/workflows/ci.yml so PR-blocking issues surface locally first. Bypassable for genuine emergencies with `git push --no-verify`. - scripts/secret-scan.sh (new) — regex scan over staged additions for the common accidents: AWS keys, Anthropic/OpenAI/GitHub/Slack/Google/ Stripe tokens, JWTs, PEM private key blocks, hardcoded password literals. Validated against synthetic leaks of each type. No external tool dependency — pure bash + grep, runs in <100ms typical. - scripts/pr-evidence.sh (new) — drafts a PR_BODY.md from the diff vs origin/main: file lists (added/modified/deleted), commit summaries, and the QA checklist. Flags screenshots as REQUIRED when the diff touches packages/web/src/{components,routes} or packages/desktop. Wired as `pnpm pr:evidence` so it runs from anywhere in the workspace. - .gitignore — adds PR_BODY.md so drafts don't get committed by accident. Pre-commit stays fast (<2s typical). Heavy checks (typecheck, full test run) live in pre-push.
2026-05-24 18:29:13 +01:00
#!/usr/bin/env bash
# Draft a PR body for the current branch based on the diff vs origin/main.
#
# Writes to PR_BODY.md in the repo root (gitignored — see end of script).
# Pre-fills the structure required by .github/pull_request_template.md
# and flags whether screenshots are required based on touched paths.
#
# Usage:
# ./scripts/pr-evidence.sh # writes ./PR_BODY.md
# ./scripts/pr-evidence.sh > /tmp/body.md # write to stdout
set -euo pipefail
OUTPUT="${1:-PR_BODY.md}"
# Find the base branch (default origin/main) the current branch diverged from.
BASE_REF="${BASE_REF:-origin/main}"
git fetch origin main --quiet 2>/dev/null || true
MERGE_BASE=$(git merge-base HEAD "$BASE_REF" 2>/dev/null || echo "$BASE_REF")
CHANGED=$(git diff --name-only "$MERGE_BASE"...HEAD)
ADDED=$(git diff --name-status --diff-filter=A "$MERGE_BASE"...HEAD | awk '{print $2}')
MODIFIED=$(git diff --name-status --diff-filter=M "$MERGE_BASE"...HEAD | awk '{print $2}')
DELETED=$(git diff --name-status --diff-filter=D "$MERGE_BASE"...HEAD | awk '{print $2}')
# Heuristic: any touched path under packages/web/src/{components,routes} or
# packages/desktop counts as a UI change and requires screenshots.
UI_CHANGED=0
if echo "$CHANGED" | grep -qE '^(packages/web/src/(components|routes)|packages/desktop)/'; then
UI_CHANGED=1
fi
# Commits since base — useful for the "What" section.
COMMITS=$(git log --pretty=format:'- %s' "$MERGE_BASE"..HEAD)
# Tests touched?
TESTS_TOUCHED=$(echo "$CHANGED" | grep -E '(\.test\.|/test/|/e2e/)' || true)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
draft() {
cat <<EOF
<!--
Auto-drafted by scripts/pr-evidence.sh from the diff vs ${BASE_REF}.
Fill in the prose sections; the file lists and checklist are pre-populated.
-->
## Why
<!-- The problem this solves, in 13 sentences. What pain or gap does this close? -->
## What
$(if [ -n "$COMMITS" ]; then printf 'Commits on this branch:\n%s\n' "$COMMITS"; else echo '<!-- describe the change -->'; fi)
$(if [ -n "$ADDED" ]; then printf '\n**Added:**\n'; printf '%s\n' "$ADDED" | sed 's/^/- /'; fi)
$(if [ -n "$MODIFIED" ]; then printf '\n**Modified:**\n'; printf '%s\n' "$MODIFIED" | sed 's/^/- /'; fi)
$(if [ -n "$DELETED" ]; then printf '\n**Deleted:**\n'; printf '%s\n' "$DELETED" | sed 's/^/- /'; fi)
## Screenshots
EOF
if [ $UI_CHANGED -eq 1 ]; then
cat <<EOF
**Required** — this PR touches packages/web/src/{components,routes} or packages/desktop.
Commit screenshots under \`docs/screenshots/<feature-slug>/\` and reference here:
\`\`\`markdown
![Description](https://raw.githubusercontent.com/BenSheridanEdwards/openconcho/${BRANCH}/docs/screenshots/<feature-slug>/01-<state>.png)
\`\`\`
See \`.claude/rules/workflows.md\`"Open a PR" for capture + commit guidance.
EOF
else
cat <<EOF
<!-- No packages/web/src/{components,routes} or packages/desktop paths touched —
screenshots not strictly required. Delete this section if truly docs-only. -->
EOF
fi
cat <<EOF
## QA checklist
- [ ] \`pnpm typecheck\` clean locally
- [ ] \`pnpm lint\` clean locally
- [ ] \`pnpm test\` green locally
$(if [ -n "$TESTS_TOUCHED" ]; then echo '- [x] Tests touched on this branch:'; printf '%s\n' "$TESTS_TOUCHED" | sed 's/^/ - /'; else echo '- [ ] Tests added for new behaviour (or note why none are needed)'; fi)
- [ ] Manual verification: <!-- which Honcho instance, which workspace/peer, what you clicked, what you saw -->
$(if echo "$CHANGED" | grep -qE '^packages/desktop/'; then echo '- [ ] \`pnpm --filter @openconcho/desktop cargo-check\` passes'; fi)
- [x] Worked in a git worktree (current branch: \`${BRANCH}\`)
## Out-of-scope
<!-- What was intentionally left out and why. -->
## Notes
<!-- Caveats, follow-ups, anything reviewers should know. -->
EOF
}
if [ "$OUTPUT" = "-" ] || [ -t 1 ]; then
# When piped or first arg is "-", write to stdout.
if [ "${1:-}" = "-" ]; then
draft
exit 0
fi
fi
draft > "$OUTPUT"
echo "✓ Drafted PR body → ${OUTPUT}"
echo " Open it, fill in Why / Manual verification / Out-of-scope / Notes, then use as the PR body."