Skip to content
Merged
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
42 changes: 42 additions & 0 deletions .github/workflows/guard-main-branch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Guard main branch

on:
pull_request:
branches:
- main
merge_group:

permissions: {}

# DO NOT RENAME the job's `name:` — it is referenced by GitHub branch
# protection as a required status check on `main`. Renaming silently
# breaks the gate.
jobs:
guard:
name: Verify PR source branch
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Reject merge_group events
if: github.event_name == 'merge_group'
run: |
echo "::error::Merge queue is not supported for main. Disable merge queue or update this guard."
exit 1
- name: Check head branch name
env:
HEAD_REF: ${{ github.head_ref }}
HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
BASE_REPO: ${{ github.repository }}
run: |
echo "PR head repo: $HEAD_REPO"
echo "PR head branch: $HEAD_REF"
if [ "$HEAD_REPO" != "$BASE_REPO" ]; then
echo "::error::PRs to main must originate from the canonical repository ($BASE_REPO), not a fork ($HEAD_REPO). External contributors should open PRs against the 'litellm_oss_branch' branch instead."
exit 1
fi
if [ "$HEAD_REF" = "litellm_internal_staging" ] || [[ "$HEAD_REF" == litellm_hotfix_?* ]]; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Fork branch name bypass

A contributor from a forked repository can name their branch litellm_internal_staging or litellm_hotfix_anything and the check will pass, since github.head_ref only contains the branch name — not the repository origin. Adding a github.event.pull_request.head.repo.full_name check ensures the source is the canonical repository:

        run: |
          echo "PR head branch: $HEAD_REF"
          REPO="${{ github.event.pull_request.head.repo.full_name }}"
          EXPECTED_REPO="${{ github.repository }}"
          if [ "$REPO" != "$EXPECTED_REPO" ]; then
            echo "::error::PRs to main must originate from the canonical repository ($EXPECTED_REPO), not a fork ($REPO)."
            exit 1
          fi
          if [ "$HEAD_REF" = "litellm_internal_staging" ] || [[ "$HEAD_REF" == litellm_hotfix_?* ]]; then
            echo "Allowed source branch."
            exit 0
          fi
          echo "::error::PRs to main must originate from 'litellm_internal_staging' or a 'litellm_hotfix_*' branch. Got: '$HEAD_REF'."
          exit 1

Note: github.event.pull_request.head.repo.full_name is a static context value (not user-controlled shell input), so it is safe to interpolate directly.

echo "Allowed source branch."
exit 0
fi
echo "::error::PRs to main must originate from 'litellm_internal_staging' or a 'litellm_hotfix_*' branch. Got: '$HEAD_REF'. If this is a contribution, retarget the PR against 'litellm_oss_branch' instead."
exit 1
Loading