1414 default : false
1515
1616permissions :
17+ contents : write
1718 pull-requests : write
1819
1920jobs :
@@ -29,46 +30,79 @@ jobs:
2930 GH_TOKEN : ${{ github.token }}
3031 run : |
3132 READY_PRS=()
32- echo "Finding PRs with all CI checks passing..."
33+ echo "Finding PRs with all CI checks passing and L1 traceability ..."
3334
3435 PR_LIST=$(gh pr list --state open --limit 50 --json number --jq '.[].number' 2>/dev/null || echo "")
3536
3637 if [ -z "$PR_LIST" ]; then
3738 echo "No open PRs found"
3839 echo "ready_prs=" >> $GITHUB_OUTPUT
3940 echo "count=0" >> $GITHUB_OUTPUT
41+ echo "skipped=0" >> $GITHUB_OUTPUT
4042 exit 0
4143 fi
4244
45+ SKIPPED=0
46+ L1_RE='(Closes?|Fixes?|Resolves?|Refs?|Updates?)\s*#[0-9]+'
47+
4348 for pr in $PR_LIST; do
44- MAIN_CHECKS=$(gh pr view "$pr" --json statusCheckRollup --jq \
45- '[.statusCheckRollup[] | select(.name != "NotebookLM Auto-Sync" and .name != ".github/workflows/notebook-sync.yml")]' 2>/dev/null || echo "[]")
49+ echo "Evaluating PR #$pr..."
50+
51+ # L1 TRACEABILITY: require an issue reference in title or body.
52+ PR_METADATA=$(gh pr view "$pr" --json title,body,reviews,statusCheckRollup 2>/dev/null || echo "")
53+ if [ -z "$PR_METADATA" ]; then
54+ echo " Skipped: cannot fetch PR metadata"
55+ SKIPPED=$((SKIPPED + 1))
56+ continue
57+ fi
58+
59+ TITLE=$(echo "$PR_METADATA" | jq -r '.title // empty')
60+ BODY=$(echo "$PR_METADATA" | jq -r '.body // empty')
61+ if ! echo "$TITLE
62+ $BODY" | grep -qiE "$L1_RE"; then
63+ echo " Skipped : missing L1 TRACEABILITY issue reference"
64+ SKIPPED=$((SKIPPED + 1))
65+ continue
66+ fi
67+
68+ # Review gate: require at least one APPROVED review.
69+ APPROVED_REVIEWS=$(echo "$PR_METADATA" | jq '[.reviews[]? | select(.state == "APPROVED")] | length')
70+ if [ "$APPROVED_REVIEWS" = "0" ]; then
71+ echo " Skipped : no approved review"
72+ SKIPPED=$((SKIPPED + 1))
73+ continue
74+ fi
4675
47- FAILING=$(echo "$MAIN_CHECKS" | jq '[.[] | select(.conclusion != "SUCCESS" and .conclusion != "SKIPPED")] | length' 2>/dev/null || echo "1")
76+ MAIN_CHECKS=$(echo "$PR_METADATA" | jq \
77+ ' [.statusCheckRollup[]? | select(.name != "NotebookLM Auto-Sync" and .name != ".github/workflows/notebook-sync.yml")]' )
78+ FAILING=$(echo "$MAIN_CHECKS" | jq '[.[] | select(.conclusion != "SUCCESS" and .conclusion != "SKIPPED")] | length')
4879
49- echo "PR #$pr : $FAILING failing checks "
80+ echo " Failing checks : $FAILING | Approved reviews: $APPROVED_REVIEWS "
5081
5182 if [ "$FAILING" = "0" ]; then
5283 READY_PRS+=("$pr")
5384 echo " Ready to merge"
5485 else
55- echo " Skipped ($FAILING failing)"
86+ echo " Skipped ($FAILING failing checks)"
87+ SKIPPED=$((SKIPPED + 1))
5688 fi
5789 done
5890
5991 echo "ready_prs=${READY_PRS[*]}" >> $GITHUB_OUTPUT
6092 echo "count=${#READY_PRS[@]}" >> $GITHUB_OUTPUT
93+ echo "skipped=$SKIPPED" >> $GITHUB_OUTPUT
6194
6295 - name : Dry Run Check
63- if : inputs.dry_run == 'true'
96+ if : inputs.dry_run
6497 run : |
6598 echo "DRY RUN - No actual merges will occur"
6699 echo "Ready PRs: ${{ steps.find-ready.outputs.count }}"
100+ echo "Skipped PRs: ${{ steps.find-ready.outputs.skipped }}"
67101 echo "PRs: ${{ steps.find-ready.outputs.ready_prs }}"
68102 exit 0
69103
70104 - name : Merge Ready PRs
71- if : steps.find-ready.outputs.count != '0'
105+ if : ${{ steps.find-ready.outputs.count != '0' && !inputs.dry_run }}
72106 env :
73107 GH_TOKEN : ${{ github.token }}
74108 run : |
84118 run : |
85119 echo "## Summary"
86120 echo ""
87- echo "**PRs processed:** ${{ steps.find-ready.outputs.count }}"
121+ echo "**PRs merged/ready:** ${{ steps.find-ready.outputs.count }}"
122+ echo "**PRs skipped:** ${{ steps.find-ready.outputs.skipped }}"
0 commit comments