Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
40b3320
feat(ci): add optional resolved message parameter to manage-pr-commen…
rafaeltonholo Jul 8, 2026
791c847
docs: update commit message guide with CI enforcement and new types
rafaeltonholo Jul 8, 2026
0ab06da
docs: update contribution workflow with PR sentinel requirements
rafaeltonholo Jul 8, 2026
6c9bc71
chore: update PR template with sentinel requirements and improved str…
rafaeltonholo Jul 8, 2026
a3156cb
feat(ci): add PR Sentinel check functions for PR and commit validation
rafaeltonholo Jul 8, 2026
9cfdd94
test(ci): add test suite for PR Sentinel check functions
rafaeltonholo Jul 8, 2026
04f308c
feat(ci): add shared PR Sentinel library for comment and label orches…
rafaeltonholo Jul 8, 2026
35622f5
feat(ci): add PR evaluation script with mandatory checks validation
rafaeltonholo Jul 8, 2026
5533cf8
feat(ci): add PR Sentinel report script with automated enforcement
rafaeltonholo Jul 8, 2026
30f7a19
feat(ci): add PR Sentinel escalation script with auto-close logic
rafaeltonholo Jul 8, 2026
9f53c46
test(ci): add tests for dry-run mode in PR Sentinel gh wrapper
rafaeltonholo Jul 8, 2026
7678220
test(ci): add test runner for PR Sentinel test suites
rafaeltonholo Jul 8, 2026
232264d
feat(ci): add PR Sentinel workflow with automated validation and enfo…
rafaeltonholo Jul 8, 2026
10c5e97
feat(ci): add PR Sentinel workflow with automated validation and enfo…
rafaeltonholo Jul 8, 2026
30268fd
chore: add markdown code formatting to PR template example
rafaeltonholo Jul 8, 2026
65206fc
fix(ci): strip markdown code blocks from linked issue check
rafaeltonholo Jul 8, 2026
c9d7bcd
chore(ci): remove backticks from PR Sentinel check error messages
rafaeltonholo Jul 9, 2026
9c54b08
feat(ci): add ready-for-review label and mutual exclusivity to Sentin…
rafaeltonholo Jul 9, 2026
f001245
feat(ci): support "Part of #N" as valid issue link in PR
rafaeltonholo Jul 9, 2026
eb43208
fix(ci): rename require_label to require_label_needs_updates in autoc…
rafaeltonholo Jul 9, 2026
b03df90
feat(ci): use GitHub App token for PR Sentinel workflows
rafaeltonholo Jul 9, 2026
acd0ed1
chore(ci): add safety flags to pr-sentinel lib.sh
rafaeltonholo Jul 10, 2026
812774f
chore(ci): remove perf type from commit message validation
rafaeltonholo Jul 10, 2026
7d2ada0
chore(ci): remove build and ci types from allowed commit message types
rafaeltonholo Jul 10, 2026
411332a
chore(ci): remove shellcheck directive comments from pr-sentinel scripts
rafaeltonholo Jul 10, 2026
ff78851
chore(ci): add safety flags to pr-sentinel checks.sh
rafaeltonholo Jul 10, 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
32 changes: 21 additions & 11 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,33 @@
> You can help us by categorizing your pull request with labels.
> We appreciate you working with us and will get to reviewing your contribution as soon as we can!

As always, thank you for the contribution!

### \~\~After reading, delete this line and the above to use the template for your pull request\~\~
> [!NOTE]
> **Automated PR Sentinel checks (required):** a linked issue, a Conventional Commit title, Conventional Commit
> messages on every commit (no `Co-authored-by:` trailers), no merge commits (rebase instead), and a completed
> **AI Disclosure**. PRs that stay non-compliant are **auto-closed 3 days** after the first Sentinel notice.
> See the [Contribution Workflow](https://github.com/thunderbird/thunderbird-android/blob/main/docs/contributing/contribution-workflow.md#-automated-pr-checks-pr-sentinel).

As always, thank you for the contribution!

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

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

#### Description
RFC / Technical Design (if applicable): _<Remove if not applicable>_

### Description

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

#### Screen Shots
### Screenshots

_<If your pull request makes any UI changes, include screenshots displaying those changes here; Remove section if not applicable>_

### Testing

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

## AI Disclosure

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

## Contribution Checklist

- [ ] 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/)
- [ ] 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/)
- [ ] This contribution is in Kotlin where possible
- [ ] This contribution does not use merge commits
- [ ] 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).
- [ ] This contribution does not break existing unit tests (run `gradlew testDebugUnitTest`; will be checked by CI).
- [ ] This contribution includes tests for any new functionality, and maintains tests for any updated functionality.
- [ ] This contribution includes tests for any new functionality and maintains tests for any updated functionality.
- [ ] This contribution adheres to our [Engineering process](https://github.com/thunderbird/thunderbird-android/tree/main/docs/engineering) (RFC/Technical Design/ADR)
- [ ] 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_).
- [ ] 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_).
2 changes: 1 addition & 1 deletion .github/workflows/pr-request-report-labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@ jobs:
PR_NUMBER: ${{ github.event.pull_request.number }}
IDENTIFIER: "<!-- pr-report-label-validation-comment -->"
run: |
./scripts/ci/manage-pr-comment.sh "$PR_NUMBER" "$IDENTIFIER" "" "valid"
./scripts/ci/manage-pr-comment.sh "$PR_NUMBER" "$IDENTIFIER" "" "valid" "✅ **Validation Passed**: All report and feature-flag labels are correctly set."
66 changes: 66 additions & 0 deletions .github/workflows/pr-sentinel-autoclose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
name: PR - Sentinel Auto-Close

