Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,9 @@

### Features

- Add validate-pr composite action for validating non-maintainer PRs against contribution guidelines and enforcing draft status ([#153](https://github.com/getsentry/github-workflows/pull/153))
- Validate PR - Action is advisory: it posts a single friendly comment on community PRs that don't reference an issue with maintainer discussion. PRs are not closed and no labels are applied. Recommended trigger is `types: [opened]`.
- Validate PR - Skip validation for PRs with fewer than 100 lines changed, excluding common lock files (`Cargo.lock`, `yarn.lock`, `package-lock.json`, `Pipfile.lock`, etc.). Tiny PRs no longer go through the issue-discussion loop.
- Add validate-pr composite action for validating non-maintainer PRs against contribution guidelines ([#153](https://github.com/getsentry/github-workflows/pull/153))

## 3.3.0

Expand Down
64 changes: 45 additions & 19 deletions validate-pr/README.md
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
# Validate PR

Validates non-maintainer pull requests against contribution guidelines.
Advisory validation for non-maintainer pull requests against contribution guidelines. Posts a single friendly comment when a PR doesn't reference an issue with prior maintainer discussion. **PRs are never closed, and no labels are applied.**

## What it does

**Validates issue references** — Non-maintainer PRs must reference a GitHub issue where the PR author and a maintainer have discussed the approach. PRs that don't meet this requirement are automatically closed with a descriptive comment.
For PRs from non-maintainer authors, the action checks that the PR body references a GitHub issue where the PR author and a maintainer have discussed the approach. When that's not the case, the action posts one short advisory comment inviting the contributor to start with an issue. Maintainers (`admin` or `maintain` role) and a hard-coded list of trusted bots are exempt.

Maintainers (users with `admin` or `maintain` role) are exempt from validation. When a maintainer reopens a previously closed PR, all checks are skipped — this allows maintainers to override the action's decision.
Small PRs (< 100 lines changed, excluding lock files) are skipped entirely — typo fixes and tiny bug fixes don't need to go through the issue-discussion loop.

## Usage

Expand All@@ -17,54 +17,80 @@ name: Validate PR

on:
pull_request_target:
types: [opened, reopened]
types: [opened]

jobs:
validate-pr:
runs-on: ubuntu-24.04
permissions:
pull-requests: write
steps:
- uses: getsentry/github-workflows/validate-pr@v3
- uses: getsentry/github-workflows/validate-pr@<sha>
with:
app-id: ${{ vars.SDK_MAINTAINER_BOT_APP_ID }}
private-key: ${{ secrets.SDK_MAINTAINER_BOT_PRIVATE_KEY }}
```

Pin to a specific commit SHA (consumers in `getsentry/*` already follow this convention). The `pull-requests: write` permission is needed because the action posts comments on the PR.

## Inputs

| Input | Required | Description |
|-------|----------|-------------|
| `app-id` | Yes | GitHub App ID for the SDK Maintainer Bot |
| `private-key` | Yes | GitHub App private key for the SDK Maintainer Bot |

## Outputs
## Validation rules

| Output | Description |
|--------|-------------|
| `was-closed` | `'true'` if the PR was closed by validation, unset otherwise |
### Skipped entirely

## Validation rules
- PR author is in the trusted-bot allowlist (Dependabot, Renovate, Codecov AI, etc.)
- PR author has `admin`, `maintain`, `push`, or `write` access on the repo
- PR has fewer than 100 lines changed (`additions + deletions`), excluding common lock files

### Lock files excluded from line counts

Matched by basename, case-insensitive:

| Ecosystem | File |
|-----------|------|
| Rust | `Cargo.lock` |
| JS | `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml` |
| Python | `Pipfile.lock`, `poetry.lock`, `uv.lock` |
| Ruby | `Gemfile.lock` |
| PHP | `composer.lock` |
| Go | `go.sum` (`go.mod` is hand-edited and stays counted) |
| Elixir | `mix.lock` |
| Dart/Flutter | `pubspec.lock` |
| .NET/NuGet | `packages.lock.json` |
| CocoaPods | `Podfile.lock` |
| Nix | `flake.lock` |

### Issue reference check

The PR body is scanned for issue references in these formats:
For PRs that reach validation, the action scans the PR body for issue references in these formats:

- `#123` (same-repo)
- `getsentry/repo#123` (cross-repo)
- `https://github.com/getsentry/repo/issues/123` (full URL)
- With optional keywords: `Fixes #123`, `Closes getsentry/repo#123`, etc.

A PR is valid if **any** referenced issue passes all checks:
The PR is considered compliant if **any** referenced issue passes all of:

- The issue is fetchable and in a `getsentry` repository
- If the issue has assignees, the PR author must be one of them
- If the issue has assignees, the PR author is one of them
- Both the PR author and a maintainer have participated in the issue discussion

## Labels
If no referenced issue passes, the action posts one advisory comment. The PR remains open and reviewable; no labels or status checks are applied. The comment is idempotent — workflow re-runs on the same PR will not produce duplicates.

## Updating from earlier revisions

Earlier revisions of this action closed non-compliant PRs and applied labels (`violating-contribution-guidelines`, `missing-issue-reference`, `missing-maintainer-discussion`, `issue-already-assigned`). The current version does neither — it only posts a comment.

To update an existing consumer:

The action creates these labels automatically (they don't need to exist beforehand):
- Bump the pinned commit SHA to the latest on `main`.
- Change `types: [opened, reopened]` → `types: [opened]`.
- Remove any code that reads the `was-closed` output (it no longer exists).

- `violating-contribution-guidelines` — added to all closed PRs
- `missing-issue-reference` — PR body has no issue references
- `missing-maintainer-discussion` — referenced issue lacks author + maintainer discussion
- `issue-already-assigned` — referenced issue is assigned to someone else
Existing labels on old PRs are not removed automatically. Clean them up with a one-off script if desired.
7 changes: 2 additions & 5 deletions validate-pr/action.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,11 +10,6 @@ inputs:
description: 'GitHub App private key for the SDK Maintainer Bot'
required: true

outputs:
was-closed:
description: 'Whether the PR was closed by the validation step'
value: ${{ steps.validate.outputs.was-closed }}

runs:
using: 'composite'
steps:
Expand All@@ -28,6 +23,8 @@ runs:
- name: Validate PR
id: validate
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
env:
APP_SLUG: ${{ steps.app-token.outputs.app-slug }}
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
Expand Down
Loading
Loading