|
2 | 2 | set -euo pipefail |
3 | 3 | IFS=$'\n\t' |
4 | 4 |
|
| 5 | +# Mode: "validate" (default, exits non-zero on human edit) or "detect" (outputs to GITHUB_OUTPUT) |
| 6 | +MODE="${MODE:-validate}" |
5 | 7 | ALLOWED_BOTS="${ALLOWED_BOTS:-github-actions[bot],dependabot[bot]}" |
6 | 8 |
|
7 | 9 | # Determine the comparison range |
|
24 | 26 | COMPARE_RANGE="HEAD~1..HEAD" |
25 | 27 | fi |
26 | 28 |
|
27 | | -# If requirements.txt changed in comparison range, ensure latest change's commit was authored by an allowed bot, or the latest commit message exactly matches the canonical bot commit message, or fallback to any bot-authored commit |
28 | | -if git diff --name-only $COMPARE_RANGE | grep -q "^requirements.txt$"; then |
29 | | - # Get commits touching requirements.txt in the range |
30 | | - commits=$(git log --pretty=format:'%H' $COMPARE_RANGE -- requirements.txt || true) |
31 | | - |
32 | | - if [ -z "$commits" ]; then |
33 | | - echo "::error::No commits found touching requirements.txt in range $COMPARE_RANGE" |
34 | | - exit 1 |
| 29 | +# Check if requirements.txt changed |
| 30 | +if ! git diff --name-only $COMPARE_RANGE | grep -q "^requirements.txt$"; then |
| 31 | + echo "'requirements.txt' unchanged" |
| 32 | + if [ "$MODE" = "detect" ]; then |
| 33 | + echo "human_edit=false" >> "${GITHUB_OUTPUT:-/dev/stdout}" |
35 | 34 | fi |
| 35 | + exit 0 |
| 36 | +fi |
36 | 37 |
|
37 | | - # Build a grep-friendly regex from comma-separated allowed bots |
38 | | - allowed_regex=$(echo "$ALLOWED_BOTS" | sed 's/,/\\|/g') |
| 38 | +# Get latest commit that touched requirements.txt |
| 39 | +latest_sha=$(git log -1 --pretty=format:'%H' $COMPARE_RANGE -- requirements.txt || true) |
39 | 40 |
|
40 | | - # Read canonical commit message first line if available |
41 | | - canonical_msg="" |
42 | | - if [ -n "${COMMIT_MSG_FILE:-}" ] && [ -f "$COMMIT_MSG_FILE" ]; then |
43 | | - canonical_msg=$(sed -n '1p' "$COMMIT_MSG_FILE" | tr -d '\r') |
| 41 | +if [ -z "$latest_sha" ]; then |
| 42 | + echo "::error::No commits found touching requirements.txt in range $COMPARE_RANGE" |
| 43 | + if [ "$MODE" = "detect" ]; then |
| 44 | + echo "human_edit=false" >> "${GITHUB_OUTPUT:-/dev/stdout}" |
44 | 45 | fi |
| 46 | + exit 1 |
| 47 | +fi |
| 48 | + |
| 49 | +latest_author=$(git show -s --format='%an' "$latest_sha") |
| 50 | +latest_committer=$(git show -s --format='%cn' "$latest_sha") |
| 51 | +latest_message=$(git show -s --format='%B' "$latest_sha") |
| 52 | +latest_subject=$(echo "$latest_message" | head -n1 | sed -e 's/[[:space:]]*$//') |
45 | 53 |
|
46 | | - # Short check: report any commit touching requirements.txt in the range that is not authored by an allowed bot and does not exactly match the canonical bot commit message (first line) |
47 | | - offending=$(git log $COMPARE_RANGE --pretty=format:'%H|%an|%s' -- requirements.txt | |
48 | | - while IFS='|' read -r sha author subject; do |
49 | | - if echo "$author" | grep -qE "^($allowed_regex)$"; then |
50 | | - continue |
51 | | - fi |
52 | | - if [ -n "$canonical_msg" ] && [ "$subject" = "$canonical_msg" ]; then |
53 | | - continue |
54 | | - fi |
55 | | - printf '%s|%s|%s\n' "$sha" "$author" "$subject" |
56 | | - break |
57 | | - done) |
| 54 | +# Build a grep-friendly regex from comma-separated allowed bots |
| 55 | +allowed_regex=$(echo "$ALLOWED_BOTS" | sed 's/,/\\|/g') |
58 | 56 |
|
59 | | - if [ -z "$offending" ]; then |
60 | | - echo "All commits touching requirements.txt in the range are from allowed bots or canonical bot messages: OK" |
| 57 | +# Check 1: author or committer is allowed bot |
| 58 | +if echo "$latest_author" | grep -qE "^($allowed_regex)$" || echo "$latest_committer" | grep -qE "^($allowed_regex)$"; then |
| 59 | + echo "Latest change by allowed bot: OK" |
| 60 | + if [ "$MODE" = "detect" ]; then |
| 61 | + echo "human_edit=false" >> "${GITHUB_OUTPUT:-/dev/stdout}" |
| 62 | + fi |
| 63 | + exit 0 |
| 64 | +fi |
| 65 | + |
| 66 | +# Check 2: commit message exactly matches canonical message |
| 67 | +if [ -n "${COMMIT_MSG_FILE:-}" ] && [ -f "$COMMIT_MSG_FILE" ]; then |
| 68 | + canonical_msg=$(sed -n '1p' "$COMMIT_MSG_FILE" | tr -d '\r') |
| 69 | + if [ "$latest_subject" = "$canonical_msg" ]; then |
| 70 | + echo "Latest commit message exactly matches canonical bot message: OK" |
| 71 | + if [ "$MODE" = "detect" ]; then |
| 72 | + echo "human_edit=false" >> "${GITHUB_OUTPUT:-/dev/stdout}" |
| 73 | + fi |
61 | 74 | exit 0 |
62 | 75 | fi |
| 76 | +fi |
63 | 77 |
|
| 78 | +# Human edit detected |
| 79 | +if [ "$MODE" = "detect" ]; then |
| 80 | + echo "human_edit=true" >> "${GITHUB_OUTPUT:-/dev/stdout}" |
| 81 | + echo "offender_author=$latest_author" >> "${GITHUB_OUTPUT:-/dev/stdout}" |
| 82 | + echo "offender_subject=$latest_subject" >> "${GITHUB_OUTPUT:-/dev/stdout}" |
| 83 | + echo "Human edit detected" |
| 84 | + exit 0 |
| 85 | +else |
64 | 86 | echo "::error::You may NOT edit 'requirements.txt'" |
65 | 87 | echo "::warning::Undo your changes to requirements.txt, so robot can maintain it." |
66 | 88 | echo "::notice::To pin dependencies, use 'poetry add <package-name>'." |
67 | | - echo "Offending commit(s):" |
68 | | - echo "$offending" | sed 's/^/ /' |
| 89 | + echo "Latest commit: $latest_sha" |
| 90 | + echo "Latest author: $latest_author" |
| 91 | + echo "Latest committer: $latest_committer" |
| 92 | + echo "Latest message: $latest_subject" |
69 | 93 | exit 1 |
70 | 94 | fi |
71 | | -
|
72 | | -echo "'requirements.txt' unchanged (or latest change by allowed bot/marker)" |
|
0 commit comments