on:
schedule:
- cron: "0 0 * * *" # 00:00 GMT/UTC daily
workflow_dispatch:
inputs:
dry_run:
description: "Log escalate/close actions without making changes."
type: boolean
default: false

permissions:
contents: read
pull-requests: write
issues: write

jobs:
autoclose:
name: Escalate and close stale non-compliant PRs
runs-on: ubuntu-latest
timeout-minutes: 15
environment: botmobile
env:
GH_REPO: ${{ github.repository }}
steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- name: App token generate
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
if: ${{ vars.BOT_CLIENT_ID }}
id: app-token
with:
client-id: ${{ vars.BOT_CLIENT_ID }}
private-key: ${{ secrets.BOT_PRIVATE_KEY }}

- name: Assert Sentinel label exists (fail loud)
env:
GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
run: |
set -euo pipefail
# shellcheck source=scripts/ci/pr-sentinel/lib.sh disable=SC1091
Comment thread
rafaeltonholo marked this conversation as resolved.
source ./scripts/ci/pr-sentinel/lib.sh
require_label_needs_updates

- name: Escalate / close labeled PRs
env:
GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
DRY_RUN_INPUT: ${{ github.event.inputs.dry_run }}
run: |
set -euo pipefail
args=()
if [[ "${DRY_RUN_INPUT:-false}" == "true" ]]; then
args+=(--dry-run)
fi
numbers="$(gh pr list --state open --label "pr-sentinel: needs updates" \
--json number --jq 'map(.number) | join(" ")')"
if [[ -z "$numbers" ]]; then
echo "No labeled PRs; nothing to do."
exit 0
fi
for pr in $numbers; do
./scripts/ci/pr-sentinel/escalate-pr.sh "$pr" "${args[@]}"
done
88 changes: 88 additions & 0 deletions .github/workflows/pr-sentinel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
name: PR - Sentinel Bot

# Warning, this job runs on pull_request_target and therefore has access to issue content.
# Don't add any steps that act on external (PR-head) code.
on:
pull_request_target:
types:
- opened
- reopened
- synchronize
- edited
- ready_for_review
- converted_to_draft
branches:
- main
workflow_dispatch:
inputs:
pr_number:
description: "Single PR to evaluate; leave blank to evaluate all open PRs."
required: false
default: ""
dry_run:
description: "Log actions without posting comments, changing labels, or closing PRs."
type: boolean
default: false

permissions:
contents: read
pull-requests: write
issues: write

concurrency:
group: pr-sentinel-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

jobs:
sentinel:
name: PR Sentinel
runs-on: ubuntu-latest
timeout-minutes: 10
environment: botmobile
env:
GH_REPO: ${{ github.repository }}
steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- name: App token generate
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
if: ${{ vars.BOT_CLIENT_ID }}
id: app-token
with:
client-id: ${{ vars.BOT_CLIENT_ID }}
private-key: ${{ secrets.BOT_PRIVATE_KEY }}

- name: Determine target PRs
id: prs
env:
GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
EVENT_NAME: ${{ github.event_name }}
EVENT_PR: ${{ github.event.pull_request.number }}
INPUT_PR: ${{ github.event.inputs.pr_number }}
run: |
set -euo pipefail
if [[ "$EVENT_NAME" == "pull_request_target" ]]; then
numbers="$EVENT_PR"
elif [[ -n "$INPUT_PR" ]]; then
numbers="$INPUT_PR"
else
numbers="$(gh pr list --state open --json number --jq 'map(.number) | join(" ")')"
fi
echo "numbers=${numbers}" >> "$GITHUB_OUTPUT"

- name: Evaluate and report
env:
GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
NUMBERS: ${{ steps.prs.outputs.numbers }}
DRY_RUN_INPUT: ${{ github.event.inputs.dry_run }}
run: |
set -euo pipefail
args=()
if [[ "${DRY_RUN_INPUT:-false}" == "true" ]]; then args+=(--dry-run); fi
overall=0
for pr in $NUMBERS; do
./scripts/ci/pr-sentinel/report-pr.sh "$pr" "${args[@]}" || overall=1
done
exit "$overall"
71 changes: 66 additions & 5 deletions docs/contributing/contribution-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,14 @@ To submit your changes for review:
- Base branch: `main`
- Head repo: your fork & branch
4. Select your fork and branch as the source
5. Click **Create pull request**
5. Make sure your [Pull Request Description](https://github.com/thunderbird/thunderbird-android/blob/main/docs/contributing-workflow.md#pull-request-description)
is compliant with our guidelines
6. Click **Create pull request**

### Pull Request Description

Before start writing the description, read the Pull Request template that will be prompted for you.

Write a clear and concise description for your pull request:

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

Example:
#### Mandatory fields

When writing your Pull Request Description, you must fill all the mandatory fields:

- **Linked Issue/Ticket**
- 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)
for available keywords.
- **Description**
- **AI Disclosure**
- Must check at least one of the checkboxes
- **Contribution Checklist**
- Must follow all the instructions of the checklist and then mark as completed by checking all.

