Skip to content

feat: Fork PR Support with Manual Approval #2581

@DerekRoberts

Description

@DerekRoberts

UPDATE: Change as little as possible, keeping fork and standard PR handling identical. Do not add approvals for safe functions, like PR validation (conventional commits).

[FEATURE] Add Fork PR Support with Manual Approval

🎯 Problem Statement

Currently, our GitHub Actions workflows only support pull requests from the same repository. Fork PRs cannot run workflows because:

  1. No access to secrets: Fork PRs don't have access to repository secrets by default (GitHub security feature)
  2. Limited permissions: Fork PRs have restricted permissions for security reasons
  3. Workflow limitations: Our current workflows use pull_request trigger which doesn't work for forks

This prevents contributors from:

  • Using Background Agents on their forks (Cursor, GitHub Copilot, etc.)
  • Getting full CI/CD validation on their fork PRs
  • Testing deployments before merging

💡 Proposed Solution

Implement dual-trigger workflow support that:

  1. Same-repo PRs: Continue using pull_request trigger (no approval needed, immediate execution)
  2. Fork PRs: Use pull_request_target trigger with environment protection (requires manual approval)

Key Features

  • Security: Fork PRs require explicit approval before running
  • Full functionality: After approval, fork PRs get same workflows as same-repo PRs
  • Transparency: Clear separation and logging of fork vs same-repo PRs
  • Flexibility: Approvers can quickly approve trusted forks (e.g., your own fork)

📋 Implementation Details

Workflow Changes Required

Update the following workflows to support both same-repo and fork PRs:

1. .github/workflows/pr-open.yml

  • Add pull_request_target trigger
  • Add check-fork job to detect fork PRs
  • Add conditional logic to jobs:
    • Same-repo PRs: Run via pull_request trigger (no approval)
    • Fork PRs: Run via pull_request_target trigger (requires approval)
  • Add environment: fork-pr-approval to fork PR jobs
  • Ensure fork code is checked out correctly using github.event.pull_request.head.sha

2. .github/workflows/analysis.yml

  • Add pull_request_target trigger
  • Add check-fork job
  • Add conditional logic and environment protection for fork PRs
  • Update checkout steps to handle fork code

3. .github/workflows/pr-validate.yml

  • Add pull_request_target trigger
  • Add check-fork job
  • Add conditional logic and environment protection for fork PRs

Environment Setup Required

Create a GitHub Environment named fork-pr-approval:

  1. Navigate to: Settings → Environments → New environment
  2. Name: fork-pr-approval
  3. Configuration:
    • Required reviewers: Add yourself and other trusted approvers
    • Wait timer: Optional (0 minutes recommended)
    • Deployment branches: All branches (or restrict as needed)
  4. Save environment

Detection Logic

Fork detection uses:
```yaml
github.event.pull_request.head.repo.full_name != github.repository
```

Conditional Execution

Jobs will run based on:

  • Same-repo PRs: github.event_name == 'pull_request' && is_fork == 'false'
  • Fork PRs: github.event_name == 'pull_request_target' && is_fork == 'true'

Code Checkout

For fork PRs via pull_request_target, explicitly checkout the fork's code:
```yaml

  • uses: actions/checkout@v6
    if: github.event_name == 'pull_request_target'
    with:
    ref: ${{ github.event.pull_request.head.sha }}
    fetch-depth: 0
    ```

🔒 Security Considerations

Why pull_request_target?

  • Runs in the base repository context (has access to secrets)
  • Requires environment protection (manual approval)
  • Checks out fork code explicitly (controlled)

Security Benefits

  1. No automatic execution: Fork PRs cannot run workflows without approval
  2. Explicit approval: Required reviewers must approve before workflows run
  3. Audit trail: All approvals are logged in GitHub
  4. Controlled access: Secrets only available after approval

Best Practices

  • ✅ Always use environment protection for fork PRs
  • ✅ Explicitly checkout fork code (don't trust default checkout)
  • ✅ Review fork PR code before approving
  • ✅ Limit required reviewers to trusted team members

🧪 Testing Plan

Test Cases

  1. Same-repo PR (should work as before)

    • Create PR from branch in same repo
    • Verify workflows run immediately (no approval needed)
    • Verify all jobs complete successfully
  2. Fork PR (new functionality)

    • Create PR from fork repository
    • Verify workflows show "waiting for approval"
    • Approve via GitHub Environment
    • Verify workflows run after approval
    • Verify fork code is checked out correctly
    • Verify all jobs complete successfully
  3. Fork PR rejection

    • Create PR from fork
    • Verify workflows are blocked without approval
    • Verify no secrets are exposed

Validation Checklist

  • Same-repo PRs work exactly as before
  • Fork PRs require approval before running
  • Fork PRs can access secrets after approval
  • Fork code is checked out correctly
  • All workflow jobs run for approved fork PRs
  • Environment protection is properly configured
  • Documentation is updated

📚 References

✅ Acceptance Criteria

  • All three workflows (pr-open.yml, analysis.yml, pr-validate.yml) support fork PRs
  • Fork PRs require manual approval via environment protection
  • Same-repo PRs continue to work without approval
  • Fork code is correctly checked out and tested
  • Environment fork-pr-approval is created and configured
  • Documentation is updated (if needed)
  • All tests pass for both same-repo and fork PRs

🚀 Implementation Priority

Priority: Medium
Effort: ~2-3 hours
Impact: Enables fork-based contributions and Background Agent workflows

📝 Notes

  • This feature enables Background Agents (Cursor, GitHub Copilot) to work on forks
  • Useful for contributors who can't install GitHub Apps on organization repos
  • Maintains security through environment protection
  • No breaking changes to existing same-repo PR workflows

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

Status

Next

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions