Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
165e533
fix: bypass error, cuz req's edit was via merge
wesleyboar Jan 5, 2026
171fe24
Revert "fix: bypass error, cuz req's edit was via merge"
wesleyboar Jan 5, 2026
18f898a
refactor: [AI] compare author of req's change
wesleyboar Jan 5, 2026
5faa588
fix: [AI] compare author of req's change
wesleyboar Jan 5, 2026
5720cff
fix: [AI] missing fi
wesleyboar Jan 5, 2026
af5abcb
refactor: [AI] unify commit message file handling
wesleyboar Jan 5, 2026
9fc9c6c
test: add new line to poetry.lock
wesleyboar Jan 5, 2026
d5ff87f
fix: missing push
wesleyboar Jan 5, 2026
f5bb0f1
chore: auto-update requirements.txt [bot]
github-actions[bot] Jan 5, 2026
53e6149
test: add new line to requirements.txt
wesleyboar Jan 5, 2026
005c122
Merge branch 'refactor/requirements-sync-and-validation' of github.co…
wesleyboar Jan 5, 2026
e0506c0
chore: auto-update requirements.txt [bot]
github-actions[bot] Jan 5, 2026
df3057d
test: (again) add new line to requirements.txt
wesleyboar Jan 5, 2026
9722566
chore: auto-update requirements.txt [bot]
github-actions[bot] Jan 5, 2026
773685d
refactor: [AI] repair changes to validation logic
wesleyboar Jan 5, 2026
07e44cb
Merge branch 'refactor/requirements-sync-and-validation' of github.co…
wesleyboar Jan 5, 2026
67f433e
fix: [AI] prevent bot commit if human edit
wesleyboar Jan 5, 2026
31b7f29
test: (again) add new line to requirements.txt
wesleyboar Jan 5, 2026
d0d0ab6
Revert "test: (again) add new line to requirements.txt"
wesleyboar Jan 5, 2026
908938b
fix: [AI] don't reject drift unless there is diff
wesleyboar Jan 5, 2026
bc3a310
revert requirements.txt to main
wesleyboar Jan 5, 2026
e8ea33d
chore: auto-update requirements.txt [bot]
github-actions[bot] Jan 5, 2026
0fd6f03
test: (again) add new line to requirements.txt
wesleyboar Jan 5, 2026
ad379fc
fix: remove (failing) attempt to comment on PR
wesleyboar Jan 5, 2026
0329a39
chore: quote message
wesleyboar Jan 5, 2026
a97a2fd
Revert "test: (again) add new line to requirements.txt"
wesleyboar Jan 5, 2026
a17276a
revert requirements.txt to main
wesleyboar Jan 5, 2026
67a7721
chore: auto-update requirements.txt [bot]
github-actions[bot] Jan 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/actions/validate-requirements/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: "Validate requirements"

description: |
Reject direct edits to `requirements.txt` by humans: the action fails if
any commit touching the file in the compare range appears to be authored by a
human or otherwise not from an allowed bot (unless the commit message exactly
matches the canonical bot commit message).

inputs:
allowed_bots:
description: "Comma-separated list of allowed bot author names"
required: false
default: "github-actions[bot],dependabot[bot]"
commit_message_file:
description: "Path to file that contains canonical commit message (exact match)"
required: false
default: ".github/commit-messages/requirements_update.txt"

runs:
using: "composite"
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Run requirements check
shell: bash
env:
ALLOWED_BOTS: ${{ inputs.allowed_bots }}
COMMIT_MSG_FILE: ${{ inputs.commit_message_file }}
run: |
bash ./.github/actions/validate-requirements/check.sh
106 changes: 106 additions & 0 deletions .github/actions/validate-requirements/check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'

# Mode: "validate" (default, exits non-zero on human edit) or "detect" (outputs to GITHUB_OUTPUT)
MODE="${MODE:-validate}"
ALLOWED_BOTS="${ALLOWED_BOTS:-github-actions[bot],dependabot[bot]}"

# Determine the comparison range
is_pr=""
if [ "${GITHUB_EVENT_NAME:-}" = "pull_request" ]; then
is_pr=1
fi
has_base_ref=""
if [ -n "${GITHUB_BASE_REF:-}" ]; then
has_base_ref=1
fi
origin_base_ref_exists=""
if [ -n "${GITHUB_BASE_REF:-}" ] && git rev-parse --verify "origin/${GITHUB_BASE_REF}" >/dev/null 2>&1; then
origin_base_ref_exists=1
fi
if [ -n "$is_pr" ] && [ -n "$has_base_ref" ] && [ -n "$origin_base_ref_exists" ]; then
BASE_REF="$(git rev-parse "origin/${GITHUB_BASE_REF}")"
COMPARE_RANGE="$BASE_REF...HEAD"
else
COMPARE_RANGE="HEAD~1..HEAD"
fi

