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
10 changes: 8 additions & 2 deletions .github/workflows/testing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,15 @@ jobs:
DRY_RUN: 'true'
AUTOMATION_ONLY: 'false'

# Failure testing is also important
# Failure testing is also important.
#
# Skipped for pull requests raised from a fork: the approval gate
# stops those before input validation is reached, so the step
# above exits successfully having transferred nothing. That is
# the gate working, not the failure case under test.
- name: "Error if step above did NOT fail"
if: steps.failure.outcome == 'success'
# yamllint disable-line rule:line-length
if: steps.failure.outcome == 'success' && github.event.pull_request.head.repo.full_name == github.repository
shell: bash
run: |
# Error if step above did NOT fail
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ name: github2gerrit
on:
pull_request_target:
types: [opened, reopened, edited, synchronize, closed]
# Re-runs when a maintainer approves a fork pull request
pull_request_review:
types: [submitted, dismissed]
Comment thread
ModeSevenIndustrialSolutions marked this conversation as resolved.
push:
branches: [main, master]
workflow_dispatch:
Expand Down Expand Up @@ -140,6 +143,9 @@ name: github2gerrit
on:
pull_request_target:
types: [opened, reopened, edited, synchronize, closed]
# Re-runs when a maintainer approves a fork pull request
pull_request_review:
types: [submitted, dismissed]
workflow_dispatch:

permissions:
Expand Down
84 changes: 81 additions & 3 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,9 +400,87 @@ Same-repository pull requests keep their previous behaviour and still read

### Approval gating

The tool does not yet require a maintainer approval before it transfers a fork
pull request to Gerrit. Until it does, use `AUTOMATION_ONLY` (see above) to
control which pull requests the tool accepts.
A fork pull request does not transfer to Gerrit until a maintainer approves it.
The check runs before the tool fetches anything from the pull request and before
it unlocks the Gerrit SSH key, so a pull request awaiting review reaches
neither.

Same-repository pull requests skip the check entirely. Pushing a branch to the
base repository already requires write access, and automation such as
Dependabot, pre-commit.ci and Copilot works that way, so the gate never affects
them.

#### What counts as approval

An approving review, where every one of the following holds:

- the reviewer's `author_association` falls in the trusted set, the same one
used for [comment commands](#who-may-issue-commands);
- the review targets the pull request's **current head commit**;
- no trusted reviewer has since requested changes; and
- the reviewer is not the pull request author.

A pull request whose provenance the tool cannot establish takes the same path as
a fork. Approval is a question about authority, and an absent signal is not an
answer.

The gate applies to unattended runs. Running the CLI directly against a pull
request URL does not require an approval: the operator chose that pull request
and is using their own credentials, so they are already the authority the gate
looks for. It exists for the automated path, where the tool acts on a shared
identity with nobody watching.

Binding to the head commit matters. GitHub keeps approvals across pushes unless
branch protection dismisses them, so an approval that was not checked against a
commit would let a contributor gain approval for one revision and then push a
different one. The cost is that maintainers re-approve after each push, which is
the correct trade.

The author exclusion is belt and braces. GitHub already rejects approving your
own pull request, and that guarantee is a large part of why the tool uses
reviews rather than comment directives — on a Gerrit mirror the pull request
author often holds the same organization membership as the reviewers.

#### Enabling re-runs on approval

`pull_request_target` carries no event for a submitted review, so approving a
pull request does nothing unless the calling workflow listens for it:

```yaml
on:
pull_request_target:
types: [opened, reopened, edited, synchronize, closed]
pull_request_review:
types: [submitted, dismissed]
```
Comment thread
ModeSevenIndustrialSolutions marked this conversation as resolved.

Without that trigger the tool only reconsiders a pull request on its next
push, or when a maintainer dispatches the workflow by hand.

#### When the gate blocks

The run finishes successfully rather than failing. A pull request waiting for a
human is not broken, and a red check would suggest otherwise.

The tool posts one comment explaining what is missing, and edits that same
comment on later runs rather than adding another. Where an approval exists but
covers an earlier commit, the comment says so, so a maintainer who did approve
is not told that nobody has.

Once approval arrives, the tool edits that comment again to record it, so a
transferred pull request does not keep displaying a stale block.

#### If the head moves mid-run

The gate reads the head commit from the API, while the workspace fetch reads
`refs/pull/<N>/head`, which the contributor can move meanwhile. The tool
compares the commit it fetched against the commit the maintainer approved and
refuses a mismatch, so a push timed against a running workflow cannot slip an
unreviewed commit into Gerrit.

That refusal fails the run, unlike the ordinary blocked case, because it falls
outside the normal course of events. The push that caused it triggers a fresh
run, which asks for approval of the new commit in the usual way.

## Duplicate Detection

Expand Down
Loading
Loading