prwatcher 2.0.0 → 2.1.0
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.
- package/README.md +34 -5
- package/bin/watch-pr.js +2 -1
- package/package.json +1 -1
- package/src/cli.js +22 -1
- package/src/github.js +16 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# prwatcher
|
|
2
2
|
|
|
3
|
-
CLI tool that watches GitHub PRs and sends Slack notifications when CI passes, fails, or the PR is ready to merge.
|
|
3
|
+
CLI tool that watches GitHub PRs and sends Slack notifications when CI passes, fails, or the PR is ready to merge. Optionally auto-merges when all checks pass.
|
|
4
4
|
|
|
5
5
|
**Zero setup for developers** — just run one command. No Slack app creation, no servers, no ngrok.
|
|
6
6
|
|
|
@@ -29,17 +29,39 @@ Or use directly with npx:
|
|
|
29
29
|
npx prwatcher https://github.com/owner/repo/pull/123
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
### GitHub Token Setup
|
|
33
|
+
|
|
34
|
+
On first run you'll be prompted for a GitHub personal access token. Here's what permissions it needs depending on how you use the tool:
|
|
35
|
+
|
|
36
|
+
#### Watching only (no auto-merge)
|
|
37
|
+
|
|
38
|
+
**Classic token** — check `repo` scope
|
|
39
|
+
|
|
40
|
+
**Fine-grained token** — enable:
|
|
41
|
+
- `Metadata` → Read-only (required)
|
|
42
|
+
- `Pull requests` → Read-only
|
|
43
|
+
|
|
44
|
+
#### Watching + Auto-merge (`--auto-merge`)
|
|
45
|
+
|
|
46
|
+
**Classic token** — check `repo` scope
|
|
47
|
+
|
|
48
|
+
**Fine-grained token** — enable:
|
|
49
|
+
- `Metadata` → Read-only (required)
|
|
50
|
+
- `Pull requests` → Read and write
|
|
51
|
+
- `Contents` → Read and write ← required for merging
|
|
52
|
+
|
|
53
|
+
> Your token is saved to `~/.watch-pr` on your machine with owner-only permissions (0600). It is never sent anywhere except the GitHub API.
|
|
54
|
+
|
|
32
55
|
### Usage
|
|
33
56
|
|
|
34
57
|
```bash
|
|
35
58
|
prwatcher https://github.com/owner/repo/pull/123
|
|
36
59
|
```
|
|
37
60
|
|
|
38
|
-
On first run, you'll be prompted for:
|
|
39
|
-
|
|
40
|
-
2. **Slack webhook URL** — your team's [Incoming Webhook](https://api.slack.com/messaging/webhooks)
|
|
61
|
+
On first run, you'll also be prompted for:
|
|
62
|
+
- **Slack webhook URL** — your team's [Incoming Webhook](https://api.slack.com/messaging/webhooks)
|
|
41
63
|
|
|
42
|
-
|
|
64
|
+
Both are saved to `~/.watch-pr` so you only enter them once.
|
|
43
65
|
|
|
44
66
|
### Options
|
|
45
67
|
|
|
@@ -48,6 +70,7 @@ prwatcher <pr-url> [options]
|
|
|
48
70
|
|
|
49
71
|
Options:
|
|
50
72
|
--interval <minutes> Polling interval in minutes (default: 1)
|
|
73
|
+
--auto-merge Automatically merge PR once all required checks pass
|
|
51
74
|
--reset-config Re-enter GitHub token and Slack webhook URL
|
|
52
75
|
-V, --version Output version number
|
|
53
76
|
-h, --help Display help
|
|
@@ -59,6 +82,9 @@ Options:
|
|
|
59
82
|
# Watch a PR with default 1-minute polling
|
|
60
83
|
prwatcher https://github.com/org/repo/pull/42
|
|
61
84
|
|
|
85
|
+
# Watch and auto-merge when ready
|
|
86
|
+
prwatcher https://github.com/org/repo/pull/42 --auto-merge
|
|
87
|
+
|
|
62
88
|
# Poll every 30 seconds
|
|
63
89
|
prwatcher https://github.com/org/repo/pull/42 --interval 0.5
|
|
64
90
|
|
|
@@ -78,8 +104,10 @@ You'll get Slack messages when:
|
|
|
78
104
|
| Required CI checks fail | ⚠️ CI checks failed (lists which ones) |
|
|
79
105
|
| PR needs rebase | 🔄 PR needs rebase |
|
|
80
106
|
| PR is ready to merge | ✅ PR is ready to merge! |
|
|
107
|
+
| PR is auto-merged | 🎉 PR was auto-merged! |
|
|
81
108
|
| PR is merged | 🎉 PR was merged! |
|
|
82
109
|
| PR is closed | ❌ PR was closed without merging |
|
|
110
|
+
| Auto-merge failed | ⚠️ Auto-merge failed (with reason) |
|
|
83
111
|
|
|
84
112
|
Notifications only fire on **state changes** — no spam.
|
|
85
113
|
|
|
@@ -113,6 +141,7 @@ Detect state change (CI failed, ready, merged, etc.)
|
|
|
113
141
|
↓
|
|
114
142
|
POST to Slack webhook
|
|
115
143
|
↓
|
|
144
|
+
--auto-merge? → merge via GitHub API → exit
|
|
116
145
|
PR merged/closed? → exit
|
|
117
146
|
```
|
|
118
147
|
|
package/bin/watch-pr.js
CHANGED
|
@@ -6,9 +6,10 @@ const { run } = require('../src/cli');
|
|
|
6
6
|
program
|
|
7
7
|
.name('prwatcher')
|
|
8
8
|
.description('Watch a GitHub PR and get Slack notifications on state changes')
|
|
9
|
-
.version('2.
|
|
9
|
+
.version('2.1.0')
|
|
10
10
|
.argument('<pr-url>', 'GitHub PR URL (e.g., https://github.com/owner/repo/pull/123)')
|
|
11
11
|
.option('--interval <minutes>', 'polling interval in minutes', '1')
|
|
12
|
+
.option('--auto-merge', 'automatically merge PR once all required checks pass')
|
|
12
13
|
.option('--reset-config', 're-enter GitHub token and Slack webhook URL')
|
|
13
14
|
.action((prUrl, options) => {
|
|
14
15
|
run(prUrl, options);
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { parsePRUrl, buildPRKey } = require('./utils');
|
|
2
2
|
const { ensureConfig } = require('./config');
|
|
3
|
-
const { createClient, fetchRequiredChecks, fetchPRState } = require('./github');
|
|
3
|
+
const { createClient, fetchRequiredChecks, fetchPRState, mergePR } = require('./github');
|
|
4
4
|
const { sendNotification, formatMerged, formatClosed, formatCIFailed, formatRebaseNeeded, formatReady } = require('./slack');
|
|
5
5
|
|
|
6
6
|
async function run(prUrl, options = {}) {
|
|
@@ -70,6 +70,9 @@ async function run(prUrl, options = {}) {
|
|
|
70
70
|
const intervalMs = intervalMinutes * 60 * 1000;
|
|
71
71
|
|
|
72
72
|
console.log(` Polling every ${intervalMinutes >= 1 ? `${intervalMinutes} minute(s)` : `${intervalMinutes * 60} seconds`}`);
|
|
73
|
+
if (options.autoMerge) {
|
|
74
|
+
console.log(' ⚠ Auto-merge is ON — PR will be merged automatically when ready');
|
|
75
|
+
}
|
|
73
76
|
console.log(' Press Ctrl+C to stop\n');
|
|
74
77
|
|
|
75
78
|
// 6. Poll function
|
|
@@ -127,6 +130,24 @@ async function run(prUrl, options = {}) {
|
|
|
127
130
|
console.log(` ✅ ${timestamp} — Ready to merge!`);
|
|
128
131
|
await sendNotification(config.slackWebhookUrl, formatReady(state.htmlUrl, state.title));
|
|
129
132
|
lastState = 'ready';
|
|
133
|
+
|
|
134
|
+
if (options.autoMerge) {
|
|
135
|
+
try {
|
|
136
|
+
console.log(` 🔀 ${timestamp} — Auto-merging...`);
|
|
137
|
+
await mergePR(octokit, { owner, repo, prNumber });
|
|
138
|
+
console.log(` 🎉 ${timestamp} — Auto-merged!`);
|
|
139
|
+
await sendNotification(config.slackWebhookUrl, `🎉 *PR was auto-merged!*\n<${state.htmlUrl}|${state.title}>`);
|
|
140
|
+
cleanup();
|
|
141
|
+
process.exit(0);
|
|
142
|
+
} catch (mergeError) {
|
|
143
|
+
const mergeMsg = mergeError.response?.data?.message || mergeError.message;
|
|
144
|
+
const hint = mergeError.status === 403
|
|
145
|
+
? ' (GitHub token needs "repo" scope or "Pull requests: write" permission)'
|
|
146
|
+
: '';
|
|
147
|
+
console.error(` ⚠ ${timestamp} — Auto-merge failed: ${mergeMsg}${hint}`);
|
|
148
|
+
await sendNotification(config.slackWebhookUrl, `⚠️ *Auto-merge failed*\n<${state.htmlUrl}|${state.title}>\nReason: ${mergeMsg}${hint}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
130
151
|
} else {
|
|
131
152
|
console.log(` ✅ ${timestamp} — Still ready (already notified)`);
|
|
132
153
|
}
|
package/src/github.js
CHANGED
|
@@ -115,8 +115,23 @@ async function fetchPRState(octokit, { owner, repo, prNumber, requiredCheckNames
|
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Merge a PR via GitHub API.
|
|
120
|
+
* Returns { merged: true } on success, or throws on failure.
|
|
121
|
+
*/
|
|
122
|
+
async function mergePR(octokit, { owner, repo, prNumber }) {
|
|
123
|
+
const { data } = await octokit.pulls.merge({
|
|
124
|
+
owner,
|
|
125
|
+
repo,
|
|
126
|
+
pull_number: prNumber,
|
|
127
|
+
merge_method: 'merge'
|
|
128
|
+
});
|
|
129
|
+
return data;
|
|
130
|
+
}
|
|
131
|
+
|
|
118
132
|
module.exports = {
|
|
119
133
|
createClient,
|
|
120
134
|
fetchRequiredChecks,
|
|
121
|
-
fetchPRState
|
|
135
|
+
fetchPRState,
|
|
136
|
+
mergePR
|
|
122
137
|
};
|