name: PR template guard on: pull_request_target: types: [opened, reopened, edited, synchronize] permissions: contents: read pull-requests: write issues: write jobs: check: runs-on: ubuntu-latest if: > github.repository == 'ikhsanddy/pixies' && !endsWith(github.event.pull_request.user.login, '[bot]') steps: - name: Check pull request body id: check env: BODY: ${{ github.event.pull_request.body }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} REPO: ${{ github.repository }} BASE_SHA: ${{ github.event.pull_request.base.sha }} run: | strip() { tr -d '\r' | sed -e 's///g' -e '//d'; } printf '%s' "$BODY" | strip > body.md gh api "repos/$REPO/contents/.github/PULL_REQUEST_TEMPLATE.md?ref=$BASE_SHA" \ --jq '.content' | base64 -d | strip > template.md section() { awk -v want="$2" ' /^#{1,6} / { keep = (index(tolower($0), tolower(want)) > 0); next } keep { print } ' "$1" } items() { section "$1" "$2" \ | grep -E '^[[:space:]]*- \[[ xX]\]' \ | sed -E -e 's/^[[:space:]]*- \[[ xX]\][[:space:]]*//' -e 's/[[:space:]]+$//' \ | tr -s ' ' | tr '[:upper:]' '[:lower:]' \ | grep -v 'other:' | sort || true } headings() { grep -E '^#{1,6} ' "$1" \ | sed -E -e 's/^#{1,6}[[:space:]]*//' -e 's/[[:space:]]+$//' \ | tr -s ' ' | tr '[:upper:]' '[:lower:]' | sort || true } problems="" add() { problems="${problems}- $1"$'\n'; } tampered() { items template.md "$1" > canonical.txt items body.md "$1" > given.txt [ -n "$(comm -3 canonical.txt given.txt || true)" ] } if ! grep -qi '^#\{1,6\} .*Changes made to' body.md || ! grep -qi '^#\{1,6\} .*Checklist' body.md; then add "the pull request template was not used" else headings template.md > canonical_headings.txt headings body.md > given_headings.txt if [ -n "$(comm -3 canonical_headings.txt given_headings.txt || true)" ]; then add "the section headings were edited, please leave them as they are" fi if grep -qi '^#\{1,6\} .*Description' body.md; then description=$(section body.md "Description" | grep -v '^\s*$' || true) if [ -z "$description" ]; then add "the description is empty" fi fi if tampered "Changes made to"; then add "the options under \"Changes made to ...\" were edited, please leave them as they are" elif ! section body.md "Changes made to" | grep -qi '^\s*- \[x\]'; then add "no option is selected under \"Changes made to ...\"" fi if tampered "Checklist"; then add "the checklist was edited, please leave the items as they are" elif section body.md "Checklist" | grep -q '^\s*- \[ \]'; then add "not every item in the checklist is checked" fi fi { echo "problems<> "$GITHUB_OUTPUT" - name: Update comment env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.number }} REPO: ${{ github.repository }} AUTHOR: ${{ github.event.pull_request.user.login }} PROBLEMS: ${{ steps.check.outputs.problems }} MARKER: "" run: | comment_id=$(gh api --paginate "repos/$REPO/issues/$PR/comments" \ --jq ".[] | select(.body | contains(\"$MARKER\")) | .id" | head -n1) if [ -z "$PROBLEMS" ]; then if [ -n "$comment_id" ]; then gh api -X DELETE "repos/$REPO/issues/comments/$comment_id" fi gh pr edit "$PR" --repo "$REPO" --remove-label "incomplete template" || true exit 0 fi { echo "$MARKER" echo "Hey @$AUTHOR, thanks for the pull request!" echo echo "Before we can review it, could you complete the pull request template? Right now:" echo printf '%s' "$PROBLEMS" echo echo "You can edit the description at any time, this comment disappears once everything is filled in." } > comment.md if [ -n "$comment_id" ]; then gh api -X PATCH "repos/$REPO/issues/comments/$comment_id" -F body=@comment.md >/dev/null else gh pr comment "$PR" --repo "$REPO" --body-file comment.md fi gh pr edit "$PR" --repo "$REPO" --add-label "incomplete template" || true