-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathAutoMergeDependabotPRs.user.js
More file actions
199 lines (176 loc) · 5.72 KB
/
AutoMergeDependabotPRs.user.js
File metadata and controls
199 lines (176 loc) · 5.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
// ==UserScript==
// @name Merge Dependabot PRs Automatically on GitHub with UI and Selection Options
// @namespace nick2bad4u.github.io
// @version 2.3
// @description Automatically clicks the merge button on Dependabot PRs and "Done" button on the notification bar
// @author Nick2bad4u
// @match https://github.com/*/*/pull/*
// @grant none
// @license UnLicense
// @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
// @homepageURL https://github.com/Nick2bad4u/UserStyles
// @homepage https://github.com/Nick2bad4u/UserStyles
// @supportURL https://github.com/Nick2bad4u/UserStyles/issues
// @downloadURL https://github.com/Nick2bad4u/UserStyles/raw/refs/heads/main/AutoMergeDependabotPRs.user.js
// @updateURL https://github.com/Nick2bad4u/UserStyles/raw/refs/heads/main/AutoMergeDependabotPRs.user.js
// ==/UserScript==
(function () {
'use strict';
const CHECK_INTERVAL = 1000; // Interval between checks in milliseconds
let lastCheck = 0;
let observer;
let token = getTokenFromCookies() || promptForToken();
if (!token) {
while (!token) {
token = prompt('Please enter your GitHub token:');
if (token) {
document.cookie = `github_token=${token}; path=/; max-age=${60 * 60 * 24 * 365}`;
} else {
alert('GitHub token is required.');
}
}
}
if (!token) {
alert('GitHub token is required for this script to work.');
return;
}
function getTokenFromCookies() {
const match = document.cookie.match(/(^| )\s*github_token\s*=\s*([^;]+)/);
return match ? match[2] : null;
}
function promptForToken() {
let token = prompt('Please enter your GitHub token. You can generate a token at https://github.com/settings/tokens');
if (token) {
document.cookie = `github_token=${token}; path=/; max-age=${60 * 60 * 24 * 365}`;
}
return token;
}
const debounce = (func, delay) => {
let debounceTimer;
return function () {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => func.apply(this, arguments), delay);
};
};
const debouncedCheckAndMerge = debounce(checkAndMerge, 300);
async function checkAndMerge() {
const now = Date.now();
if (now - lastCheck < CHECK_INTERVAL) return;
lastCheck = now;
console.log('checkAndMerge function called');
try {
const authorElement = document.querySelector('.author');
if (authorElement && /^(dependabot\[bot\]|Nick2bad4u)$/.test(authorElement.textContent.trim())) {
const prNumber = window.location.pathname.split('/').pop();
const repoPath = window.location.pathname.split('/').slice(1, 3).join('/');
console.log('PR is created by dependabot or specified user, attempting to merge via API');
const mergeSuccess = await mergePR(repoPath, prNumber);
if (mergeSuccess) {
console.log('PR merged successfully');
void markAsDone();
} else {
console.log('Failed to merge PR');
}
} else {
console.log('PR is not created by dependabot or specified user');
}
} catch (error) {
console.error('Error in checkAndMerge:', error);
}
}
async function mergePR(repoPath, prNumber) {
try {
const response = await fetch(`https://api.github.com/repos/${repoPath}/pulls/${prNumber}/merge`, {
method: 'PUT',
headers: {
Authorization: `token ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
commit_title: `Merge PR #${prNumber}`,
merge_method: 'merge',
}),
});
if (response.ok) {
return true;
} else {
const errorData = await response.json();
console.error('Failed to merge PR:', errorData);
return false;
}
} catch (error) {
console.error('Error in mergePR:', error);
return false;
}
}
async function markAsDone() {
try {
const notificationBar = document.querySelector('.js-flash-container');
if (!notificationBar) {
console.log('Notification bar not found');
return;
}
let doneButton = document.querySelector('button[aria-label="Done"].btn.btn-sm');
if (!doneButton) {
doneButton = Array.from(document.querySelectorAll('button.btn.btn-sm')).find((button) => button.textContent.trim() === 'Done');
}
if (doneButton) {
console.log('Done button found, clicking it');
doneButton.click();
} else {
console.log('Done button not found, attempting to mark as done via API');
const notificationId = getNotificationId();
if (notificationId) {
const response = await fetch(`https://api.github.com/notifications/threads/${notificationId}`, {
method: 'PATCH',
headers: {
Authorization: `token ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
state: 'done',
}),
});
if (response.ok) {
console.log('Notification marked as done via API');
} else {
const errorData = await response.json();
console.error('Failed to mark notification as done via API:', errorData);
}
} else {
console.log('Notification ID not found');
}
}
} catch (error) {
console.error('Error in markAsDone:', error);
}
}
/**
* Retrieves the notification ID from the URL parameters.
*/
function getNotificationId() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('notification_id');
}
globalThis.addEventListener(
'load',
function () {
console.log('Page loaded');
const targetNode = document.querySelector('.gh-header-meta');
if (!targetNode) {
console.log('Target node for observation not found');
return;
}
observer = new MutationObserver(() => {
console.log('Relevant DOM mutation detected');
debouncedCheckAndMerge();
});
observer.observe(targetNode, {
childList: true,
subtree: true,
});
void checkAndMerge();
},
false,
);
})();