pullshark 2.4.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sʜɪɴᴇɪ Nᴏᴜᴢᴇɴ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,576 @@
1
+ Metadata-Version: 2.4
2
+ Name: pullshark
3
+ Version: 2.4.6
4
+ Summary: Automate pull request creation and merging to unlock GitHub's Pull Shark achievement.
5
+ Author-email: Sʜɪɴᴇɪ Nᴏᴜᴢᴇɴ <ikx7a@hotmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Shineii86/PullShark
8
+ Project-URL: Repository, https://github.com/Shineii86/PullShark
9
+ Project-URL: Issues, https://github.com/Shineii86/PullShark/issues
10
+ Keywords: github,pull-request,automation,achievement,pull-shark
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: PyGithub>=2.1.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.0; extra == "dev"
26
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
27
+ Requires-Dist: ruff>=0.4.0; extra == "dev"
28
+ Dynamic: license-file
29
+
30
+ <div align="center">
31
+
32
+ [![Pull Shark Automator Banner](https://raw.githubusercontent.com/Shineii86/PullShark/refs/heads/main/images/PullShark.png)](https://github.com/Shineii86/PullShark)
33
+
34
+ [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Shineii86/PullShark/blob/main/notebooks/PullShark.ipynb)
35
+ [![CI](https://github.com/Shineii86/PullShark/actions/workflows/ci.yml/badge.svg)](https://github.com/Shineii86/PullShark/actions/workflows/ci.yml)
36
+ [![PyPI](https://img.shields.io/pypi/v/pullshark.svg)](https://pypi.org/project/pullshark/)
37
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
38
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
39
+
40
+ [![GitHub Stars](https://img.shields.io/github/stars/Shineii86/PullShark?style=for-the-badge)](https://github.com/Shineii86/PullShark/stargazers)
41
+ [![GitHub Forks](https://img.shields.io/github/forks/Shineii86/PullShark?style=for-the-badge)](https://github.com/Shineii86/PullShark/fork)
42
+
43
+ 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**.
44
+
45
+ </div>
46
+
47
+ ---
48
+
49
+ > [!WARNING]
50
+ > **This script creates real pull requests on your GitHub account.**
51
+ > - Never share your **Personal Access Token** — treat it like a password.
52
+ > - Use a **dedicated repository** to avoid cluttering important projects.
53
+ > - GitHub may rate‑limit excessive API calls; the built‑in delay helps prevent this.
54
+
55
+ ---
56
+
57
+ ## 📖 Table of Contents
58
+
59
+ - [What is Pull Shark?](#-what-is-pull-shark)
60
+ - [Features](#-features)
61
+ - [Prerequisites](#-prerequisites)
62
+ - [Project Structure](#-project-structure)
63
+ - [Installation](#-installation)
64
+ - [Usage](#-usage)
65
+ - [Google Colab](#1️⃣-google-colab)
66
+ - [Command Line (CLI)](#2️⃣-command-line-cli)
67
+ - [As a Python Package](#3️⃣-as-a-python-package)
68
+ - [Configuration Options](#-configuration-options)
69
+ - [How It Works](#-how-it-works-technical-overview)
70
+ - [Merge Methods](#-merge-methods)
71
+ - [Testing & Contributing](#-testing--contributing)
72
+ - [Troubleshooting](#-troubleshooting)
73
+ - [Changelog](#-changelog)
74
+ - [License & Disclaimer](#-license--disclaimer)
75
+
76
+ ---
77
+
78
+ ## 🎯 What is Pull Shark?
79
+
80
+ **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.
81
+
82
+ 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.
83
+
84
+ | Base | Bronze | Silver | Gold |
85
+ | :--: | :----: | :----: | :--: |
86
+ | [![Base](https://github.com/Shineii86/PullShark/blob/main/images/pull-shark-default.png)](https://github.com/Shineii86/PullShark) | [![Bronze](https://github.com/Shineii86/PullShark/blob/main/images/pull-shark-bronze.png)](https://github.com/Shineii86/PullShark) | [![Silver](https://github.com/Shineii86/PullShark/blob/main/images/pull-shark-silver.png)](https://github.com/Shineii86/PullShark) | [![Gold](https://github.com/Shineii86/PullShark/blob/main/images/pull-shark-gold.png)](https://github.com/Shineii86/PullShark) |
87
+
88
+
89
+ > 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.
90
+
91
+ ### 🦈 Pull Shark Achievement Tiers
92
+
93
+ | Tier | Badge Name | Required Merged Pull Requests |
94
+ | :--- | :--- | :--- |
95
+ | 1 | **Default / x1** | **2** merged PRs |
96
+ | 2 | **Bronze / x2** | **16** merged PRs |
97
+ | 3 | **Silver / x3** | **128** merged PRs |
98
+ | 4 | **Gold / x4** | **1024** merged PRs |
99
+
100
+ > [!IMPORTANT]
101
+ > Only merged PRs count toward this achievement. PRs that are closed without being merged do not contribute to your progress.
102
+
103
+ ---
104
+
105
+ ## ✨ Features
106
+
107
+ | Feature | Description |
108
+ |:--------|:------------|
109
+ | ☁️ **Google Colab** | One-click notebook with 5-step guided flow — no install needed |
110
+ | 🎨 **Dark Mode** | Colab notebook auto-adapts to light and dark themes |
111
+ | 🖥️ **CLI** | Full terminal interface with `run` and `clean` subcommands |
112
+ | 🔍 **Dry Run** | Preview branches, commits, and PRs without making any changes |
113
+ | 🧹 **Branch Cleanup** | Bulk-delete auto-created branches after a run |
114
+ | 📊 **Rate Limit Check** | View your API quota before starting — prevents mid-run failures |
115
+ | 🔀 **Merge Strategies** | Choose between `merge`, `squash`, or `rebase` methods |
116
+ | 🔄 **Retry Logic** | Automatically retries failed merges with configurable attempts |
117
+ | 📝 **Logging** | `--log file.log` saves timestamped debug output for auditing |
118
+ | 📄 **JSON Reports** | `--output report.json` saves structured run results |
119
+ | 🏷️ **Custom Prefix** | `--prefix mybot` to customize branch names |
120
+ | 📦 **Python Package** | Import into your own scripts for custom workflows |
121
+ | 🐍 **`python -m`** | Run as `python -m pullshark` without installing |
122
+ | ✅ **Validation** | Catches config errors before making any API calls |
123
+ | 🧪 **Tested** | 35+ pytest tests with CI on Python 3.9–3.12 |
124
+ | 📦 **PyPI** | `pip install pullshark` — install from PyPI |
125
+ | 🔧 **Pre-Commit** | Ruff lint/format + pytest hooks for contributors |
126
+
127
+ ---
128
+
129
+ ## 🧰 Prerequisites
130
+
131
+ Before you begin, make sure you have:
132
+
133
+ 1. **A GitHub account** — obviously 😄
134
+ 2. **A repository** where you have **write access** (you can create a new one just for this).
135
+ 3. **A GitHub Personal Access Token** with `repo` scope.
136
+
137
+ ### 🔐 How to Get a Personal Access Token
138
+
139
+ <details>
140
+ <summary><b>Classic Token (Recommended for beginners)</b></summary>
141
+
142
+ 1. Go to **Settings** → **Developer settings** → **Personal access tokens** → **Tokens (classic)**.
143
+ 2. Click **Generate new token (classic)**.
144
+ 3. Give it a name (e.g., `Pull Shark Bot`).
145
+ 4. Under **Select scopes**, check **`repo`**.
146
+ 5. Click **Generate token** and **copy it immediately**.
147
+
148
+ </details>
149
+
150
+ <details>
151
+ <summary><b>Fine-Grained Token (More secure, recommended)</b></summary>
152
+
153
+ 1. Go to **Settings** → **Developer settings** → **Personal access tokens** → **Fine-grained tokens**.
154
+ 2. Click **Generate new token**.
155
+ 3. Set **Resource owner** to your username.
156
+ 4. Under **Repository access**, select **Only select repositories** → pick your target repo.
157
+ 5. Under **Permissions** → **Repository permissions**, set:
158
+ - **Contents**: `Read and write`
159
+ - **Metadata**: `Read-only`
160
+ - **Pull requests**: `Read and write`
161
+ 6. Click **Generate token** and copy it.
162
+
163
+ </details>
164
+
165
+ ---
166
+
167
+ ## 📁 Project Structure
168
+
169
+ ```
170
+ PullShark/
171
+ ├── pullshark/ # Python package
172
+ │ ├── __init__.py # Package init & version
173
+ │ ├── __main__.py # python -m pullshark support
174
+ │ ├── config.py # Configuration dataclass with validation
175
+ │ ├── core.py # PullSharkBot — main automation logic
176
+ │ ├── utils.py # Helpers (random strings, mergeability, reports)
177
+ │ └── cli.py # Command-line interface (run & clean subcommands)
178
+ ├── tests/
179
+ │ └── test_pullshark.py # pytest test suite (35+ tests)
180
+ ├── notebooks/
181
+ │ └── PullShark.ipynb # Google Colab notebook (5-step guided flow)
182
+ ├── .github/
183
+ │ ├── workflows/
184
+ │ │ ├── ci.yml # CI on push/PR (Python 3.9–3.12)
185
+ │ │ └── publish.yml # PyPI publish on tag
186
+ │ └── dependabot.yml # Auto dependency updates
187
+ ├── images/ # Achievement badge images
188
+ ├── pyproject.toml # Python packaging + pytest + ruff config
189
+ ├── requirements.txt # Dependencies
190
+ ├── .pre-commit-config.yaml # Pre-commit hooks (ruff, pytest, linting)
191
+ ├── CONTRIBUTING.md # Contribution guidelines
192
+ ├── CHANGELOG.md # Version history
193
+ ├── LICENSE # MIT License
194
+ └── README.md # This file
195
+ ```
196
+
197
+ ---
198
+
199
+ ## 📥 Installation
200
+
201
+ ### From PyPI (Easiest)
202
+
203
+ ```bash
204
+ pip install pullshark
205
+ ```
206
+
207
+ ### From Source (Recommended for Contributors)
208
+
209
+ ```bash
210
+ git clone https://github.com/Shineii86/PullShark.git
211
+ cd PullShark
212
+ pip install -e .
213
+ ```
214
+
215
+ This installs the `pullshark` CLI command and makes the package importable.
216
+
217
+ ### With Dev Dependencies (For Contributors)
218
+
219
+ ```bash
220
+ pip install -e ".[dev]"
221
+ pip install pre-commit && pre-commit install
222
+ ```
223
+
224
+ Installs pytest, pytest-cov, ruff, and sets up pre-commit hooks.
225
+
226
+ ### Dependencies Only
227
+
228
+ ```bash
229
+ pip install PyGithub>=2.1.0
230
+ ```
231
+
232
+ ---
233
+
234
+ ## 🚀 Usage
235
+
236
+ ### 1️⃣ Google Colab
237
+
238
+ <a href="https://colab.research.google.com/github/Shineii86/PullShark/blob/main/notebooks/PullShark.ipynb">
239
+ <img src="https://user-images.githubusercontent.com/125879861/255389999-a0d261cf-893a-46a7-9a3d-2bb52811b997.png" alt="Open In Colab" width="200px">
240
+ </a>
241
+
242
+ The notebook walks you through **5 steps**:
243
+
244
+ | Step | Name | What it does |
245
+ |:----:|:-----|:-------------|
246
+ | 1 | 📦 **Install & Load** | Installs PyGithub and loads the package |
247
+ | 2 | 🔌 **Test Connection** | Validates token, repo access, write permissions, API rate limit, and existing branches |
248
+ | 3 | 🔍 **Dry Run** | Preview what the bot will do — no changes made *(auto-fills from Step 2)* |
249
+ | 4 | 🚀 **Run for Real** | Create and merge PRs with full configuration *(auto-fills from Step 3)* |
250
+ | 5 | 🧹 **Cleanup** | Delete all auto-created branches *(auto-fills from Step 4)* |
251
+
252
+ > 💡 **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.
253
+
254
+ ### 2️⃣ Command Line (CLI)
255
+
256
+ After installing with `pip install -e .`, use the `pullshark` command:
257
+
258
+ ```bash
259
+ # Create and merge PRs
260
+ pullshark run --token ghp_xxx --username YourUsername --repo YourRepo --prs 4
261
+
262
+ # Preview what would happen (no changes made)
263
+ pullshark run --token ghp_xxx --username YourUsername --repo YourRepo --dry-run
264
+
265
+ # Check API quota first
266
+ pullshark run --token ghp_xxx --username YourUsername --repo YourRepo --check-rate
267
+
268
+ # Use squash merge with custom branch prefix
269
+ pullshark run --token ghp_xxx --username YourUsername --repo YourRepo --merge-method squash --prefix mybot
270
+
271
+ # Save logs and JSON report
272
+ pullshark run --token ghp_xxx --username YourUsername --repo YourRepo --log run.log --output report.json
273
+
274
+ # Clean up auto-created branches
275
+ pullshark clean --token ghp_xxx --username YourUsername --repo YourRepo
276
+
277
+ # Preview cleanup without deleting
278
+ pullshark clean --token ghp_xxx --username YourUsername --repo YourRepo --dry-run
279
+
280
+ # Clean with custom prefix
281
+ pullshark clean --token ghp_xxx --username YourUsername --repo YourRepo --prefix mybot
282
+
283
+ # Run without installing
284
+ python -m pullshark run --token ghp_xxx --username YourUsername --repo YourRepo
285
+ ```
286
+
287
+ > 💡 The `run` subcommand is optional — `pullshark --token ... --repo ...` still works as a shortcut.
288
+
289
+ #### CLI Arguments — `run`
290
+
291
+ | Flag | Short | Required | Default | Description |
292
+ |------|-------|:--------:|---------|-------------|
293
+ | `--token` | `-t` | ✅ | — | GitHub Personal Access Token |
294
+ | `--username` | `-u` | ✅ | — | Your GitHub username |
295
+ | `--repo` | `-r` | ✅ | — | Target repository name |
296
+ | `--prs` | `-n` | | `4` | Number of PRs to create |
297
+ | `--branch` | `-b` | | `main` | Base branch to target |
298
+ | `--delay` | `-d` | | `10` | Delay (seconds) between PRs |
299
+ | `--max-retries` | | | `3` | Max merge retry attempts |
300
+ | `--merge-method` | | | `merge` | Merge strategy: `merge`, `squash`, `rebase` |
301
+ | `--prefix` | | | `auto-pr` | Branch name prefix |
302
+ | `--dry-run` | | | off | Preview mode — no changes made |
303
+ | `--check-rate` | | | off | Show API quota before running |
304
+ | `--log` | | | — | Save detailed logs to a file |
305
+ | `--output` | | | — | Save run report as JSON |
306
+
307
+ #### CLI Arguments — `clean`
308
+
309
+ | Flag | Short | Required | Description |
310
+ |------|-------|:--------:|-------------|
311
+ | `--token` | `-t` | ✅ | GitHub Personal Access Token |
312
+ | `--username` | `-u` | ✅ | Your GitHub username |
313
+ | `--repo` | `-r` | ✅ | Target repository name |
314
+ | `--prefix` | | Branch prefix to clean (default: `auto-pr`) |
315
+ | `--dry-run` | | | Show branches that would be deleted without deleting |
316
+ | `--log` | | | Save detailed logs to a file |
317
+
318
+ #### CLI Output Example
319
+
320
+ ```
321
+ Configuration: user='Shineii86' repo='PullShark'
322
+ Base branch: main
323
+ Will create 4 PR(s) with 10s delay.
324
+ Merge method: squash
325
+ Branch prefix: mybot
326
+
327
+ --- 📦 PR #1 of 4 ---
328
+ Latest main commit: a1b2c3d
329
+ ✅ Created branch: mybot-xyz123-1234567890
330
+ 📝 Updated README.md
331
+ 🔗 Created PR: https://github.com/Shineii86/PullShark/pull/178
332
+ ⏳ Waiting for GitHub to calculate mergeability...
333
+ 🎉 Merged PR #178
334
+ ⏸️ Pausing 10s for GitHub to process...
335
+
336
+ 🏁 Finished. 4 out of 4 pull requests merged.
337
+ 🦈 Congratulations! You've met the requirements for Pull Shark!
338
+
339
+ 📄 Report saved to report.json
340
+ ```
341
+
342
+ #### JSON Report Format
343
+
344
+ When using `--output report.json`, the file contains:
345
+
346
+ ```json
347
+ {
348
+ "version": "2.4.6",
349
+ "timestamp": "2026-05-09T02:37:00+00:00",
350
+ "config": {
351
+ "username": "Shineii86",
352
+ "repo": "PullShark",
353
+ "base_branch": "main",
354
+ "num_prs": 4,
355
+ "merge_method": "squash",
356
+ "branch_prefix": "mybot"
357
+ },
358
+ "summary": {
359
+ "total": 4,
360
+ "successful": 4,
361
+ "failed": 0,
362
+ "pull_shark_tier": "Default"
363
+ },
364
+ "pull_requests": [
365
+ {"index": 1, "merged": true, "branch": "mybot-abc123", "pr_number": 178, "pr_url": "..."}
366
+ ]
367
+ }
368
+ ```
369
+
370
+ ### 3️⃣ As a Python Package
371
+
372
+ Import the bot into your own scripts for custom workflows:
373
+
374
+ ```python
375
+ from pullshark.config import Config
376
+ from pullshark.core import PullSharkBot
377
+
378
+ config = Config(
379
+ github_username="YourUsername",
380
+ github_token="ghp_xxx",
381
+ repo_name="YourRepo",
382
+ num_prs=6,
383
+ delay_seconds=15,
384
+ merge_method="squash",
385
+ branch_prefix="mybot",
386
+ output_file="report.json", # Save JSON report
387
+ )
388
+
389
+ bot = PullSharkBot(config)
390
+ merged = bot.run()
391
+ print(f"Merged {merged} PRs")
392
+
393
+ # Clean up branches when done
394
+ bot.clean()
395
+
396
+ # Check API quota
397
+ info = bot.check_rate_limit()
398
+ print(f"API: {info['remaining']}/{info['limit']} remaining")
399
+ ```
400
+
401
+ ---
402
+
403
+ ## ⚙️ Configuration Options
404
+
405
+ | Parameter | Default | Description |
406
+ |:----------|:--------|:------------|
407
+ | `num_prs` | `4` | Total number of pull requests to create and merge. Minimum `2` for the badge. |
408
+ | `base_branch` | `"main"` | Target branch for PRs (e.g., `master`, `develop`). |
409
+ | `delay_seconds` | `10` | Wait time (in seconds) between PRs and between merge retries. |
410
+ | `max_retries` | `3` | Number of times to retry a failed merge before stopping. |
411
+ | `merge_method` | `"merge"` | Merge strategy: `"merge"`, `"squash"`, or `"rebase"`. |
412
+ | `branch_prefix` | `"auto-pr"` | Prefix for auto-generated branch names. |
413
+ | `dry_run` | `False` | If `True`, previews actions without making changes. |
414
+ | `log_file` | `""` | Path to save detailed debug logs. |
415
+ | `output_file` | `""` | Path to save JSON run report. |
416
+
417
+ ### Advanced Customization
418
+
419
+ - **File Modified**: By default, the script updates or creates `README.md`. To change this, edit `_make_commit()` in `pullshark/core.py`.
420
+ - **Repository**: Use any repo you own or have write access to — just update the repo name.
421
+
422
+ ---
423
+
424
+ ## 🔬 How It Works (Technical Overview)
425
+
426
+ The script performs the following steps for **each** pull request:
427
+
428
+ 1. **Fetch the latest commit SHA** from the specified base branch to ensure we branch from the most up‑to‑date state.
429
+ 2. **Create a new branch** with a unique name (e.g., `auto-pr-abc123-1234567890`).
430
+ 3. **Make a commit** on that branch — either appending a line to `README.md` or creating it if it doesn't exist.
431
+ 4. **Open a pull request** from the new branch to the base branch.
432
+ 5. **Wait for GitHub's mergeability check** (polling the PR status every 3 seconds).
433
+ 6. **Merge the pull request** using the configured merge method.
434
+ 7. **Pause for `DELAY_SECONDS`** to let GitHub fully update the base branch before starting the next iteration.
435
+
436
+ **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.
437
+
438
+ **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.
439
+
440
+ ---
441
+
442
+ ## 🔀 Merge Methods
443
+
444
+ PullShark supports three merge strategies. Choose based on your preference:
445
+
446
+ | Method | Flag | What It Does | History |
447
+ |:-------|:-----|:-------------|:--------|
448
+ | **Merge** | `--merge-method merge` | Creates a merge commit joining the branch to base | Preserves all commits |
449
+ | **Squash** | `--merge-method squash` | Combines all branch commits into a single commit | Clean, linear history |
450
+ | **Rebase** | `--merge-method rebase` | Replays branch commits on top of base | Linear, no merge commit |
451
+
452
+ > 💡 For Pull Shark achievement purposes, all three methods count equally. Use `squash` for cleaner history.
453
+
454
+ ---
455
+
456
+ ## 🧪 Testing & Contributing
457
+
458
+ ### Running Tests
459
+
460
+ ```bash
461
+ # Install dev dependencies
462
+ pip install -e ".[dev]"
463
+
464
+ # Run all tests
465
+ pytest tests/ -v
466
+
467
+ # Run with coverage
468
+ pytest tests/ -v --cov=pullshark --cov-report=term-missing
469
+
470
+ # Lint
471
+ ruff check pullshark/ tests/
472
+ ```
473
+
474
+ ### Pre-Commit Hooks
475
+
476
+ For contributors, pre-commit hooks run automatically before each commit:
477
+
478
+ ```bash
479
+ pip install pre-commit
480
+ pre-commit install
481
+ ```
482
+
483
+ Hooks include:
484
+ - **ruff** — lint and auto-format Python code
485
+ - **trailing-whitespace** — remove trailing spaces
486
+ - **end-of-file-fixer** — ensure files end with newline
487
+ - **check-yaml / check-json** — validate config files
488
+ - **pytest** — run the test suite
489
+
490
+ ### CI Pipeline
491
+
492
+ Every push and PR triggers the GitHub Actions workflow which:
493
+ - Runs the test suite across Python 3.9, 3.10, 3.11, and 3.12
494
+ - Lints code with ruff
495
+ - Validates notebook JSON structure
496
+
497
+ ### Publishing (Maintainers)
498
+
499
+ Tag a release to auto-publish to PyPI:
500
+
501
+ ```bash
502
+ git tag v2.4.6
503
+ git push origin v2.4.6
504
+ ```
505
+
506
+ The `publish.yml` workflow builds and publishes automatically via trusted publishing.
507
+
508
+ ### Contributing
509
+
510
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, branch naming conventions, and PR guidelines.
511
+
512
+ ---
513
+
514
+ ## 🆘 Troubleshooting
515
+
516
+ | Issue | Solution |
517
+ |:------|:---------|
518
+ | `BadCredentialsException` | Token is wrong or expired. Generate a new one with `repo` scope. |
519
+ | `405 Not mergeable` | Enable **Allow auto-merge** in repo Settings → General → Pull Requests. |
520
+ | Hangs at "Waiting for mergeability" | PR may have a conflict. Delete the branch manually and retry. |
521
+ | `RateLimitExceededException` | Wait an hour or increase `DELAY_SECONDS`. Use `--check-rate` to check beforehand. |
522
+ | No badge after success | Wait a few minutes and refresh your profile. Achievement updates are not always instant. |
523
+ | Leftover branches | Run `pullshark clean` to delete all auto-created branches. |
524
+
525
+ ---
526
+
527
+ ## 📋 Changelog
528
+
529
+ See [CHANGELOG.md](CHANGELOG.md) for a full history of changes.
530
+
531
+ ---
532
+
533
+ ## 📄 License & Disclaimer
534
+
535
+ This project is licensed under the **MIT License** – see the [LICENSE](LICENSE) file for details.
536
+
537
+ > [!WARNING]
538
+ > **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.
539
+
540
+ ---
541
+
542
+ ### 🔗 Quick Links
543
+
544
+ - [PyPI Package](https://pypi.org/project/pullshark/)
545
+ - [Google Colab](https://colab.research.google.com/)
546
+ - [GitHub Personal Access Tokens](https://github.com/settings/tokens)
547
+ - [Fine-Grained Tokens](https://github.com/settings/personal-access-tokens/new)
548
+ - [Pull Shark Achievement Details](https://github.com/Schweinepriester/github-profile-achievements#pull-shark-)
549
+ - [Contributing Guide](CONTRIBUTING.md)
550
+
551
+ ---
552
+
553
+ ## 💕 Loved My Work?
554
+
555
+ 🚨 [Follow me on GitHub](https://github.com/Shineii86)
556
+
557
+ ⭐ [Give a star to this project](https://github.com/Shineii86/PullShark)
558
+
559
+ <div align="center">
560
+
561
+ <a href="https://github.com/Shineii86/PullShark">
562
+ <img src="https://github.com/Shineii86/AniPay/blob/main/Source/Banner6.png" alt="Banner">
563
+ </a>
564
+
565
+ *For inquiries or collaborations*
566
+
567
+ [![Telegram Badge](https://img.shields.io/badge/-Telegram-2CA5E0?style=flat&logo=Telegram&logoColor=white)](https://telegram.me/Shineii86 "Contact on Telegram")
568
+ [![Instagram Badge](https://img.shields.io/badge/-Instagram-C13584?style=flat&logo=Instagram&logoColor=white)](https://instagram.com/ikx7.a "Follow on Instagram")
569
+ [![Pinterest Badge](https://img.shields.io/badge/-Pinterest-E60023?style=flat&logo=Pinterest&logoColor=white)](https://pinterest.com/ikx7a "Follow on Pinterest")
570
+ [![Gmail Badge](https://img.shields.io/badge/-Gmail-D14836?style=flat&logo=Gmail&logoColor=white)](mailto:ikx7a@hotmail.com "Send an Email")
571
+
572
+ <sup><b>Copyright © 2026 <a href="https://telegram.me/Shineii86">Shinei Nouzen</a> All Rights Reserved</b></sup>
573
+
574
+ ![Last Commit](https://img.shields.io/github/last-commit/Shineii86/PullShark?style=for-the-badge)
575
+
576
+ </div>