├── package.json ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── README.md ├── LICENSE ├── drawer.js ├── .gitignore ├── yarn.lock └── spoon.js /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "axios": "^0.18.0", 4 | "bluebird": "^3.5.4", 5 | "dotenv": "^7.0.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/README.md: -------------------------------------------------------------------------------- 1 | # spoon 2 | 3 | [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) 4 | TODO: Put more badges here. 5 | 6 | > The GitHub anti-fork 7 | 8 | TODO: Fill out this long description. 9 | 10 | ## Table of Contents 11 | 12 | - [Background](#background) 13 | - [Install](#install) 14 | - [Usage](#usage) 15 | - [Maintainers](#maintainers) 16 | - [Contribute](#contribute) 17 | - [License](#license) 18 | 19 | ## Background 20 | 21 | ## Install 22 | 23 | ``` 24 | ``` 25 | 26 | ## Usage 27 | 28 | ``` 29 | ``` 30 | 31 | ## Maintainers 32 | 33 | [@nickcannariato](https://github.com/nickcannariato) 34 | [@BrandonGardner](https://github.com/brandongardner2) 35 | 36 | ## Contribute 37 | 38 | PRs accepted. 39 | 40 | Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. 41 | 42 | ## License 43 | 44 | MIT © 2019 Nick Cannariato 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Nick Cannariato 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. 22 | -------------------------------------------------------------------------------- /drawer.js: -------------------------------------------------------------------------------- 1 | /* 2 | Welcome to the drawer. 3 | Please DO NOT run this script if you have not confirmed all of your repos were successfully created and imported. 4 | This script delete ALL of the repositories we marked as forked that are found in the backups.json file. 5 | If you do NOT want a repository deleted, please remove the object from backups.json. 6 | This is NOT reversible. 7 | */ 8 | require("dotenv").config(); 9 | const bluebird = require("bluebird"); 10 | const axios = require("axios"); 11 | const bakRepos = require("./backups.json"); 12 | 13 | const token = process.env.GHKEY; //YOUR API KEY HERE. 14 | 15 | axios.interceptors.request.use(config => { 16 | config.headers.authorization = `bearer ${token}`; 17 | return config; 18 | }); 19 | 20 | function main(repos) { 21 | console.log("Starting to delete -bak repos..."); 22 | bluebird 23 | .each(repos, repo => { 24 | return axios.delete(`https://api.github.com/repos/${repo.full_name}`); 25 | }) 26 | .then(() => { 27 | console.log("All -bak repos have been successfully deleted."); 28 | }) 29 | .catch(error => { 30 | console.log("Something went wrong in the -bak deletion.", error); 31 | }); 32 | } 33 | 34 | main(bakRepos); 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # user specific back ups 64 | backups.json 65 | filteredRepos.json 66 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | axios@^0.18.0: 6 | version "0.18.0" 7 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102" 8 | integrity sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI= 9 | dependencies: 10 | follow-redirects "^1.3.0" 11 | is-buffer "^1.1.5" 12 | 13 | bluebird@^3.5.4: 14 | version "3.5.4" 15 | resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.4.tgz#d6cc661595de30d5b3af5fcedd3c0b3ef6ec5714" 16 | integrity sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw== 17 | 18 | debug@^3.2.6: 19 | version "3.2.6" 20 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" 21 | integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== 22 | dependencies: 23 | ms "^2.1.1" 24 | 25 | dotenv@^7.0.0: 26 | version "7.0.0" 27 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c" 28 | integrity sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g== 29 | 30 | follow-redirects@^1.3.0: 31 | version "1.7.0" 32 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" 33 | integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== 34 | dependencies: 35 | debug "^3.2.6" 36 | 37 | is-buffer@^1.1.5: 38 | version "1.1.6" 39 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 40 | integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== 41 | 42 | ms@^2.1.1: 43 | version "2.1.1" 44 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 45 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== 46 | -------------------------------------------------------------------------------- /spoon.js: -------------------------------------------------------------------------------- 1 | /* 2 | This script has been designed to use your provided GitHub API key and procedurally replace your forks with repos that you own. 3 | This provides you with all of your commit contributions that you may have missed out on. 4 | The creators of this are not responsible for any errors that you may make by altering the script. 5 | Please DO NOT use the drawer script until you are positive that your back-up forks and new repositories are made. 6 | 7 | Thanks, 8 | -Nick & Brandon 9 | 10 | P.S. - It would also be a REALLY good idea to make sure that any unmerged PRs in your forked repos are merged _BEFORE_ running 11 | this script! 12 | */ 13 | require("dotenv").config(); 14 | const fs = require("fs"); 15 | const axios = require("axios"); 16 | const bluebird = require("bluebird"); 17 | 18 | const token = process.env.GHKEY; // YOUR API KEY HERE. 19 | const user = "fake-user"; // YOUR GITHUB HANDLE HERE 20 | 21 | axios.interceptors.request.use(config => { 22 | config.headers.authorization = `bearer ${token}`; 23 | config.headers.accept = "application/vnd.github.barred-rock-preview"; 24 | return config; 25 | }); 26 | 27 | const main = async () => { 28 | console.log("Fetching forked repos..."); 29 | const forkedRepos = await fetchRepos(); 30 | console.log("Fetch complete."); 31 | console.log( 32 | `You have ${ 33 | forkedRepos.length 34 | } forked repositories. Please search -bak after the operation and confirm the number of repos with -bak in the name.` 35 | ); 36 | if (forkedRepos.length === 0) { 37 | console.log('You currently have no forked repos, here\'s a pony!') 38 | return null 39 | } else { 40 | console.log("Creating backup..."); 41 | await genBackup(forkedRepos); 42 | console.log("Backup complete."); 43 | console.log("Renaming existing forks..."); 44 | await renameForks(forkedRepos); 45 | console.log("Renaming complete."); 46 | console.log("Generating new repos..."); 47 | await genRepos(forkedRepos); 48 | console.log("New repos complete."); 49 | console.log("Importing data from forks to new repos..."); 50 | await importData(forkedRepos); 51 | console.log("Import complete!"); 52 | } 53 | }; 54 | 55 | main(); 56 | 57 | //1. We grab a list of ALL of your current forked repos. If there are any that you do NOT want to defork, you will need to filter them manually after our initial filtering. 58 | function fetchRepos() { 59 | return new Promise(async (resolve, reject) => { 60 | try { 61 | //TODO Deal with > 100 repos. 62 | const { data: repos } = await axios.get( 63 | `https://api.github.com/users/${user}/repos?per_page=100` 64 | ); 65 | const filteredRepos = repos.reduce((arr, repo) => { 66 | if (repo.fork) { 67 | arr.push({ 68 | owner: repo.owner.login, 69 | name: repo.name, 70 | full_name: repo.full_name, 71 | clone_url: repo.clone_url, 72 | forked: repo.fork, 73 | description: repo.description 74 | }); 75 | } 76 | return arr; 77 | }, []); 78 | resolve(filteredRepos); 79 | } catch (error) { 80 | console.error("ERROR", error); 81 | reject(error); 82 | } 83 | }); 84 | } 85 | 86 | //2. We generate a list of all of the forked repos with appended names to include -bak. 87 | function genBackup(arr) { 88 | const backups = arr.map(repo => ({ 89 | ...repo, 90 | name: repo.name + "-bak", 91 | full_name: repo.full_name + "-bak" 92 | })); 93 | fs.writeFileSync("./backups.json", JSON.stringify(backups)); 94 | return backups; 95 | } 96 | 97 | //3. We rename the current forked repos on GitHub. 98 | function renameForks(repos) { 99 | return new Promise(async (resolve, reject) => { 100 | bluebird 101 | .each(repos, repo => { 102 | return axios.patch(`https://api.github.com/repos/${repo.full_name}`, { 103 | name: repo.name + "-bak" 104 | }); 105 | }) 106 | .then(() => { 107 | resolve(); 108 | }) 109 | .catch(error => { 110 | console.log("FORK RENAMING ERROR", error); 111 | reject(error); 112 | }); 113 | }); 114 | } 115 | 116 | //4. We generate all new repos using the array of forked repo names. 117 | //TODO Trial run with bluebird, maybe add a timeout after each iteration for safety? 118 | function genRepos(repos) { 119 | return new Promise((resolve, reject) => { 120 | try { 121 | setTimeout(() => { 122 | bluebird 123 | .each(repos, repo => { 124 | return axios.post(`https://api.github.com/user/repos`, { 125 | name: repo.name, 126 | description: repo.description 127 | }); 128 | }) 129 | .then(() => { 130 | resolve(); 131 | }); 132 | }, 500); 133 | } catch (error) { 134 | console.log("REPO CREATION ERROR", error); 135 | reject(); 136 | } 137 | }); 138 | } 139 | 140 | //5. We make a request to the GitHub import endpoint for each repo using the information from our -bak ammended array. This could take a while. 141 | //TODO Figure out how to handle import error "Import already in progress" 142 | function importData(repos) { 143 | return new Promise((resolve, reject) => { 144 | try { 145 | setTimeout(() => { 146 | bluebird 147 | .each(repos, repo => { 148 | return axios.put( 149 | `https://api.github.com/repos/${repo.owner}/${repo.name}/import`, 150 | { 151 | vcs_url: `https://github.com/${repo.full_name}-bak.git`, 152 | vcs: "git" 153 | } 154 | ); 155 | }) 156 | .then(() => { 157 | resolve(); 158 | }) 159 | .catch(error => { 160 | console.log("IMPORT ERROR", error); 161 | reject(); 162 | }); 163 | }, 5000); 164 | } catch (error) { 165 | console.log("IMPORT ERROR", error); 166 | reject(); 167 | } 168 | }); 169 | } 170 | 171 | //6. You should now have all new repos with the original names AND all of your -bak repos that are still forked. Please proceed to drawer.js ONLY if this is true. 172 | 173 | //Old import repo code. Bluebird IS tested and GOOD. 174 | // repos.forEach(async repo => { 175 | // try { 176 | // const res = await axios.put( 177 | // `https://api.github.com/repos/${repo.owner}/${repo.name}/import`, 178 | // { 179 | // vcs_url: `https://github.com/${repo.full_name}-bak.git`, 180 | // vcs: "git" 181 | // } 182 | // ); 183 | // console.log(res); 184 | // } catch (error) { 185 | // console.log("IMPORT ERROR", error); 186 | // reject(); 187 | // } 188 | // }); 189 | 190 | // Old generate new repo code. Bluebird function NOT tested yet 191 | // repos.forEach(async repo => { 192 | // try { 193 | // await axios.post(`https://api.github.com/user/repos`, { 194 | // name: repo.name, 195 | // description: repo.description 196 | // }); 197 | // await new Promise(resolve => { 198 | // setTimeout(resolve, 500); 199 | // }); 200 | // } catch (error) { 201 | // console.log("REPO CREATION ERROR", error); 202 | // reject(); 203 | // } 204 | // }); 205 | 206 | // Rename forks old code. Bluebird function NOT tested yet 207 | // repos.forEach(async repo => { 208 | // try { 209 | // await axios.patch(`https://api.github.com/repos/${repo.full_name}`, { 210 | // name: repo.name + "-bak" 211 | // }); 212 | // } catch (error) { 213 | // console.error("RENAME FORK ERROR", error); 214 | // } 215 | // }); 216 | // resolve("Fork renaming complete"); 217 | --------------------------------------------------------------------------------