Skip to content

Instantly share code, notes, and snippets.

@escherize
Last active April 24, 2025 16:46
Show Gist options
  • Save escherize/daeda2467ee60e5255a4c24212041036 to your computer and use it in GitHub Desktop.
Save escherize/daeda2467ee60e5255a4c24212041036 to your computer and use it in GitHub Desktop.
violent monkey script for ci rerun
// ==UserScript==
// @name GitHub Rerun Failed Jobs (Manual Trigger + Watcher) @escherize
// @namespace http://tampermonkey.net/
// @version 0.7
// @description Manually trigger a watcher to auto re-run failed GitHub Actions jobs
// @match https://github.com/*/*/actions/runs/**
// @grant none
// ==/UserScript==
(function () {
'use strict';
const waitForTextButton = (text, timeout = 60000) => new Promise((resolve, reject) => {
const interval = 300;
const maxTries = timeout / interval;
let tries = 0;
const timer = setInterval(() => {
const btn = Array.from(document.querySelectorAll('span'))
.find(el => el.textContent.trim() === text);
if (btn) {
clearInterval(timer);
resolve(btn);
} else if (++tries > maxTries) {
clearInterval(timer);
reject(`Timeout waiting for button: ${text}`);
}
}, interval);
});
const tryRerun = async () => {
console.log('[ManualRerun] Waiting for "Re-run jobs"...');
const rerunButton = await waitForTextButton('Re-run jobs');
console.log('[ManualRerun] Clicking dropdown...');
rerunButton.click();
console.log('[ManualRerun] Waiting for "Re-run failed jobs"...');
const failedJobsBtn = await waitForTextButton('Re-run failed jobs');
failedJobsBtn.click();
setTimeout(() => {
const buttons = Array.from(document.querySelectorAll('button'))
.filter(btn => btn.textContent.trim() === 'Re-run jobs')
.reverse();
console.log(`[ManualRerun] Clicking ${buttons.length} "Re-run jobs" buttons...`);
buttons.forEach(btn => btn.click());
}, 1000);
};
let observer;
const startWatcher = () => {
if (observer) {
console.log('[ManualRerun] Watcher already active.');
return;
}
triggerButton.textContent = '👀 Watching for Re-run...';
observer = new MutationObserver(() => {
const found = Array.from(document.querySelectorAll('button'))
.some(btn => btn.textContent.trim() === 'Re-run jobs');
if (found) {
observer.disconnect();
observer = null;
triggerButton.textContent = '✅ Re-run Triggered!';
tryRerun().catch(err => {
console.error('[ManualRerun] Failed:', err);
triggerButton.textContent = '❌ Re-run Failed';
});
}
});
observer.observe(document.body, { childList: true, subtree: true });
console.log('[ManualRerun] Watcher started...');
};
const triggerButton = document.createElement('button');
triggerButton.textContent = '▶️ Start Re-run Watcher';
triggerButton.style.position = 'fixed';
triggerButton.style.top = '10px';
triggerButton.style.right = '10px';
triggerButton.style.zIndex = 9999;
triggerButton.style.padding = '8px 12px';
triggerButton.style.fontSize = '14px';
triggerButton.style.background = '#0366d6';
triggerButton.style.color = 'white';
triggerButton.style.border = 'none';
triggerButton.style.borderRadius = '6px';
triggerButton.style.cursor = 'pointer';
document.body.appendChild(triggerButton);
triggerButton.addEventListener('click', startWatcher);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment