Skip to content

Commit c32ccc1

Browse files
authored
Merge branch 'main' into fix/complete-script-injection-hardening
2 parents d839083 + a940f77 commit c32ccc1

9 files changed

Lines changed: 464 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Features
6+
7+
- 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]`.
8+
- 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.
9+
- Add validate-pr composite action for validating non-maintainer PRs against contribution guidelines ([#153](https://github.com/getsentry/github-workflows/pull/153))
10+
11+
### Fixes
12+
13+
- Updater - Trigger CI for new PRs without changelog updates ([#166](https://github.com/getsentry/github-workflows/pull/166))
14+
- Updater - Select the first branch when multiple branches point at `HEAD` ([#165](https://github.com/getsentry/github-workflows/pull/165))
15+
16+
### Dependencies
17+
18+
- Bump Danger JS from v13.0.4 to v13.0.5 ([#160](https://github.com/getsentry/github-workflows/pull/160))
19+
- [changelog](https://github.com/danger/danger-js/blob/main/CHANGELOG.md#1305)
20+
- [diff](https://github.com/danger/danger-js/compare/13.0.4...13.0.5)
21+
322
## 3.3.0
423

524
### Features

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ Runs DangerJS on Pull Requests with a pre-configured set of rules.
1616

1717
**[📖 View full documentation →](danger/README.md)**
1818

19+
### Validate PR
20+
21+
Validates non-maintainer PRs against contribution guidelines and enforces draft status.
22+
23+
**[📖 View full documentation →](validate-pr/README.md)**
24+
1925
## Legacy Reusable Workflows (v2)
2026

2127
> ⚠️ **Deprecated**: Reusable workflows have been converted to composite actions in v3. Please migrate to the composite actions above.

danger/danger.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
version=13.0.4
1+
version=13.0.5
22
repo=https://github.com/danger/danger-js

updater/action.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,3 +479,32 @@ runs:
479479
Auto-generated by a [dependency updater](https://github.com/getsentry/github-workflows/blob/main/updater/action.yml).
480480
${{ env.TARGET_CHANGELOG }}
481481
labels: dependencies
482+
483+
- name: Trigger PR workflows
484+
if: ${{ (inputs.changelog-entry != 'true') && ( steps.create-pr.outputs.pull-request-operation == 'created' ) && ( steps.update.outputs.pull-request-head-sha == '' || steps.update.outputs.pull-request-head-sha == steps.create-pr.outputs.pull-request-head-sha ) }}
485+
shell: bash
486+
working-directory: caller-repo
487+
env:
488+
PR_BRANCH: ${{ steps.root.outputs.prBranch }}
489+
PR_HEAD_SHA: ${{ steps.create-pr.outputs.pull-request-head-sha }}
490+
API_TOKEN: ${{ inputs.api-token }}
491+
SSH_KEY: ${{ inputs.ssh-key }}
492+
run: |
493+
git fetch origin "$PR_BRANCH"
494+
git checkout --force -B "$PR_BRANCH" "origin/$PR_BRANCH"
495+
496+
current_head=$(git rev-parse HEAD)
497+
if [ "$current_head" != "$PR_HEAD_SHA" ]; then
498+
echo "::notice::PR branch changed from $PR_HEAD_SHA to $current_head; skipping synthetic CI trigger."
499+
exit 0
500+
fi
501+
502+
git config user.name "github-actions[bot]"
503+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
504+
git commit --amend --no-edit
505+
506+
if [ -n "$API_TOKEN" ] && [ -z "$SSH_KEY" ]; then
507+
git remote set-url origin "https://x-access-token:${API_TOKEN}@github.com/${{ github.repository }}.git"
508+
fi
509+
510+
git push --force-with-lease="refs/heads/$PR_BRANCH:$PR_HEAD_SHA" origin "HEAD:refs/heads/$PR_BRANCH"

updater/scripts/update-dependency.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ if ("$Tag" -eq '') {
162162
if ("$headRef" -eq '') {
163163
throw "Couldn't determine repository head (no ref returned by ls-remote HEAD"
164164
}
165-
$mainBranch = (git ls-remote --heads $url | Where-Object { $_.StartsWith($headRef) }) -replace '.*\srefs/heads/', ''
165+
$mainBranch = (git ls-remote --heads $url | Where-Object { $_.StartsWith($headRef) } | Select-Object -First 1) -replace '.*\srefs/heads/', ''
166166
}
167167

168168
$url = $url -replace '\.git$', ''

updater/tests/update-dependency.Tests.ps1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,9 @@ switch ($action)
220220
$output | Should -Contain 'latestTag=0.28.0'
221221
$output | Should -Contain 'latestTagNice=v0.28.0'
222222
$output | Should -Contain 'url=https://github.com/getsentry/sentry-cli'
223-
$output | Should -Contain 'mainBranch=master'
223+
# sentry-cli has both `main` and `master`; which one is reported depends on
224+
# which currently points at HEAD upstream, so accept either.
225+
($output | Where-Object { $_ -like 'mainBranch=*' }) | Should -BeIn @('mainBranch=main', 'mainBranch=master')
224226
}
225227
}
226228

validate-pr/README.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Validate PR
2+
3+
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.**
4+
5+
## What it does
6+
7+
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.
8+
9+
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.
10+
11+
## Usage
12+
13+
Create `.github/workflows/validate-pr.yml` in your repository:
14+
15+
```yaml
16+
name: Validate PR
17+
18+
on:
19+
pull_request_target:
20+
types: [opened]
21+
22+
jobs:
23+
validate-pr:
24+
runs-on: ubuntu-24.04
25+
permissions:
26+
pull-requests: write
27+
steps:
28+
- uses: getsentry/github-workflows/validate-pr@<sha>
29+
with:
30+
app-id: ${{ vars.SDK_MAINTAINER_BOT_APP_ID }}
31+
private-key: ${{ secrets.SDK_MAINTAINER_BOT_PRIVATE_KEY }}
32+
```
33+
34+
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.
35+
36+
## Inputs
37+
38+
| Input | Required | Description |
39+
|-------|----------|-------------|
40+
| `app-id` | Yes | GitHub App ID for the SDK Maintainer Bot |
41+
| `private-key` | Yes | GitHub App private key for the SDK Maintainer Bot |
42+
43+
## Validation rules
44+
45+
### Skipped entirely
46+
47+
- PR author is in the trusted-bot allowlist (Dependabot, Renovate, Codecov AI, etc.)
48+
- PR author has `admin`, `maintain`, `push`, or `write` access on the repo
49+
- PR has fewer than 100 lines changed (`additions + deletions`), excluding common lock files
50+
51+
### Lock files excluded from line counts
52+
53+
Matched by basename, case-insensitive:
54+
55+
| Ecosystem | File |
56+
|-----------|------|
57+
| Rust | `Cargo.lock` |
58+
| JS | `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml` |
59+
| Python | `Pipfile.lock`, `poetry.lock`, `uv.lock` |
60+
| Ruby | `Gemfile.lock` |
61+
| PHP | `composer.lock` |
62+
| Go | `go.sum` (`go.mod` is hand-edited and stays counted) |
63+
| Elixir | `mix.lock` |
64+
| Dart/Flutter | `pubspec.lock` |
65+
| .NET/NuGet | `packages.lock.json` |
66+
| CocoaPods | `Podfile.lock` |
67+
| Nix | `flake.lock` |
68+
69+
### Issue reference check
70+
71+
For PRs that reach validation, the action scans the PR body for issue references in these formats:
72+
73+
- `#123` (same-repo)
74+
- `getsentry/repo#123` (cross-repo)
75+
- `https://github.com/getsentry/repo/issues/123` (full URL)
76+
- With optional keywords: `Fixes #123`, `Closes getsentry/repo#123`, etc.
77+
78+
The PR is considered compliant if **any** referenced issue passes all of:
79+
80+
- The issue is fetchable and in a `getsentry` repository
81+
- If the issue has assignees, the PR author is one of them
82+
- Both the PR author and a maintainer have participated in the issue discussion
83+
84+
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.
85+
86+
## Updating from earlier revisions
87+
88+
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.
89+
90+
To update an existing consumer:
91+
92+
- Bump the pinned commit SHA to the latest on `main`.
93+
- Change `types: [opened, reopened]` → `types: [opened]`.
94+
- Remove any code that reads the `was-closed` output (it no longer exists).
95+
96+
Existing labels on old PRs are not removed automatically. Clean them up with a one-off script if desired.

validate-pr/action.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: 'Validate PR'
2+
description: 'Validates non-maintainer PRs against contribution guidelines'
3+
author: 'Sentry'
4+
5+
inputs:
6+
app-id:
7+
description: 'GitHub App ID for the SDK Maintainer Bot'
8+
required: true
9+
private-key:
10+
description: 'GitHub App private key for the SDK Maintainer Bot'
11+
required: true
12+
13+
runs:
14+
using: 'composite'
15+
steps:
16+
- name: Generate GitHub App token
17+
id: app-token
18+
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v2
19+
with:
20+
app-id: ${{ inputs.app-id }}
21+
private-key: ${{ inputs.private-key }}
22+
23+
- name: Validate PR
24+
id: validate
25+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
26+
env:
27+
APP_SLUG: ${{ steps.app-token.outputs.app-slug }}
28+
with:
29+
github-token: ${{ steps.app-token.outputs.token }}
30+
script: |
31+
const script = require('${{ github.action_path }}/scripts/validate-pr.js');
32+
await script({ github, context, core });

0 commit comments

Comments
 (0)