Last active
March 2, 2017 15:29
-
-
Save endquote/ead21f78b83901563ff3 to your computer and use it in GitHub Desktop.
Migrate private repositories from your GitHub organization to your Bitbucket team.
This file contains 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
/* | |
To use: | |
* save this script as migrate.js | |
* fill in the user/pass variables at the top | |
* save the block of json below as package.json right next to it | |
* install node.js from nodejs.org | |
* run 'npm install' from the directory where you saved the files | |
* run 'node migrate.js' | |
* profit | |
{ | |
"dependencies": { | |
"async": "^0.9.0", | |
"fs.extra": "^1.3.2", | |
"github": "^0.2.4", | |
"request": "^2.55.0", | |
"underscore": "^1.8.3" | |
} | |
} | |
*/ | |
var GitHubApi = require('github'); | |
var request = require('request'); | |
var child_process = require('child_process'); | |
var async = require('async'); | |
var fs = require('fs.extra'); | |
var path = require('path'); | |
var _ = require('underscore'); | |
// fill in this stuff | |
var githubUser = ''; | |
var githubPass = ''; | |
var githubOrg = ''; | |
var bitbucketUser = ''; | |
var bitbucketPass = ''; | |
var bitbucketTeam = ''; | |
var checkoutDir = 'git'; | |
var threads = 10; | |
var github = new GitHubApi({ | |
version: '3.0.0' | |
}); | |
async.waterfall([ | |
// Wipe out previous checkouts | |
function(callback) { | |
fs.rmrf(checkoutDir, callback); | |
}, | |
// Make a directory to check out to | |
function(callback) { | |
fs.mkdir(checkoutDir, 0777, callback); | |
}, | |
// Auth to GitHub | |
function(callback) { | |
github.authenticate({ | |
type: 'basic', | |
username: githubUser, | |
password: githubPass | |
}); | |
callback(); | |
}, | |
// Get the list of repositories | |
function(callback) { | |
github.repos.getFromOrg({ | |
org: githubOrg, | |
type: 'all', | |
per_page: 100 | |
}, callback); | |
}, | |
], function(error, data) { | |
// Ignore public repos, they can stay on GitHub. | |
var repos = _.filter(data, function(repo) { | |
return repo.private; | |
}); | |
console.log(repos.length + ' repositories found.'); | |
async.eachLimit(repos, threads, function(item, callback) { | |
var index = repos.indexOf(item) + 1; | |
var prompt = index + '/' + repos.length + ' - ' + item.name + ' - '; | |
async.waterfall([ | |
// Clone the repo from GitHub | |
function(callback) { | |
console.info(prompt + 'cloning from GitHub'); | |
child_process.exec('git clone --mirror ' + item.clone_url, { | |
cwd: path.join(__dirname, checkoutDir) | |
}, function(error, stdout, stderr) { | |
callback(error); | |
}); | |
}, | |
// Set the new remote | |
function(callback) { | |
console.info(prompt + 'setting remote '); | |
var remote = 'https://' + bitbucketUser + '@bitbucket.org/' + bitbucketTeam + '/' + item.name + '.git'; | |
child_process.exec('git remote set-url --push origin ' + remote, { | |
cwd: path.join(__dirname, checkoutDir, item.name + '.git') | |
}, function(error, stdout, stderr) { | |
callback(error); | |
}); | |
}, | |
// Delete the Bitbucket repo if it exists. | |
function(callback) { | |
console.info(prompt + 'deleting Bitbucket repo'); | |
request | |
.del('https://bitbucket.org/api/2.0/repositories/' + bitbucketTeam + '/' + item.name.toLowerCase(), function(error, response, body) { | |
callback(); | |
}) | |
.auth(bitbucketUser, bitbucketPass); | |
}, | |
// Create the new Bitbucket repo. | |
function(callback) { | |
console.info(prompt + 'creating Bitbucket repo'); | |
request | |
.post('https://bitbucket.org/api/2.0/repositories/' + bitbucketTeam + '/' + item.name.toLowerCase(), function(error, response, body) { | |
body = JSON.parse(body); | |
error = body.error; | |
callback(error); | |
}) | |
.form({ | |
name: item.name, | |
description: item.description, | |
is_private: true, | |
scm: 'git', | |
has_issues: false, | |
has_wiki: false | |
}) | |
.auth(bitbucketUser, bitbucketPass); | |
}, | |
// Push the repo to Bitbucket | |
function(callback) { | |
console.info(prompt + 'pushing to Bitbucket'); | |
child_process.exec('git push --mirror', { | |
cwd: path.join(__dirname, checkoutDir, item.name + '.git') | |
}, function(error, stdout, stderr) { | |
callback(error); | |
}); | |
}, | |
// Delete the checkout | |
function(callback) { | |
console.info(prompt + 'deleting clone'); | |
fs.rmrf(path.join(__dirname, checkoutDir, item.name + '.git'), function(error) { | |
callback(error) | |
}); | |
} | |
], function(error, result) { | |
// Go on to the next one | |
if (error) { | |
console.error(prompt + error); | |
} else { | |
callback(); | |
} | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Great job, useful stuff.