pronto-pr 0.1.0__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 Zachary Love
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,422 @@
1
+ Metadata-Version: 2.4
2
+ Name: pronto-pr
3
+ Version: 0.1.0
4
+ Summary: Automatically press GitHub's "Update branch" button on all your open PRs.
5
+ Author: Zachary Love
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Zachary-Love/PRonto
8
+ Project-URL: Issues, https://github.com/Zachary-Love/PRonto/issues
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: pystray<0.20,>=0.19
13
+ Requires-Dist: pillow
14
+ Dynamic: license-file
15
+
16
+ # PRonto
17
+
18
+ <p align="center">
19
+ <img src="https://raw.githubusercontent.com/Zachary-Love/PRonto/main/assets/pronto.png" width="128" alt="PRonto logo: a dog in profile with a green dot">
20
+ </p>
21
+
22
+ **Never look at an out-of-date pull request again.**
23
+
24
+ PRonto runs quietly on your computer and keeps every open GitHub pull request you've authored up to date with its base branch. Whenever GitHub would show the **Update branch** button on one of your PRs, PRonto presses it for you, using the default "merge commit" option. When you open your PRs, they're already current.
25
+
26
+ ```
27
+ $ pronto
28
+ updated acme/webapp#412
29
+ updated acme/api#88
30
+ skipped acme/api#85: There are no new commits on the base branch.
31
+ skipped acme/infra#1205: merge conflict between base and head
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Contents
37
+
38
+ - [Features](#features)
39
+ - [How it works](#how-it-works)
40
+ - [Requirements](#requirements)
41
+ - [Installation](#installation)
42
+ - [Creating a GitHub token](#creating-a-github-token)
43
+ - [Quick start](#quick-start)
44
+ - [Usage](#usage)
45
+ - [One-off runs](#one-off-runs)
46
+ - [Running in the background](#running-in-the-background)
47
+ - [The menu-bar / tray icon](#the-menu-bar--tray-icon)
48
+ - [Command reference](#command-reference)
49
+ - [Changing settings](#changing-settings)
50
+ - [Where PRonto keeps things](#where-pronto-keeps-things)
51
+ - [Uninstalling](#uninstalling)
52
+ - [Troubleshooting](#troubleshooting)
53
+ - [FAQ](#faq)
54
+ - [Development](#development)
55
+ - [License](#license)
56
+
57
+ ---
58
+
59
+ ## Features
60
+
61
+ - **Automatic "Update branch".** Merges the base branch into each of your open PRs that's behind, exactly as the GitHub button does.
62
+ - **Safe by design.** PRs that are already up to date, or that have merge conflicts, are left alone. PRonto never force-pushes, rebases, or resolves conflicts.
63
+ - **Runs in the background.** One command registers PRonto with your operating system's scheduler (launchd on macOS, Task Scheduler on Windows, cron on Linux). It keeps running across reboots, with no terminal window left open.
64
+ - **Menu-bar / system-tray icon.** Turn PRonto on or off, change how often it checks, limit it to recent PRs, run it on demand, and open the log, all from an icon.
65
+ - **Ignore old PRs.** Limit PRonto to PRs opened in the last *N* days, so that a forgotten PR from two years ago doesn't get a merge commit every 15 minutes.
66
+ - **Dry-run mode.** See which PRs PRonto would touch without changing anything.
67
+ - **Cross-platform.** macOS, Windows and Linux.
68
+ - **Small and auditable.** About 350 lines of Python. The only dependencies are for the tray icon.
69
+
70
+ ## How it works
71
+
72
+ Each run, PRonto:
73
+
74
+ 1. Searches GitHub for open pull requests authored by you (`is:pr is:open author:@me archived:false`). If you set a max age, it adds `created:>=<date>`.
75
+ 2. Calls GitHub's [update-branch API](https://docs.github.com/en/rest/pulls/pulls#update-a-pull-request-branch) for each one. This is the same action as clicking **Update branch → Update with merge commit**.
76
+ 3. GitHub decides the outcome:
77
+ - **Branch is behind:** GitHub merges the base branch into it → `updated`
78
+ - **Already up to date:** nothing happens → `skipped … no new commits`
79
+ - **Has conflicts:** nothing happens → `skipped … merge conflict`
80
+
81
+ PRonto doesn't use git and never touches your local clones. Everything happens on GitHub's side.
82
+
83
+ ## Requirements
84
+
85
+ - **Python 3.9 or newer**
86
+ - **A GitHub account** on github.com (GitHub Enterprise Server isn't supported yet)
87
+ - **A GitHub personal access token** ([see below](#creating-a-github-token)), or the [`gh` CLI](https://cli.github.com) logged in
88
+
89
+ ## Installation
90
+
91
+ ### Recommended: `uv` or `pipx`
92
+
93
+ These install PRonto as a standalone command, in its own isolated environment:
94
+
95
+ ```sh
96
+ uv tool install pronto-pr
97
+ # or
98
+ pipx install pronto-pr
99
+ ```
100
+
101
+ Don't have either? Install [uv](https://docs.astral.sh/uv/getting-started/installation/) with `curl -LsSf https://astral.sh/uv/install.sh | sh` (macOS/Linux) or `winget install astral-sh.uv` (Windows).
102
+
103
+ > The package is called **`pronto-pr`** because `pronto` was already taken on PyPI. The command you run is still `pronto`.
104
+
105
+ ### Try it without installing
106
+
107
+ ```sh
108
+ uvx --from pronto-pr pronto --dry-run
109
+ ```
110
+
111
+ ### From source
112
+
113
+ ```sh
114
+ git clone https://github.com/Zachary-Love/PRonto.git
115
+ cd PRonto
116
+ uv tool install -e .
117
+ ```
118
+
119
+ `-e` (editable) means code changes take effect immediately. If you change dependencies in `pyproject.toml`, re-run with `uv tool install -e . --reinstall`.
120
+
121
+ ### Upgrading
122
+
123
+ ```sh
124
+ uv tool upgrade pronto-pr # or: pipx upgrade pronto-pr
125
+ ```
126
+
127
+ If the background job is installed, it picks up the new version on its next run. To update the tray icon, quit it and run `pronto tray`.
128
+
129
+ ## Creating a GitHub token
130
+
131
+ PRonto needs a token that can read your PRs and push merge commits to their branches. Create one at **https://github.com/settings/tokens**.
132
+
133
+ ### Option A: Classic token (simplest)
134
+
135
+ 1. Go to **Generate new token → Generate new token (classic)**.
136
+ 2. Give it a name like `PRonto` and pick an expiration.
137
+ 3. Check the **`repo`** scope.
138
+ 4. Generate it and copy it (it starts with `ghp_`).
139
+
140
+ **Organizations with SAML SSO:** after creating the token, click **Configure SSO** next to it on the tokens page and **Authorize** each organization. Without this, PRonto can't see or update PRs in those orgs.
141
+
142
+ ### Option B: Fine-grained token (tighter permissions)
143
+
144
+ 1. Go to **Generate new token → Fine-grained token**.
145
+ 2. Set **Resource owner** to the account or organization that owns the repos.
146
+ 3. Under **Repository access**, choose the repos (or **All repositories**).
147
+ 4. Under **Repository permissions**, set:
148
+ - **Contents:** Read and write
149
+ - **Pull requests:** Read and write
150
+ 5. Generate it and copy it (it starts with `github_pat_`).
151
+
152
+ A fine-grained token only covers **one** resource owner. If your PRs are spread across several orgs, or across your personal account and an org, use a classic token. Some orgs also require an admin to approve fine-grained tokens.
153
+
154
+ ### Giving the token to PRonto
155
+
156
+ PRonto looks for a token in this order:
157
+
158
+ 1. The `GITHUB_TOKEN` environment variable
159
+ 2. The `GH_TOKEN` environment variable
160
+ 3. The token saved by `pronto install` (`~/.config/pronto/token`)
161
+ 4. The `gh` CLI (`gh auth token`), if it's installed and logged in
162
+
163
+ For a first run, set the environment variable:
164
+
165
+ ```sh
166
+ export GITHUB_TOKEN=ghp_yourtokenhere # macOS / Linux
167
+ $env:GITHUB_TOKEN = "ghp_yourtokenhere" # Windows PowerShell
168
+ ```
169
+
170
+ `pronto install` checks the token with GitHub and saves it, so you only need to do this once.
171
+
172
+ ## Quick start
173
+
174
+ ```sh
175
+ uv tool install pronto-pr
176
+ export GITHUB_TOKEN=ghp_yourtokenhere
177
+
178
+ pronto --dry-run # 1. see which PRs it would look at (changes nothing)
179
+ pronto install # 2. run it every 15 minutes in the background, plus the tray icon
180
+ ```
181
+
182
+ That's it. PRonto runs once right away, then every 15 minutes, and comes back automatically after a reboot. A little dog appears in your menu bar or system tray.
183
+
184
+ ## Usage
185
+
186
+ ### One-off runs
187
+
188
+ ```sh
189
+ pronto --dry-run # list the open PRs PRonto would try; changes nothing
190
+ pronto # update all your out-of-date PRs once, then exit
191
+ pronto --max-age 30 # only PRs opened in the last 30 days
192
+ pronto --every 10 # keep running in this terminal, every 10 minutes (Ctrl+C to stop)
193
+ ```
194
+
195
+ Each PR prints one line: `updated`, or `skipped` with GitHub's reason.
196
+
197
+ ### Running in the background
198
+
199
+ ```sh
200
+ pronto install # every 15 minutes, all PRs, with the tray icon
201
+ pronto install --every 30 --max-age 60 # every 30 minutes, only PRs opened in the last 60 days
202
+ pronto install --no-tray # background job only, no icon (good for servers)
203
+ ```
204
+
205
+ `pronto install`:
206
+
207
+ 1. Finds your token and checks it with GitHub (it fails right away if the token is bad).
208
+ 2. Saves the token to `~/.config/pronto/token`, readable only by your user.
209
+ 3. Registers a background job with your OS:
210
+
211
+ | OS | Mechanism | Runs while |
212
+ |---------|------------------------------------|----------------------|
213
+ | macOS | launchd user agent | you're logged in |
214
+ | Windows | Task Scheduler task named `pronto` | you're logged in |
215
+ | Linux | a line in your crontab | the machine is on |
216
+
217
+ 4. Starts the tray icon and sets it to open at login (unless you pass `--no-tray`).
218
+
219
+ Every run is logged to `~/.config/pronto/pronto.log`.
220
+
221
+ While your computer is asleep, nothing runs. PRonto catches up at the next scheduled run after it wakes.
222
+
223
+ ### The menu-bar / tray icon
224
+
225
+ `pronto install` starts the icon for you. If you quit it, start it again with:
226
+
227
+ ```sh
228
+ pronto tray
229
+ ```
230
+
231
+ The icon is a black-and-white dog in profile with a **green dot** when PRonto is on, and no dot when it's off. On macOS it's a native template icon, so it turns white or black to match your menu bar. Its menu:
232
+
233
+ | Menu item | What it does |
234
+ |-----------------|--------------|
235
+ | **Enabled** | Turns the background job on or off. |
236
+ | **Max PR age** | Only update PRs opened within: Any age, 7 days, 30 days, 90 days, or 1 year. |
237
+ | **Check every** | How often the background job runs: 5 min, 15 min, 30 min, or 1 hour. |
238
+ | **Run now** | Runs immediately and shows a notification with how many PRs were updated. |
239
+ | **Open log** | Opens `pronto.log` in your default text viewer. |
240
+ | **Quit** | Closes the icon. Updates keep happening in the background. |
241
+
242
+ Notes:
243
+ - The icon is a **remote control** for the background job, not the job itself. Quitting the icon doesn't stop updates; use **Enabled** or `pronto uninstall` for that.
244
+ - The icon opens automatically at login, and only one copy runs at a time.
245
+ - The menu and the command line share the same settings, so you can switch between them freely.
246
+ - **Linux:** the icon needs a desktop with system-tray support. GNOME needs the *AppIndicator* extension.
247
+
248
+ ## Command reference
249
+
250
+ ```
251
+ pronto [command] [options]
252
+ ```
253
+
254
+ | Command | Description |
255
+ |-------------|-------------|
256
+ | `run` | *(default)* Update your out-of-date PRs now. |
257
+ | `install` | Save your token, set up the background job, and start the tray icon. Run it again to change settings. |
258
+ | `uninstall` | Remove the background job and the tray icon's login entry, and delete the saved token and settings. |
259
+ | `tray` | Start the menu-bar / tray icon in the background. |
260
+
261
+ | Option | Applies to | Description |
262
+ |-------------------|------------------|-------------|
263
+ | `--dry-run` | `run` | List PRs without updating anything. |
264
+ | `--max-age DAYS` | `run`, `install` | Ignore PRs opened more than `DAYS` days ago. |
265
+ | `--every MIN` | `run`, `install` | Minutes between runs: 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60, 120, 180, 240, 360, 480 or 720. These are the intervals every OS scheduler can run exactly. With `run`, PRonto keeps running in the terminal; without it, it runs once. With `install`, the default is 15. |
266
+ | `--no-tray` | `install` | Don't start the tray icon. |
267
+ | `-h`, `--help` | all | Show help. |
268
+
269
+ Exit codes: `0` on success. `1` if a one-off run can't reach GitHub, or no token is found.
270
+
271
+ ## Changing settings
272
+
273
+ Use the tray menu, or re-run `install` with the options you want:
274
+
275
+ ```sh
276
+ pronto install --every 60 --max-age 14
277
+ ```
278
+
279
+ `install` **replaces** all settings with the ones you pass. Leaving out `--max-age` resets it to "any age", and leaving out `--every` resets it to 15 minutes.
280
+
281
+ To change your token, set `GITHUB_TOKEN` to the new one and run `pronto install` again.
282
+
283
+ ## Where PRonto keeps things
284
+
285
+ All PRonto files live in `~/.config/pronto/` (on Windows: `C:\Users\<you>\.config\pronto\`):
286
+
287
+ | File | Contents |
288
+ |-----------------|----------|
289
+ | `token` | Your GitHub token (permissions `600`: only you can read it). |
290
+ | `settings.json` | Enabled, check interval, and max PR age. |
291
+ | `pronto.log` | Output from every background run. Starts over once it passes 1 MB. |
292
+ | `tray.lock` | Keeps a second tray icon from starting. |
293
+
294
+ Background job and login entries:
295
+
296
+ | OS | Background job | Tray icon at login |
297
+ |---------|------------------------------------------|--------------------|
298
+ | macOS | `~/Library/LaunchAgents/pronto.plist` | `~/Library/LaunchAgents/pronto-tray.plist` |
299
+ | Windows | Task Scheduler → `pronto` | Registry: `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` → `pronto` |
300
+ | Linux | crontab line ending in `# pronto` | `~/.config/autostart/pronto.desktop` |
301
+
302
+ ## Uninstalling
303
+
304
+ ```sh
305
+ pronto uninstall # remove the background job, login entry, saved token and settings
306
+ uv tool uninstall pronto-pr # remove the program (or: pipx uninstall pronto-pr)
307
+ ```
308
+
309
+ If the tray icon is running, choose **Quit** from its menu. To remove the log too, delete `~/.config/pronto/`.
310
+
311
+ You may also want to revoke the token at https://github.com/settings/tokens.
312
+
313
+ ## Troubleshooting
314
+
315
+ **Start with the log:** `~/.config/pronto/pronto.log`, or **Open log** in the tray menu. Each run starts with a `--- YYYY-MM-DD HH:MM` line.
316
+
317
+ **`Set GITHUB_TOKEN (or log in with gh auth login)`**
318
+ No token was found. [Create one](#creating-a-github-token), `export GITHUB_TOKEN=...`, then run `pronto install`.
319
+
320
+ **`HTTP Error 401: Unauthorized`**
321
+ The token is wrong, expired, or revoked. Create a new one and run `pronto install` again.
322
+
323
+ **Some PRs (often in an org) never show up**
324
+ - *Classic token:* authorize it for the org via **Configure SSO** on the tokens page.
325
+ - *Fine-grained token:* make sure the **resource owner** is that org and the repos are included. The org may also need to approve the token.
326
+
327
+ **`skipped … Resource not accessible by personal access token`**
328
+ The token can see the PR but can't push to its branch. Give it **Contents: Read and write** and **Pull requests: Read and write**, or use a classic token with `repo`.
329
+
330
+ **`skipped … merge conflict between base and head`**
331
+ Expected: GitHub can't merge automatically. Resolve the conflict yourself; PRonto picks the PR up again afterwards.
332
+
333
+ **`skipped … There are no new commits on the base branch`**
334
+ Expected: the PR is already up to date.
335
+
336
+ **`Tray icon not available: No module named 'pystray'`**
337
+ Your install is missing its dependencies. This usually happens with an editable (`-e`) install made before dependencies changed. Reinstall:
338
+ ```sh
339
+ uv tool install pronto-pr --reinstall # from PyPI
340
+ uv tool install -e . --reinstall # from a source checkout
341
+ ```
342
+
343
+ **`Tray icon not available (…); the background job is still installed.`**
344
+ PRonto couldn't show an icon, usually because there's no desktop (for example a server or SSH session). Updates still run. Use `pronto install --no-tray` to skip the icon.
345
+
346
+ **The icon doesn't appear on Linux**
347
+ Your desktop needs tray support. On GNOME, install the *AppIndicator and KStatusNotifierItem Support* extension.
348
+
349
+ **No "Run now" notification on macOS**
350
+ Notifications are sent through macOS's built-in scripting tool, so they appear under **Script Editor**. Allow them in **System Settings → Notifications → Script Editor**.
351
+
352
+ **Windows laptop: nothing runs on battery**
353
+ Task Scheduler's defaults skip tasks while on battery. Open **Task Scheduler → pronto → Properties → Conditions** and uncheck **Start the task only if the computer is on AC power**.
354
+
355
+ **Check that the background job is registered**
356
+ ```sh
357
+ launchctl list | grep pronto # macOS
358
+ schtasks /Query /TN pronto # Windows
359
+ crontab -l | grep pronto # Linux
360
+ ```
361
+
362
+ **Debug the tray icon**
363
+ Run it attached to your terminal so errors print directly:
364
+ ```sh
365
+ pronto tray --foreground
366
+ ```
367
+
368
+ ## FAQ
369
+
370
+ **Does PRonto rebase my branches?**
371
+ No. It only uses the "merge commit" form of Update branch. It never rebases, force-pushes, or rewrites history.
372
+
373
+ **Will this trigger CI on every update?**
374
+ Yes. Each update is a new commit on your PR branch, so CI runs just as it would after clicking the button. Use `--max-age` or a longer `--every` if that's too much.
375
+
376
+ **Which PRs does it look at?**
377
+ Open PRs that **you** authored, in repositories that aren't archived, including drafts. It doesn't touch other people's PRs.
378
+
379
+ **What about PRs from two years ago that I forgot about?**
380
+ Use `--max-age`, or **Max PR age** in the tray menu. For example, `--max-age 30` only considers PRs opened in the last 30 days.
381
+
382
+ **Does it work when my laptop is closed?**
383
+ No. It runs on your machine, so it only works while the machine is awake (and, on macOS and Windows, while you're logged in). It catches up at the next scheduled run.
384
+
385
+ **Will it hit GitHub's rate limits?**
386
+ Unlikely. Each run makes one search request per 100 PRs, plus one request per PR. Even every 5 minutes, that's well within GitHub's limits for normal use.
387
+
388
+ **Is my token safe?**
389
+ It's stored in a file only your user can read, sent only to `api.github.com`, and never logged. `pronto uninstall` deletes it. A classic token with `repo` scope is powerful, so set an expiration, or use a fine-grained token if your PRs live under one owner.
390
+
391
+ **Does it work with GitHub Enterprise Server?**
392
+ Not yet: the API URL is fixed to `api.github.com`.
393
+
394
+ ## Development
395
+
396
+ ```sh
397
+ git clone https://github.com/Zachary-Love/PRonto.git
398
+ cd PRonto
399
+ uv tool install -e . # installs the `pronto` command from your checkout
400
+ python3 test_pronto.py # prints "ok"
401
+ ```
402
+
403
+ Project layout:
404
+
405
+ | File | Purpose |
406
+ |-------------------|---------|
407
+ | `pronto.py` | CLI, GitHub API calls, background-job and login-item setup |
408
+ | `pronto_tray.py` | Menu-bar / tray icon |
409
+ | `test_pronto.py` | Scheduler and login-item tests (fakes OS calls; touches nothing real) |
410
+ | `pyproject.toml` | Packaging metadata |
411
+
412
+ A safe way to try changes end to end: create a throwaway repo, open a PR, then push a commit to `main` so the PR falls behind. `pronto --max-age 1` should then report it as `updated`.
413
+
414
+ ### Publishing a release
415
+
416
+ 1. Bump `version` in `pyproject.toml`.
417
+ 2. `uv build`
418
+ 3. `uv publish --token pypi-...`
419
+
420
+ ## License
421
+
422
+ [MIT](LICENSE) © 2026 Zachary Love