An automated PR Sentinel will be checking if you are following the requirements and will flag the PR if not.
See [Automated PR checks (PR Sentinel)](https://github.com/thunderbird/thunderbird-android/blob/main/docs/contributing/contribution-workflow.md#-automated-pr-checks-pr-sentinel)
for more details.

#### Example

```markdown
## Contribution Summary

Linked Issue/Ticket: #123
Linked Issue/Ticket: Closes #123
RFC / Technical Design (if applicable):

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

#### Screen Shots
#### Screenshots

[Screenshot of error message]
[Screenshot of the error message]

#### Testing

1. Enter an invalid email (e.g., "test@")
2. Verify that an error message appears
3. Enter a valid email
4. Verify that the error message disappears

## AI Disclosure

Select **one** of the following (mandatory)

- [x] This contribution does not include any changes created or assisted by AI.
- [ ] This contribution includes changes assisted by AI.
- [ ] This contribution includes changes created by AI.

## Contribution Checklist

- [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/)
- [x] This contribution is in Kotlin where possible
- [x] This contribution does not use merge commits
- [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).
- [x] This contribution does not break existing unit tests (run `gradlew testDebugUnitTest`; will be checked by CI).
- [x] This contribution includes tests for any new functionality, and maintains tests for any updated functionality.
- [x] This contribution adheres to our [Engineering process](https://github.com/thunderbird/thunderbird-android/tree/main/docs/engineering) (RFC/Technical Design/ADR)
- [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_).

```

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

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

### 🤖 Automated PR checks (PR Sentinel)

Every PR is validated automatically to verify if the PR is ready for review. To be reviewable it must have:

1. A **linked issue** in the description (e.g. `Closes #123`).
2. A **Conventional Commit title** (see the [Git Commit Guide](git-commit-guide.md)).
3. **Conventional Commit messages** on every commit, with **no `Co-authored-by:` trailers**.
4. A completed **AI Disclosure** section (exactly one option selected).
5. **No merge commits** — rebase onto the base branch instead of merging.

When something is missing, PR Sentinel posts a single comment listing it and labels the PR
`pr-sentinel: needs updates`. If it stays unresolved, the bot posts a closing warning after **1 day**
and **auto-closes the PR after 3 days**.

Push a fix (or edit the description), and the comment is removed, the `pr-sentinel: needs updates`
label is swapped for **`pr-sentinel: ready for review`**, and the check turns green.

**Draft PRs are skipped** until marked ready for review, and **bot PRs** (Dependabot, Renovate, …) are exempt.
Everyone else — **including maintainers and members** — must comply.

## 🔄 Keeping Your Fork Updated

To keep your fork in sync with the main repository:
Expand Down
27 changes: 18 additions & 9 deletions docs/contributing/git-commit-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ This makes your work easier to review, track, and maintain for everyone involved

## ✍️ Commit Message Format

> [!IMPORTANT]
> **Enforced in CI:**
> - 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)).
> - The `<description>` must be 70 characters or fewer.
>
> See [Automated PR checks (PR Sentinel)](https://github.com/thunderbird/thunderbird-android/blob/main/docs/contributing/contribution-workflow.md#-automated-pr-checks-pr-sentinel)
> for more details.

```git
<type>(<scope>): <description>

Expand All @@ -17,7 +25,7 @@ Components:

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

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

| Scope | Use for... | Example |
|------------|----------------|------------------------------------------|
| `auth` | Authentication | `feat(auth): add login functionality` |
| `settings` | User settings | `feat(settings): add dark mode toggle` |
| `build` | Build system | `fix(build): improve build performance` |
| `ui` | UI/theme | `refactor(ui): split theme into modules` |
| `deps` | Dependencies | `chore(deps): bump Kotlin to 2.0.0` |
| Scope | Use for... | Example |
|------------|------------------|------------------------------------------|
| `auth` | Authentication | `feat(auth): add login functionality` |
| `settings` | User settings | `feat(settings): add dark mode toggle` |
| `build` | Build system | `fix(build): improve build performance` |
| `ci` | CI configuration | `chore(ci): add PR Sentinel workflow` |
| `ui` | UI/theme | `refactor(ui): split theme into modules` |
| `deps` | Dependencies | `chore(deps): bump Kotlin to 2.0.0` |

## 🧠 Best Practices

### 1. One Commit, One Purpose

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

### 2. Keep It Manageable
Expand Down
Loading
Loading