# Check if requirements.txt changed
if ! git diff --name-only $COMPARE_RANGE | grep -q "^requirements.txt$"; then
echo "'requirements.txt' unchanged"
if [ "$MODE" = "detect" ]; then
echo "is_human_edit=false" >> "${GITHUB_OUTPUT:-/dev/stdout}"
fi
exit 0
fi

# Check if requirements.txt differs from base (net change across all commits in range)
if [ -n "$is_pr" ] && [ -n "$has_base_ref" ] && [ -n "$origin_base_ref_exists" ]; then
BASE_REF_PARSED="origin/${GITHUB_BASE_REF}"
if git diff --quiet "$BASE_REF_PARSED" HEAD -- requirements.txt; then
echo "requirements.txt touched but matches base branch (likely reverted): OK"
if [ "$MODE" = "detect" ]; then
echo "is_human_edit=false" >> "${GITHUB_OUTPUT:-/dev/stdout}"
fi
exit 0
fi
fi

# Get latest commit that touched requirements.txt
latest_sha=$(git log -1 --pretty=format:'%H' $COMPARE_RANGE -- requirements.txt || true)

if [ -z "$latest_sha" ]; then
echo "::error::No commits found touching requirements.txt in range $COMPARE_RANGE"
if [ "$MODE" = "detect" ]; then
echo "is_human_edit=false" >> "${GITHUB_OUTPUT:-/dev/stdout}"
fi
exit 1
fi

latest_author=$(git show -s --format='%an' "$latest_sha")
latest_committer=$(git show -s --format='%cn' "$latest_sha")
latest_message=$(git show -s --format='%B' "$latest_sha")
latest_subject=$(echo "$latest_message" | head -n1 | sed -e 's/[[:space:]]*$//')

# Build a grep-friendly regex from comma-separated allowed bots
allowed_regex=$(echo "$ALLOWED_BOTS" | sed 's/,/\\|/g')

# Check 1: author or committer is allowed bot
if echo "$latest_author" | grep -qE "^($allowed_regex)$" || echo "$latest_committer" | grep -qE "^($allowed_regex)$"; then
echo "Latest change by allowed bot: OK"
if [ "$MODE" = "detect" ]; then
echo "is_human_edit=false" >> "${GITHUB_OUTPUT:-/dev/stdout}"
fi
exit 0
fi

# Check 2: commit message exactly matches canonical message
if [ -n "${COMMIT_MSG_FILE:-}" ] && [ -f "$COMMIT_MSG_FILE" ]; then
canonical_msg=$(sed -n '1p' "$COMMIT_MSG_FILE" | tr -d '\r')
if [ "$latest_subject" = "$canonical_msg" ]; then
echo "Latest commit message exactly matches canonical bot message: OK"
if [ "$MODE" = "detect" ]; then
echo "is_human_edit=false" >> "${GITHUB_OUTPUT:-/dev/stdout}"
fi
exit 0
fi
fi

# Human edit detected
if [ "$MODE" = "detect" ]; then
echo "is_human_edit=true" >> "${GITHUB_OUTPUT:-/dev/stdout}"
echo "offender_author=$latest_author" >> "${GITHUB_OUTPUT:-/dev/stdout}"
echo "offender_subject=$latest_subject" >> "${GITHUB_OUTPUT:-/dev/stdout}"
echo "Human edit detected"
exit 0
else
echo "::error::You may NOT edit 'requirements.txt'"
echo "::warning::Undo your changes to requirements.txt, so robot can maintain it."
echo "::notice::To pin dependencies, use 'poetry add <package-name>'."
echo "Latest commit: $latest_sha"
echo "Latest author: $latest_author"
echo "Latest committer: $latest_committer"
echo "Latest message: \"$latest_subject\""
exit 1
fi
1 change: 1 addition & 0 deletions .github/commit-messages/requirements_update.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
chore: auto-update requirements.txt [bot]
26 changes: 7 additions & 19 deletions .github/workflows/requirements-validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ on:
jobs:
reject-requirements-drift:
runs-on: ubuntu-latest
env:
COMMIT_MSG_FILE: .github/commit-messages/requirements_update.txt

# Skip if the last commit was from the bot (prevent unnecessary check)
if: github.event.head_commit.author.name != 'github-actions[bot]'
Expand All @@ -19,23 +21,9 @@ jobs:
with:
fetch-depth: 0 # full history

- name: Check if requirements.txt was modified unexpectedly
run: |
# For PRs, check against base branch
# For pushes, check last commit
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE_REF="${{ github.event.pull_request.base.sha }}"
COMPARE_RANGE="$BASE_REF...HEAD"
else
COMPARE_RANGE="HEAD~1..HEAD"
fi

# If requirements.txt modified in that range
if git diff --name-only $COMPARE_RANGE | grep -q "^requirements.txt$"; then
echo "::error::You may NOT edit 'requirements.txt'"
echo "::warning::Undo your changes to requirements.txt, so robot can maintain it."
echo "::notice::To pin dependencies, use 'poetry add <package-name>'."
exit 1
fi
- name: Validate requirements
uses: ./.github/actions/validate-requirements
with:
allowed_bots: 'github-actions[bot],dependabot[bot]'
commit_message_file: ${{ env.COMMIT_MSG_FILE }}

