Skip to content

Commit d52aa73

Browse files
authored
chore(ci): add PR Sentinel compliance checks and grace-period auto-close (#11254)
2 parents d3e48c6 + de13f93 commit d52aa73

19 files changed

Lines changed: 1187 additions & 30 deletions

‎.github/pull_request_template.md‎

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,33 @@
1313
> You can help us by categorizing your pull request with labels.
1414
> We appreciate you working with us and will get to reviewing your contribution as soon as we can!
1515
16-
As always, thank you for the contribution!
17-
18-
### \~\~After reading, delete this line and the above to use the template for your pull request\~\~
16+
> [!NOTE]
17+
> **Automated PR Sentinel checks (required):** a linked issue, a Conventional Commit title, Conventional Commit
18+
> messages on every commit (no `Co-authored-by:` trailers), no merge commits (rebase instead), and a completed
19+
> **AI Disclosure**. PRs that stay non-compliant are **auto-closed 3 days** after the first Sentinel notice.
20+
> See the [Contribution Workflow](https://github.com/thunderbird/thunderbird-android/blob/main/docs/contributing/contribution-workflow.md#-automated-pr-checks-pr-sentinel).
1921
22+
As always, thank you for the contribution!
2023

24+
# \~\~After reading, delete this line and the above to use the template for your pull request\~\~
25+
-------------------------------------------------------------------------------------------------
2126
## Contribution Summary
2227

23-
Linked Issue/Ticket:
24-
RFC / Technical Design (if applicable):
28+
Linked Issue/Ticket: _<Please include the issue number with the closing keyword, e.g. `Closes #1234`>_
2529

26-
#### Description
30+
RFC / Technical Design (if applicable): _<Remove if not applicable>_
31+
32+
### Description
2733

2834
_<Please provide a detailed description of your contribution here>_
2935

30-
#### Screen Shots
36+
### Screenshots
37+
38+
_<If your pull request makes any UI changes, include screenshots displaying those changes here; Remove section if not applicable>_
39+
40+
### Testing
3141

32-
_<If your pull request makes any UI changes, include screenshots displaying those changes here>_
42+
_<Please provide the steps to reproduce the issue, so we can review it properly>_
3343

3444
## AI Disclosure
3545

@@ -41,11 +51,11 @@ Select **one** of the following (mandatory)
4151

4252
## Contribution Checklist
4353

44-
- [ ] I have read and affirm that my contribution adheres to [Mozilla’s Community Participation Guidelines](https://www.mozilla.org/en-US/about/governance/policies/participation/)
54+
- [ ] I have read, and I affirm that my contribution adheres to [Mozilla’s Community Participation Guidelines](https://www.mozilla.org/en-US/about/governance/policies/participation/)
4555
- [ ] This contribution is in Kotlin where possible
4656
- [ ] This contribution does not use merge commits
4757
- [ ] This contribution adheres to the existing codestyle (run `gradlew spotlessCheck` to check and `gradlew spotlessApply` to format your source code; will be checked by CI).
4858
- [ ] This contribution does not break existing unit tests (run `gradlew testDebugUnitTest`; will be checked by CI).
49-
- [ ] This contribution includes tests for any new functionality, and maintains tests for any updated functionality.
59+
- [ ] This contribution includes tests for any new functionality and maintains tests for any updated functionality.
5060
- [ ] This contribution adheres to our [Engineering process](https://github.com/thunderbird/thunderbird-android/tree/main/docs/engineering) (RFC/Technical Design/ADR)
51-
- [ ] This PR has a descriptive title and body that accurately outlines all changes made, and contains a reference to any issues that it fixes (e.g. _Closes #XXX_ or _Fixes #XXX_).
61+
- [ ] This PR has a descriptive title and body that accurately outlines all changes made and contains a reference to any issues that it fixes (e.g. _Closes #XXX_ or _Fixes #XXX_).

‎.github/workflows/pr-request-report-labels.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,4 @@ jobs:
5454
PR_NUMBER: ${{ github.event.pull_request.number }}
5555
IDENTIFIER: "<!-- pr-report-label-validation-comment -->"
5656
run: |
57-
./scripts/ci/manage-pr-comment.sh "$PR_NUMBER" "$IDENTIFIER" "" "valid"
57+
./scripts/ci/manage-pr-comment.sh "$PR_NUMBER" "$IDENTIFIER" "" "valid" "✅ **Validation Passed**: All report and feature-flag labels are correctly set."
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
name: PR - Sentinel Auto-Close
3+
4+
on:
5+
schedule:
6+
- cron: "0 0 * * *" # 00:00 GMT/UTC daily
7+
workflow_dispatch:
8+
inputs:
9+
dry_run:
10+
description: "Log escalate/close actions without making changes."
11+
type: boolean
12+
default: false
13+
14+
permissions:
15+
contents: read
16+
pull-requests: write
17+
issues: write
18+
19+
jobs:
20+
autoclose:
21+
name: Escalate and close stale non-compliant PRs
22+
if: github.repository == 'thunderbird/thunderbird-android'
23+
runs-on: ubuntu-latest
24+
timeout-minutes: 15
25+
environment: botmobile
26+
env:
27+
GH_REPO: ${{ github.repository }}
28+
steps:
29+
- name: Checkout code
30+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
31+
with:
32+
ref: ${{ github.event.repository.default_branch }}
33+
persist-credentials: false
34+
35+
- name: App token generate
36+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
37+
id: app-token
38+
with:
39+
client-id: ${{ vars.BOT_CLIENT_ID }}
40+
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
41+
42+
- name: Assert Sentinel label exists (fail loud)
43+
env:
44+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
45+
run: |
46+
set -euo pipefail
47+
# shellcheck source=scripts/ci/pr-sentinel/lib.sh disable=SC1091
48+
source ./scripts/ci/pr-sentinel/lib.sh
49+
require_label_needs_updates
50+
51+
- name: Escalate / close labeled PRs
52+
env:
53+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
54+
DRY_RUN_INPUT: ${{ github.event.inputs.dry_run }}
55+
run: |
56+
set -euo pipefail
57+
args=()
58+
if [[ "${DRY_RUN_INPUT:-false}" == "true" ]]; then
59+
args+=(--dry-run)
60+
fi
61+
numbers="$(gh pr list --state open --label "pr-sentinel: needs updates" \
62+
--json number --jq 'map(.number) | join(" ")')"
63+
if [[ -z "$numbers" ]]; then
64+
echo "No labeled PRs; nothing to do."
65+
exit 0
66+
fi
67+
for pr in $numbers; do
68+
./scripts/ci/pr-sentinel/escalate-pr.sh "$pr" "${args[@]}"
69+
done

‎.github/workflows/pr-sentinel.yml‎

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---
2+
name: PR - Sentinel Bot
3+
4+
# Warning, this job runs on pull_request_target and therefore has access to issue content.
5+
# Don't add any steps that act on external (PR-head) code.
6+
on:
7+
pull_request_target:
8+
types:
9+
- opened
10+
- reopened
11+
- synchronize
12+
- edited
13+
- ready_for_review
14+
- converted_to_draft
15+
branches:
16+
- main
17+
workflow_dispatch:
18+
inputs:
19+
pr_number:
20+
description: "Single PR to evaluate; leave blank to evaluate all open PRs."
21+
required: false
22+
default: ""
23+
dry_run:
24+
description: "Log actions without posting comments, changing labels, or closing PRs."
25+
type: boolean
26+
default: false
27+
28+
permissions:
29+
contents: read
30+
pull-requests: write
31+
issues: write
32+
33+
concurrency:
34+
group: pr-sentinel-${{ github.event.pull_request.number || github.run_id }}
35+
cancel-in-progress: true
36+
37+
jobs:
38+
sentinel:
39+
name: PR Sentinel
40+
if: github.repository == 'thunderbird/thunderbird-android'
41+
runs-on: ubuntu-latest
42+
timeout-minutes: 10
43+
environment: botmobile
44+
env:
45+
GH_REPO: ${{ github.repository }}
46+
steps:
47+
- name: Checkout code
48+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
49+
with:
50+
ref: ${{ github.event.repository.default_branch }}
51+
persist-credentials: false
52+
53+
- name: App token generate
54+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
55+
id: app-token
56+
with:
57+
client-id: ${{ vars.BOT_CLIENT_ID }}
58+
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
59+
60+
- name: Determine target PRs
61+
id: prs
62+
env:
63+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
64+
EVENT_NAME: ${{ github.event_name }}
65+
EVENT_PR: ${{ github.event.pull_request.number }}
66+
INPUT_PR: ${{ github.event.inputs.pr_number }}
67+
run: |
68+
set -euo pipefail
69+
if [[ "$EVENT_NAME" == "pull_request_target" ]]; then
70+
numbers="$EVENT_PR"
71+
elif [[ -n "$INPUT_PR" ]]; then
72+
numbers="$INPUT_PR"
73+
else
74+
numbers="$(gh pr list --state open --json number --jq 'map(.number) | join(" ")')"
75+
fi
76+
echo "numbers=${numbers}" >> "$GITHUB_OUTPUT"
77+
78+
- name: Evaluate and report
79+
env:
80+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
81+
NUMBERS: ${{ steps.prs.outputs.numbers }}
82+
DRY_RUN_INPUT: ${{ github.event.inputs.dry_run }}
83+
run: |
84+
set -euo pipefail
85+
args=()
86+
if [[ "${DRY_RUN_INPUT:-false}" == "true" ]]; then args+=(--dry-run); fi
87+
overall=0
88+
for pr in $NUMBERS; do
89+
./scripts/ci/pr-sentinel/report-pr.sh "$pr" "${args[@]}" || overall=1
90+
done
91+
exit "$overall"

‎docs/contributing/contribution-workflow.md‎

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,14 @@ To submit your changes for review:
213213
- Base branch: `main`
214214
- Head repo: your fork & branch
215215
4. Select your fork and branch as the source
216-
5. Click **Create pull request**
216+
5. Make sure your [Pull Request Description](https://github.com/thunderbird/thunderbird-android/blob/main/docs/contributing-workflow.md#pull-request-description)
217+
is compliant with our guidelines
218+
6. Click **Create pull request**
217219

218220
### Pull Request Description
219221

222+
Before start writing the description, read the Pull Request template that will be prompted for you.
223+
220224
Write a clear and concise description for your pull request:
221225

222226
1. Reference the issue number (e.g., "Fixes #123", "Resolves #456")
@@ -225,12 +229,29 @@ Write a clear and concise description for your pull request:
225229
4. Include screenshots or videos for UI changes
226230
5. Mention any related issues or pull requests
227231

228-
Example:
232+
#### Mandatory fields
233+
234+
When writing your Pull Request Description, you must fill all the mandatory fields:
235+
236+
- **Linked Issue/Ticket**
237+
- Must use closing keywords, e.g. Closes, Fixes, Resolves, etc. See [Linking a pull request to an issue using a keyword](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword)
238+
for available keywords.
239+
- **Description**
240+
- **AI Disclosure**
241+
- Must check at least one of the checkboxes
242+
- **Contribution Checklist**
243+
- Must follow all the instructions of the checklist and then mark as completed by checking all.
244+
245+
An automated PR Sentinel will be checking if you are following the requirements and will flag the PR if not.
246+
See [Automated PR checks (PR Sentinel)](https://github.com/thunderbird/thunderbird-android/blob/main/docs/contributing/contribution-workflow.md#-automated-pr-checks-pr-sentinel)
247+
for more details.
248+
249+
#### Example
229250

230251
```markdown
231252
## Contribution Summary
232253

233-
Linked Issue/Ticket: #123
254+
Linked Issue/Ticket: Closes #123
234255
RFC / Technical Design (if applicable):
235256

236257
#### Description
@@ -240,16 +261,36 @@ This PR adds email validation to the login form. It:
240261
- Shows error messages for invalid emails
241262
- Adds unit tests for the validation logic
242263

243-
#### Screen Shots
264+
#### Screenshots
244265

245-
[Screenshot of error message]
266+
[Screenshot of the error message]
246267

247268
#### Testing
248269

249270
1. Enter an invalid email (e.g., "test@")
250271
2. Verify that an error message appears
251272
3. Enter a valid email
252273
4. Verify that the error message disappears
274+
275+
## AI Disclosure
276+
277+
Select **one** of the following (mandatory)
278+
279+
- [x] This contribution does not include any changes created or assisted by AI.
280+
- [ ] This contribution includes changes assisted by AI.
281+
- [ ] This contribution includes changes created by AI.
282+
283+
## Contribution Checklist
284+
285+
- [x] I have read, and I affirm that my contribution adheres to [Mozilla’s Community Participation Guidelines](https://www.mozilla.org/en-US/about/governance/policies/participation/)
286+
- [x] This contribution is in Kotlin where possible
287+
- [x] This contribution does not use merge commits
288+
- [x] This contribution adheres to the existing codestyle (run `gradlew spotlessCheck` to check and `gradlew spotlessApply` to format your source code; will be checked by CI).
289+
- [x] This contribution does not break existing unit tests (run `gradlew testDebugUnitTest`; will be checked by CI).
290+
- [x] This contribution includes tests for any new functionality, and maintains tests for any updated functionality.
291+
- [x] This contribution adheres to our [Engineering process](https://github.com/thunderbird/thunderbird-android/tree/main/docs/engineering) (RFC/Technical Design/ADR)
292+
- [x] This PR has a descriptive title and body that accurately outlines all changes made, and contains a reference to any issues that it fixes (e.g. _Closes #XXX_ or _Fixes #XXX_).
293+
253294
```
254295

255296
## 👀 Code Review Process
@@ -265,6 +306,26 @@ After submitting your pull request:
265306

266307
👉 For expectations and etiquette, see [Code Review Guide](code-review-guide.md).
267308

309+
### 🤖 Automated PR checks (PR Sentinel)
310+
311+
Every PR is validated automatically to verify if the PR is ready for review. To be reviewable it must have:
312+
313+
1. A **linked issue** in the description (e.g. `Closes #123`).
314+
2. A **Conventional Commit title** (see the [Git Commit Guide](git-commit-guide.md)).
315+
3. **Conventional Commit messages** on every commit, with **no `Co-authored-by:` trailers**.
316+
4. A completed **AI Disclosure** section (exactly one option selected).
317+
5. **No merge commits** — rebase onto the base branch instead of merging.
318+
319+
When something is missing, PR Sentinel posts a single comment listing it and labels the PR
320+
`pr-sentinel: needs updates`. If it stays unresolved, the bot posts a closing warning after **1 day**
321+
and **auto-closes the PR after 3 days**.
322+
323+
Push a fix (or edit the description), and the comment is removed, the `pr-sentinel: needs updates`
324+
label is swapped for **`pr-sentinel: ready for review`**, and the check turns green.
325+
326+
**Draft PRs are skipped** until marked ready for review, and **bot PRs** (Dependabot, Renovate, …) are exempt.
327+
Everyone else — **including maintainers and members** — must comply.
328+
268329
## 🔄 Keeping Your Fork Updated
269330

270331
To keep your fork in sync with the main repository:

‎docs/contributing/git-commit-guide.md‎

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ This makes your work easier to review, track, and maintain for everyone involved
55

66
## ✍️ Commit Message Format
77

8+
> [!IMPORTANT]
9+
> **Enforced in CI:**
10+
> - the PR title **and every commit subject** must match this format (checked by the [PR Sentinel workflow](https://github.com/thunderbird/thunderbird-android/blob/main/.github/workflows/pr-sentinel.yml)).
11+
> - The `<description>` must be 70 characters or fewer.
12+
>
13+
> See [Automated PR checks (PR Sentinel)](https://github.com/thunderbird/thunderbird-android/blob/main/docs/contributing/contribution-workflow.md#-automated-pr-checks-pr-sentinel)
14+
> for more details.
15+
816
```git
917
<type>(<scope>): <description>
1018
@@ -17,7 +25,7 @@ Components:
1725

1826
- `<type>`: The [type of change](#-commit-types) being made (e.g., feat, fix, docs).
1927
- `<scope>` **(optional)**: The [scope](#optional-scope) indicates the area of the codebase affected by the change (e.g., auth, ui).
20-
- `<description>`: Short description of the change (50 characters or less)
28+
- `<description>`: Short description of the change (70 characters or less)
2129
- `<body>` **(optional)**: Explain what changed and why, include context if helpful.
2230
- `<footer(s)>` **(optional)**: Include issue references, breaking changes, etc.
2331

@@ -63,20 +71,21 @@ Fixes #123
6371
The **scope** is optional but recommended for clarity, especially for large changes or or when multiple areas of the
6472
codebase are involved.
6573

66-
| Scope | Use for... | Example |
67-
|------------|----------------|------------------------------------------|
68-
| `auth` | Authentication | `feat(auth): add login functionality` |
69-
| `settings` | User settings | `feat(settings): add dark mode toggle` |
70-
| `build` | Build system | `fix(build): improve build performance` |
71-
| `ui` | UI/theme | `refactor(ui): split theme into modules` |
72-
| `deps` | Dependencies | `chore(deps): bump Kotlin to 2.0.0` |
74+
| Scope | Use for... | Example |
75+
|------------|------------------|------------------------------------------|
76+
| `auth` | Authentication | `feat(auth): add login functionality` |
77+
| `settings` | User settings | `feat(settings): add dark mode toggle` |
78+
| `build` | Build system | `fix(build): improve build performance` |
79+
| `ci` | CI configuration | `chore(ci): add PR Sentinel workflow` |
80+
| `ui` | UI/theme | `refactor(ui): split theme into modules` |
81+
| `deps` | Dependencies | `chore(deps): bump Kotlin to 2.0.0` |
7382

7483
## 🧠 Best Practices
7584

7685
### 1. One Commit, One Purpose
7786

7887
- ✅ Each commit should represent a single logical change or addition to the codebase.
79-
- ❌ Don’t mix unrelated changes together (e.g., fixing a bug and updating docs, or changing a style and )
88+
- ❌ Don’t mix unrelated changes together (e.g., fixing a bug and updating docs, or changing a style and
8089
adding a feature).
8190

8291
### 2. Keep It Manageable

0 commit comments

Comments
 (0)