I wanted to build the world’s biggest open-source contributors list. A place where anyone could add their name with a pull request and become part of something massive.
Sounds simple. It is, until you realize that “anyone” could mean thousands of people. Manually reviewing and merging every single PR? No thanks.
So I built a self-merging PR system that validates, cleans, merges, and updates everything automatically. From submission to merge, the whole thing takes about fifteen seconds.
Here’s how it works.
The problem
When you open a repo to the internet, you can’t rely on manual moderation.
People fork, change whatever they want, and send PRs. Most are fine. Some will break things. A few will test your patience.
I needed a system that could:
- Accept PRs from forks
- Validate what was changed
- Merge automatically if everything looked good
- Flag or reject anythign invalid
Basically, I wanted to press zero buttons.
Figuring out the permissions
Here’s where it got tricky.
GitHub limits what workflows can do with PRs coming from forks. You can’t comment, label, or merge them directly using the default pull_request
trigger.
The fix is to use pull_request_target
, which runs in the context of the base repository, giving the workflow the permissions it needs.
The key part is to explicitly checkout the PR commit so you never execute untrusted code from a fork.
on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]
branches: [master]
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
This setup runs securely while still giving the workflow full access.
Making the bot act like me
The default GITHUB_TOKEN
works, but it’s limited.
It can’t comment on fork PRs or bypass branch protection, and all its commits show up as github-actions[bot].
That’s not what I wanted.
So I created a personal access token (PAT) with full repo and workflow permissions, then configured git to use my credentials.
env:
GH_TOKEN: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }}
- name: Setup Git
run: |
git config --global user.name "Sashank Bhamidi"
git config --global user.email "hello@sashank.wiki"
Now every commit and comment looks like it came from me, but I’m not the one doing it.
Stopping duplicate runs
At one point, every time the workflow added or removed a label, it triggered itself again.
That meant duplicate comments and multiple runs per PR.
The fix was simple: skip when the action is related to labels.
if: github.event.action != 'labeled' && github.event.action != 'unlabeled'
Sometimes, one clean conditional saves hours of noise.
Validating contributions
Each contributor edits a single file: ADD_YOUR_NAME.md
.
Here’s what the format looks like:
- Name: Your Name
- Username: github-username
- Message: Optional message
The workflow checks a few things before merging:
- The structure is still intact
- The name and username are properly formatted
- The username hasn’t already been used
- There’s no profanity
- No other files were changed
The profanity check uses the purgomalum.com API.
If the API goes down, the PR is flagged for manual review instead of being approved blindly. Always fail closed, not open.
Auto-merging PRs
Once validation passes, the workflow comments, labels, approves, and merges automatically.
gh pr merge ${{ github.event.number }} --squash --auto
That’s it.
--auto
merges the PR the moment all checks pass, and --squash
keeps the commit history clean.
What happens after a merge
When a PR merges, another workflow kicks in. It:
- Extracts the new entry from
ADD_YOUR_NAME.md
- Appends it to
CONTRIBUTORS.md
- Sorts contributors alphabetically
- Updates the contributor count in
README.md
- Resets the template
- Commits and pushes everything back
If anything fails mid-process, it rolls back automatically to a backup commit.
No human debugging. No broken files.
The result
The entire pipeline, from pull request to merge to updated list, runs in around fifteen seconds.
No reviews. No waiting. No stress.
It handles:
- Fork permissions
- Validation
- Duplicate prevention
- Profanity checks
- Auto-merging
- Rollbacks
It just works. Every time.
What I learned
-
pull_request_target
but you have to use it safely. - PAT tokens unlock what
GITHUB_TOKEN
can’t. - Always fail closed when user input is involved.
- Validation saves time later.
- Rollbacks are essential, not optional.
Try it yourself
You can see it live at git-gang.
Add your name, open a PR, and watch it merge itself. It takes about fifteen seconds.
Goal’s ten thousand contributors. Started with ten.
Let’s see how far it goes.
Top comments (0)