Created
April 14, 2016 06:58
-
-
Save timaschew/c5daed16394522b756fc86039b8d19d6 to your computer and use it in GitHub Desktop.
automate a pull request flow via GitHub API - fork, create branch or update from upstream, commit changes, do pull request and optional merge
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
'use strict' | |
const Octokat = require('octokat') | |
const Promise = require('bluebird') | |
const ORIGIN_USERNAME = 'username-or-organisation' | |
const ORIGIN_REPO = 'the-origin-repo' | |
const ORIGIN_BRANCH = 'master' | |
const WAIT_FOR_FORK = 5 | |
const WAIT_FOR_MERGE = 5 | |
// support nodejs and browser runtime | |
var base64Encode = function(content) { | |
if (typeof btoa !== 'undefined') { | |
return btoa(content) | |
} else { | |
return new Buffer(content).toString('base64') | |
} | |
} | |
const init = function(octo) { | |
const helper = {} | |
const originRepo = octo.repos(ORIGIN_USERNAME, ORIGIN_REPO) | |
helper.push = Promise.coroutine(function*(username, branchName, commitMessage, prBody, changeSetArray, autoMerge) { | |
const fork = yield helper.forkRepo(username) | |
yield helper.createNewBranch(fork, branchName) | |
yield helper.commitChanges(fork, branchName, changeSetArray, commitMessage) | |
return yield helper.doPullRequestAndMerge(branchName, username, commitMessage, prBody, autoMerge) | |
}) | |
helper.forkRepo = Promise.coroutine(function*(username) { | |
let fork = null | |
yield originRepo.forks.create() | |
var tryCounter = 0 | |
while (fork == null && tryCounter < WAIT_FOR_FORK) { | |
console.log('waiting until repo is forked') | |
yield Promise.delay(tryCounter * 1000) | |
fork = yield octo.repos(username, ORIGIN_REPO).fetch() | |
tryCounter++ | |
} | |
if (fork == null) { | |
console.error('could not fork the origin repo') | |
return null | |
} | |
return fork | |
}) | |
helper.createNewBranch = Promise.coroutine(function*(fork, branchName) { | |
var forkCommits = yield fork.commits.fetch({sha: 'master'}) | |
var originCommits = yield originRepo.commits.fetch({sha: 'master'}) | |
if (originCommits[0].sha != forkCommits[0].sha) { | |
console.log('master branch of fork is not in sync, force updating from upstream') | |
yield fork.git.refs('heads/master').update({ | |
force: true, | |
sha: originCommits[0].sha | |
}) | |
} | |
var allBranches = yield fork.git.refs.fetch() | |
var branch = allBranches.filter(function(item) { | |
var name = item.ref.split('/')[2] // refs/heads/master -> master | |
return name === branchName | |
})[0] | |
if (branch == null) { | |
console.log('creating branch') | |
var branch = yield fork.git.refs.create({ | |
ref: 'refs/heads/' + branchName, | |
sha: originCommits[0].sha // recent commit SHA | |
}) | |
} | |
return originCommits[0].sha | |
}) | |
helper.commitChanges = Promise.coroutine(function*(fork, branchName, changeSetArray, commitMessage) { | |
const changed = {added: 0, updated: 0, deleted: 0} | |
for (var i=0; i<changeSetArray.length; i++) { | |
var changeset = changeSetArray[i] | |
var config = { | |
message: commitMessage + '(' + (i+1) + '/' + changeSetArray.length + ')', | |
content: changeset.delete ? undefined : base64Encode(changeset.payload), | |
branch: branchName | |
} | |
try { | |
console.log('try to fetching sha from', changeset.path) | |
var meta = yield fork.contents(changeset.path).fetch({ref: branchName}) | |
config.sha = meta.sha | |
} catch (err) { | |
// file seems to be new, so there is no SHA1 | |
} | |
if (changeset.delete) { | |
yield fork.contents(changeset.path).remove(config) | |
changed.deleted++ | |
console.log('deleted file', changeset.path) | |
} else { | |
yield fork.contents(changeset.path).add(config) | |
if (changeset.new) { | |
changed.added++ | |
console.log('added new file', changeset.path) | |
} else { | |
changed.updated++ | |
console.log('updated file', changeset.path) | |
} | |
} | |
} | |
return | |
}) | |
helper.mergePullRequest = Promise.coroutine(function*(pullRequest, counter, max) { | |
if (counter >= max) { | |
throw new Error('could not merge pull request') | |
} | |
try { | |
return yield originRepo.pulls(pullRequest.number).merge.add({ | |
// commitMessage: 'optional message', | |
sha: pullRequest.head.sha | |
}) | |
} catch (err) { | |
// seems that GitHub is not ready for the merge, just wait and try again | |
// https://github.com/githubteacher/welcome-june/issues/127 | |
if (err.json != null && err.json.message === 'Base branch was modified. Review and try the merge again.') { | |
return null | |
} | |
throw err | |
} | |
}) | |
helper.doPullRequestAndMerge = Promise.coroutine(function*(branchName, username, commitMessage, prBody, autoMerge) { | |
console.log('checking pulll requests') | |
var pullRequestArray = yield originRepo.pulls.fetch({head: username + ':' + branchName}) | |
if (pullRequestArray.length === 0) { | |
console.log('creating pulll request') | |
const pullRequest = yield originRepo.pulls.create({ | |
title: commitMessage, | |
body: prBody, | |
head: username + ":" + branchName, | |
base: ORIGIN_BRANCH | |
}) | |
var merged = null | |
var message = null | |
var mergeResult = null | |
if (autoMerge) { | |
// MERGE BUTTON | |
var counter = 0 | |
var mergeResult = null | |
while (mergeResult == null) { | |
console.log('try to merge pull request', pullRequest.number, pullRequest.head.sha) | |
yield Promise.delay(1000 * counter) | |
mergeResult = yield mergePullRequest(pullRequest, counter++, WAIT_FOR_MERGE) | |
} | |
if (mergeResult.merged) { | |
merged = true, | |
message = 'merged' | |
} else { | |
merged = false, | |
message = mergeResult.message | |
} | |
} | |
return { | |
ok: true, | |
created: true, | |
merged: merged, | |
message: message, | |
pr: pullRequest, | |
} | |
} else { | |
return { | |
created: false, | |
pr: pullRequestArray | |
} | |
} | |
}) | |
return helper | |
} | |
module.exports = function(token) { | |
return init(new Octokat({token: token})) | |
} | |
module.exports.init = init |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey @timaschew ๐, funny I've being working on PRB0t
๐