A fully automated Python tool that creates and merges multiple pull requests in your GitHub repository — helping you earn the coveted Pull Shark achievement. Runs in Google Colab or directly from your terminal.
Warning
This script creates real pull requests on your GitHub account.
- Never share your Personal Access Token — treat it like a password.
- Use a dedicated repository to avoid cluttering important projects.
- GitHub may rate‑limit excessive API calls; the built‑in delay helps prevent this.
- What is Pull Shark?
- Features
- Prerequisites
- Project Structure
- Installation
- Usage
- Configuration Options
- How It Works
- Merge Methods
- Testing & Contributing
- Troubleshooting
- Changelog
- License & Disclaimer
Pull Shark is a GitHub achievement awarded when you have at least 2 pull requests merged into any repository. It's one of the most popular achievements and a fun way to show off your open-source contributions.
This script automates the creation and merging of pull requests, so you can unlock the achievement in under 5 minutes — from your browser or terminal.
| Base | Bronze | Silver | Gold |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
To earn the "Pull Shark" achievement on GitHub, you need to have your pull requests (PRs) merged. The badge has four tiers, each requiring a specific number of merged PRs.
| Tier | Badge Name | Required Merged Pull Requests |
|---|---|---|
| 1 | Default / x1 | 2 merged PRs |
| 2 | Bronze / x2 | 16 merged PRs |
| 3 | Silver / x3 | 128 merged PRs |
| 4 | Gold / x4 | 1024 merged PRs |
Important
Only merged PRs count toward this achievement. PRs that are closed without being merged do not contribute to your progress.
| Feature | Description |
|---|---|
| ☁️ Google Colab | One-click notebook with 5-step guided flow — no install needed |
| 🎨 Dark Mode | Colab notebook auto-adapts to light and dark themes |
| 🖥️ CLI | Full terminal interface with run and clean subcommands |
| 🔍 Dry Run | Preview branches, commits, and PRs without making any changes |
| 🧹 Branch Cleanup | Bulk-delete auto-created branches after a run |
| 📊 Rate Limit Check | View your API quota before starting — prevents mid-run failures |
| 🔀 Merge Strategies | Choose between merge, squash, or rebase methods |
| 🔄 Retry Logic | Automatically retries failed merges with configurable attempts |
| 📝 Logging | --log file.log saves timestamped debug output for auditing |
| 📄 JSON Reports | --output report.json saves structured run results |
| 🏷️ Custom Prefix | --prefix mybot to customize branch names |
| 📦 Python Package | Import into your own scripts for custom workflows |
🐍 python -m |
Run as python -m pullshark without installing |
| ✅ Validation | Catches config errors before making any API calls |
| 🧪 Tested | 35+ pytest tests with CI on Python 3.9–3.12 |
| 📦 PyPI | pip install pullshark — install from PyPI |
| 🔧 Pre-Commit | Ruff lint/format + pytest hooks for contributors |
Before you begin, make sure you have:
- A GitHub account — obviously 😄
- A repository where you have write access (you can create a new one just for this).
- A GitHub Personal Access Token with
reposcope.
Classic Token (Recommended for beginners)
- Go to Settings → Developer settings → Personal access tokens → Tokens (classic).
- Click Generate new token (classic).
- Give it a name (e.g.,
Pull Shark Bot). - Under Select scopes, check
repo. - Click Generate token and copy it immediately.
Fine-Grained Token (More secure, recommended)
- Go to Settings → Developer settings → Personal access tokens → Fine-grained tokens.
- Click Generate new token.
- Set Resource owner to your username.
- Under Repository access, select Only select repositories → pick your target repo.
- Under Permissions → Repository permissions, set:
- Contents:
Read and write - Metadata:
Read-only - Pull requests:
Read and write
- Contents:
- Click Generate token and copy it.
PullShark/
├── pullshark/ # Python package
│ ├── __init__.py # Package init & version
│ ├── __main__.py # python -m pullshark support
│ ├── config.py # Configuration dataclass with validation
│ ├── core.py # PullSharkBot — main automation logic
│ ├── utils.py # Helpers (random strings, mergeability, reports)
│ └── cli.py # Command-line interface (run & clean subcommands)
├── tests/
│ └── test_pullshark.py # pytest test suite (35+ tests)
├── notebooks/
│ └── PullShark.ipynb # Google Colab notebook (5-step guided flow)
├── .github/
│ ├── workflows/
│ │ ├── ci.yml # CI on push/PR (Python 3.9–3.12)
│ │ └── publish.yml # PyPI publish on tag
│ └── dependabot.yml # Auto dependency updates
├── images/ # Achievement badge images
├── pyproject.toml # Python packaging + pytest + ruff config
├── requirements.txt # Dependencies
├── .pre-commit-config.yaml # Pre-commit hooks (ruff, pytest, linting)
├── CONTRIBUTING.md # Contribution guidelines
├── CHANGELOG.md # Version history
├── LICENSE # MIT License
└── README.md # This file
pip install pullsharkgit clone https://github.com/Shineii86/PullShark.git
cd PullShark
pip install -e .This installs the pullshark CLI command and makes the package importable.
pip install -e ".[dev]"
pip install pre-commit && pre-commit installInstalls pytest, pytest-cov, ruff, and sets up pre-commit hooks.
pip install PyGithub>=2.1.0
The notebook walks you through 5 steps:
| Step | Name | What it does |
|---|---|---|
| 1 | 📦 Install & Load | Installs PyGithub and loads the package |
| 2 | 🔌 Test Connection | Validates token, repo access, write permissions, API rate limit, and existing branches |
| 3 | 🔍 Dry Run | Preview what the bot will do — no changes made (auto-fills from Step 2) |
| 4 | 🚀 Run for Real | Create and merge PRs with full configuration (auto-fills from Step 3) |
| 5 | 🧹 Cleanup | Delete all auto-created branches (auto-fills from Step 4) |
💡 Tip: Enter your credentials once in Step 2 — they flow through to Steps 3, 4, and 5 automatically. Run the styling cell first to enable dark/light mode support.
After installing with pip install -e ., use the pullshark command:
# Create and merge PRs
pullshark run --token ghp_xxx --username YourUsername --repo YourRepo --prs 4
# Preview what would happen (no changes made)
pullshark run --token ghp_xxx --username YourUsername --repo YourRepo --dry-run
# Check API quota first
pullshark run --token ghp_xxx --username YourUsername --repo YourRepo --check-rate
# Use squash merge with custom branch prefix
pullshark run --token ghp_xxx --username YourUsername --repo YourRepo --merge-method squash --prefix mybot
# Save logs and JSON report
pullshark run --token ghp_xxx --username YourUsername --repo YourRepo --log run.log --output report.json
# Clean up auto-created branches
pullshark clean --token ghp_xxx --username YourUsername --repo YourRepo
# Preview cleanup without deleting
pullshark clean --token ghp_xxx --username YourUsername --repo YourRepo --dry-run
# Clean with custom prefix
pullshark clean --token ghp_xxx --username YourUsername --repo YourRepo --prefix mybot
# Run without installing
python -m pullshark run --token ghp_xxx --username YourUsername --repo YourRepo💡 The
runsubcommand is optional —pullshark --token ... --repo ...still works as a shortcut.
| Flag | Short | Required | Default | Description |
|---|---|---|---|---|
--token |
-t |
✅ | — | GitHub Personal Access Token |
--username |
-u |
✅ | — | Your GitHub username |
--repo |
-r |
✅ | — | Target repository name |
--prs |
-n |
4 |
Number of PRs to create | |
--branch |
-b |
main |
Base branch to target | |
--delay |
-d |
10 |
Delay (seconds) between PRs | |
--max-retries |
3 |
Max merge retry attempts | ||
--merge-method |
merge |
Merge strategy: merge, squash, rebase |
||
--prefix |
auto-pr |
Branch name prefix | ||
--dry-run |
off | Preview mode — no changes made | ||
--check-rate |
off | Show API quota before running | ||
--log |
— | Save detailed logs to a file | ||
--output |
— | Save run report as JSON |
| Flag | Short | Required | Description |
|---|---|---|---|
--token |
-t |
✅ | GitHub Personal Access Token |
--username |
-u |
✅ | Your GitHub username |
--repo |
-r |
✅ | Target repository name |
--prefix |
Branch prefix to clean (default: auto-pr) |
||
--dry-run |
Show branches that would be deleted without deleting | ||
--log |
Save detailed logs to a file |
Configuration: user='Shineii86' repo='PullShark'
Base branch: main
Will create 4 PR(s) with 10s delay.
Merge method: squash
Branch prefix: mybot
--- 📦 PR #1 of 4 ---
Latest main commit: a1b2c3d
✅ Created branch: mybot-xyz123-1234567890
📝 Updated README.md
🔗 Created PR: https://github.com/Shineii86/PullShark/pull/178
⏳ Waiting for GitHub to calculate mergeability...
🎉 Merged PR #178
⏸️ Pausing 10s for GitHub to process...
🏁 Finished. 4 out of 4 pull requests merged.
🦈 Congratulations! You've met the requirements for Pull Shark!
📄 Report saved to report.json
When using --output report.json, the file contains:
{
"version": "2.4.6",
"timestamp": "2026-05-09T02:37:00+00:00",
"config": {
"username": "Shineii86",
"repo": "PullShark",
"base_branch": "main",
"num_prs": 4,
"merge_method": "squash",
"branch_prefix": "mybot"
},
"summary": {
"total": 4,
"successful": 4,
"failed": 0,
"pull_shark_tier": "Default"
},
"pull_requests": [
{"index": 1, "merged": true, "branch": "mybot-abc123", "pr_number": 178, "pr_url": "..."}
]
}Import the bot into your own scripts for custom workflows:
from pullshark.config import Config
from pullshark.core import PullSharkBot
config = Config(
github_username="YourUsername",
github_token="ghp_xxx",
repo_name="YourRepo",
num_prs=6,
delay_seconds=15,
merge_method="squash",
branch_prefix="mybot",
output_file="report.json", # Save JSON report
)
bot = PullSharkBot(config)
merged = bot.run()
print(f"Merged {merged} PRs")
# Clean up branches when done
bot.clean()
# Check API quota
info = bot.check_rate_limit()
print(f"API: {info['remaining']}/{info['limit']} remaining")| Parameter | Default | Description |
|---|---|---|
num_prs |
4 |
Total number of pull requests to create and merge. Minimum 2 for the badge. |
base_branch |
"main" |
Target branch for PRs (e.g., master, develop). |
delay_seconds |
10 |
Wait time (in seconds) between PRs and between merge retries. |
max_retries |
3 |
Number of times to retry a failed merge before stopping. |
merge_method |
"merge" |
Merge strategy: "merge", "squash", or "rebase". |
branch_prefix |
"auto-pr" |
Prefix for auto-generated branch names. |
dry_run |
False |
If True, previews actions without making changes. |
log_file |
"" |
Path to save detailed debug logs. |
output_file |
"" |
Path to save JSON run report. |
- File Modified: By default, the script updates or creates
README.md. To change this, edit_make_commit()inpullshark/core.py. - Repository: Use any repo you own or have write access to — just update the repo name.
The script performs the following steps for each pull request:
- Fetch the latest commit SHA from the specified base branch to ensure we branch from the most up‑to‑date state.
- Create a new branch with a unique name (e.g.,
auto-pr-abc123-1234567890). - Make a commit on that branch — either appending a line to
README.mdor creating it if it doesn't exist. - Open a pull request from the new branch to the base branch.
- Wait for GitHub's mergeability check (polling the PR status every 3 seconds).
- Merge the pull request using the configured merge method.
- Pause for
DELAY_SECONDSto let GitHub fully update the base branch before starting the next iteration.
Retry Logic: If a merge fails (e.g., due to a temporary GitHub hiccup), the script will wait DELAY_SECONDS and retry up to MAX_RETRIES times before giving up.
Rate Limit Check: With --check-rate, the bot inspects your remaining API quota before starting. Each PR cycle uses ~4 API calls, so the bot estimates whether you have enough.
PullShark supports three merge strategies. Choose based on your preference:
| Method | Flag | What It Does | History |
|---|---|---|---|
| Merge | --merge-method merge |
Creates a merge commit joining the branch to base | Preserves all commits |
| Squash | --merge-method squash |
Combines all branch commits into a single commit | Clean, linear history |
| Rebase | --merge-method rebase |
Replays branch commits on top of base | Linear, no merge commit |
💡 For Pull Shark achievement purposes, all three methods count equally. Use
squashfor cleaner history.
# Install dev dependencies
pip install -e ".[dev]"
# Run all tests
pytest tests/ -v
# Run with coverage
pytest tests/ -v --cov=pullshark --cov-report=term-missing
# Lint
ruff check pullshark/ tests/For contributors, pre-commit hooks run automatically before each commit:
pip install pre-commit
pre-commit installHooks include:
- ruff — lint and auto-format Python code
- trailing-whitespace — remove trailing spaces
- end-of-file-fixer — ensure files end with newline
- check-yaml / check-json — validate config files
- pytest — run the test suite
Every push and PR triggers the GitHub Actions workflow which:
- Runs the test suite across Python 3.9, 3.10, 3.11, and 3.12
- Lints code with ruff
- Validates notebook JSON structure
Tag a release to auto-publish to PyPI:
git tag v2.4.6
git push origin v2.4.6The publish.yml workflow builds and publishes automatically via trusted publishing.
See CONTRIBUTING.md for development setup, branch naming conventions, and PR guidelines.
| Issue | Solution |
|---|---|
BadCredentialsException |
Token is wrong or expired. Generate a new one with repo scope. |
405 Not mergeable |
Enable Allow auto-merge in repo Settings → General → Pull Requests. |
| Hangs at "Waiting for mergeability" | PR may have a conflict. Delete the branch manually and retry. |
RateLimitExceededException |
Wait an hour or increase DELAY_SECONDS. Use --check-rate to check beforehand. |
| No badge after success | Wait a few minutes and refresh your profile. Achievement updates are not always instant. |
| Leftover branches | Run pullshark clean to delete all auto-created branches. |
See CHANGELOG.md for a full history of changes.
This project is licensed under the MIT License – see the LICENSE file for details.
Warning
This script is intended for educational purposes and to help users unlock a harmless GitHub achievement. Please use responsibly and do not abuse automation to spam repositories. The author is not responsible for any consequences arising from misuse of this tool.
- PyPI Package
- Google Colab
- GitHub Personal Access Tokens
- Fine-Grained Tokens
- Pull Shark Achievement Details
- Contributing Guide