echo "'requirements.txt' unchanged (or only changed by bot)"
36 changes: 34 additions & 2 deletions .github/workflows/requirments-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,37 @@ jobs:
echo "has_change=true" >> $GITHUB_OUTPUT
fi

commit-requirements-delta:
prevent-bot-commit-if-human-edit:
runs-on: ubuntu-latest
needs: detect-requirements-delta
if: needs.detect-requirements-delta.outputs.has_change == 'true'
outputs:
is_human_edit: ${{ steps.check.outputs.is_human_edit }}
offender_author: ${{ steps.check.outputs.offender_author }}
offender_subject: ${{ steps.check.outputs.offender_subject }}
env:
ALLOWED_BOTS: 'github-actions[bot],dependabot[bot]'
COMMIT_MSG_FILE: .github/commit-messages/requirements_update.txt

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref || github.ref_name }}
fetch-depth: 0

- name: Check for human edits
id: check
env:
MODE: detect
run: bash ./.github/actions/validate-requirements/check.sh

commit-requirements-delta:
runs-on: ubuntu-latest
needs: [detect-requirements-delta, prevent-bot-commit-if-human-edit]
if: needs.detect-requirements-delta.outputs.has_change == 'true' && needs.prevent-bot-commit-if-human-edit.outputs.is_human_edit == 'false'
env:
COMMIT_MSG_FILE: .github/commit-messages/requirements_update.txt

steps:
- name: Checkout code
Expand Down Expand Up @@ -83,6 +110,11 @@ jobs:
if git diff --staged --quiet; then
echo "No changes to requirements.txt"
else
git commit -m "chore: auto-update requirements.txt [bot]"
commit_msg=$(sed -n '1p' "$COMMIT_MSG_FILE" 2>/dev/null | tr -d '\r')
if [ -z "$commit_msg" ]; then
echo "::error::Missing or empty canonical commit message file: $COMMIT_MSG_FILE"
exit 1
fi
git commit -m "$commit_msg"
git push
fi
24 changes: 8 additions & 16 deletions .github/workflows/validate-requirements.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,18 @@ on:
jobs:
check-requirements:
runs-on: ubuntu-latest
env:
COMMIT_MSG_FILE: .github/commit-messages/requirements_update.txt

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 2
fetch-depth: 0

- name: Check if requirements.txt was modified unexpectedly
run: |
# Get author of last commit
AUTHOR=$(git log -1 --pretty=format:'%an')

# Check if requirements.txt was modified in last commit
if git diff --name-only HEAD~1 HEAD | grep -q "^requirements.txt$"; then
if [ "$AUTHOR" != "github-actions[bot]" ]; then
echo "❌ ERROR: You may NOT edit `requirements.txt`"
echo "To pin dependencies, use `poetry add <package-name>`."
echo "Please remove your changes to requirements.txt, so the robot can maintain it."
exit 1
fi
fi
- name: Validate requirements
uses: ./.github/actions/validate-requirements
with:
allowed_bots: 'github-actions[bot],dependabot[bot]'
commit_message_file: ${{ env.COMMIT_MSG_FILE }}

echo "✅ SUCCESS: `requirements.txt` not modified unexpectedly"
1 change: 1 addition & 0 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@ mkdocs-exclude-search==0.6.6 ; python_version >= "3.10" and python_version < "3.
mkdocs-include-markdown-plugin==5.1.0 ; python_version >= "3.10" and python_version < "3.13" \
--hash=sha256:4a1b8d79a0e1b6fd357ca8013a6d1701c755ada4acb74ee97b0642d1afe6756e \
--hash=sha256:e9ca188ab1d86f5fc4a6b96ce8c85acf6e25f114897868041056ec7945f29f65
mkdocs-tacc==1.0.0 ; python_version >= "3.10" and python_version < "3.13" \
--hash=sha256:5d9f1d4a4b871526f74e92bda8eb52584ece817d1eef5d4064ef40fe6adcf99d \
--hash=sha256:cbd107eab1ff1659bc164c84f17055f367097a0b3dfe2ec3b41ef34850f7181c
mkdocs-tacc==1.0.1 ; python_version >= "3.10" and python_version < "3.13" \
--hash=sha256:08b8e0b1ab5bdcdfea493c2f18077723b36569afdd71a23a5d097fb7942799ea \
--hash=sha256:f14ac8d3833bb43447cdb91210f6179e85fe5bef92f7d948ac9c50e41ec8e6c0
mkdocs==1.4.3 ; python_version >= "3.10" and python_version < "3.13" \
--hash=sha256:5955093bbd4dd2e9403c5afaf57324ad8b04f16886512a3ee6ef828956481c57 \
--hash=sha256:6ee46d309bda331aac915cd24aab882c179a933bd9e77b80ce7d2eaaa3f689dd
Expand Down