Created
May 24, 2026 14:24
-
-
Save bitbybit/a6019a37364b2a9c69095b8b334fca89 to your computer and use it in GitHub Desktop.
Removing yourself from every repository where you are a collaborator on GitHub
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Run this in the browser console on the https://github.com/settings/repositories page | |
| (async function massLeaveRepos() { | |
| const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); | |
| // Helper 1: Waits for element to exist AND enter the viewport | |
| const waitForVisible = (selector) => { | |
| return new Promise((resolve) => { | |
| // First, wait for the element to actually exist in the DOM | |
| const checkExist = setInterval(() => { | |
| const el = document.querySelector(selector); | |
| if (el && !el.disabled) { | |
| clearInterval(checkExist); | |
| // Once it exists, observe it to see when it paints on screen | |
| const observer = new IntersectionObserver((entries) => { | |
| if (entries[0].isIntersecting) { | |
| observer.disconnect(); // Stop observing once found | |
| resolve(el); | |
| } | |
| }); | |
| observer.observe(el); | |
| } | |
| }, 100); | |
| }); | |
| }; | |
| // Helper 2: Waits for element to leave the viewport or be destroyed | |
| const waitForHidden = (selector) => { | |
| return new Promise((resolve) => { | |
| const el = document.querySelector(selector); | |
| if (!el) return resolve(); // Already gone | |
| let isResolved = false; | |
| const observer = new IntersectionObserver((entries) => { | |
| if (!entries[0].isIntersecting) { | |
| observer.disconnect(); | |
| if (!isResolved) { | |
| isResolved = true; | |
| resolve(); | |
| } | |
| } | |
| }); | |
| observer.observe(el); | |
| // Backup: IntersectionObserver can sometimes be flaky if an element | |
| // is ripped completely out of the DOM rather than just hidden. | |
| const checkRemoved = setInterval(() => { | |
| if (!document.querySelector(selector)) { | |
| observer.disconnect(); | |
| clearInterval(checkRemoved); | |
| if (!isResolved) { | |
| isResolved = true; | |
| resolve(); | |
| } | |
| } | |
| }, 100); | |
| }); | |
| }; | |
| const leaveButtons = Array.from(document.querySelectorAll('[data-repo-id] [role="button"]')); | |
| if (leaveButtons.length === 0) { | |
| console.log("No repositories found to leave."); | |
| return; | |
| } | |
| console.log(`Found ${leaveButtons.length} repositories. Starting process...`); | |
| for (let i = 0; i < leaveButtons.length; i++) { | |
| console.log(`Processing repo ${i + 1} of ${leaveButtons.length}...`); | |
| // 1. Click initial button | |
| leaveButtons[i].click(); | |
| // 2. Wait for the dialog button to enter the viewport | |
| const confirmBtn = await waitForVisible('.js-leave-collaborated-repo .btn'); | |
| // 3. Click the confirmation | |
| confirmBtn.click(); | |
| // 4. Wait for the dialog button to leave the viewport | |
| await waitForHidden('.js-leave-collaborated-repo .btn'); | |
| // 5. Safety delay before the next iteration | |
| await sleep(600); | |
| } | |
| console.log("Finished leaving all repositories on this page!"); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment