Skip to content

Commit

Permalink
scripts: new attempt-backport script for PRs
Browse files Browse the repository at this point in the history
  • Loading branch information
Fishrock123 committed Nov 2, 2016
1 parent 9e348cb commit 663b410
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/node-repo.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,5 @@ function updatePrWithLabels (options, labels) {
})
}

exports.updatePrWithLabels = updatePrWithLabels
exports.resolveLabelsThenUpdatePr = deferredResolveLabelsThenUpdatePr
205 changes: 205 additions & 0 deletions scripts/attempt-backport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
'use strict'

const child_process = require('child_process')
const debug = require('debug')('attempt-backport')
const request = require('request')
const updatePrWithLabels = require('../lib/node-repo').updatePrWithLabels

const enabledRepos = ['node']
const queue = []
let inProgress = false

module.exports = function (app) {
if (!global._node_repo_dir) return

app.on('pull_request.opened', handlePrUpdate)
// Pull Request updates
app.on('pull_request.synchronize', handlePrUpdate)

function handlePrUpdate (event, owner, repo) {
if (!~enabledRepos.indexOf(repo)) return

const prId = event.number
const options = { owner, repo, prId, logger: event.logger }

debug(`/${owner}/${repo}/pull/${prId} sync`)
queueAttemptBackport(options, 7, false)
queueAttemptBackport(options, 6, true)
queueAttemptBackport(options, 4, true)

if (!inProgress) processNextBackport()
}

// to trigger polling manually
app.get('/pr/:owner/:repo/:id', (req, res) => {
const owner = req.params.owner
const repo = req.params.repo
const prId = parseInt(req.params.id, 10)
const options = { owner, repo, prId, logger: req.log }

if (~enabledRepos.indexOf(repo)) {
queueAttemptBackport(options, 7, false)
queueAttemptBackport(options, 6, true)
queueAttemptBackport(options, 4, true)
}

if (!inProgress) processNextBackport()

res.end()
})
}

function processNextBackport() {
const item = queue.shift()
if (!item) return

if (typeof item !== 'function') {
debug(`item was not a function! - queue size: ${queue.length}`)
return
} else if (inProgress) {
debug(`was still in progress! - queue size: ${queue.length}`)
return
}
item()
}

function queueAttemptBackport(options, version, isLTS) {
queue.push(function() {
debug(`processing a new backport to v${version}`)
attemptBackport(options, version, isLTS, processNextBackport)
})
}

function attemptBackport(options, version, isLTS, cb) {
// Start
gitAmAbort()

function wrapCP(cmd, args, opts, callback) {
let exited = false

if (arguments.length === 3) {
callback = opts
opts = {}
}

opts.cwd = global._node_repo_dir

const cp = child_process.spawn(cmd, args, opts)
const argsString = [cmd, ...args].join(' ')

cp.on('error', function(err) {
debug(`child_process err: ${err}`)
if (!exited) onError()
})
cp.on('exit', function(code) {
exited = true
if (!cb) {
debug(`error before exit, code: ${code}, on '${argsString}'`)
return
} else if (code > 0) {
debug(`exit code > 0: ${code}, on '${argsString}'`)
onError()
return
}
callback()
})
// Useful when debugging.
//
// cp.stdout.on('data', (data) => {
// console.log(data.toString())
// })
// cp.stderr.on('data', (data) => {
// console.log(data.toString())
// })

return cp
}

function onError() {
if (!cb) return
const _cb = cb
setImmediate(() => {
debug(`backport to ${version} failed`)
if (!isLTS) updatePrWithLabels(options, [`dont-land-on-v${version}.x`])
setImmediate(() => {
inProgress = false
_cb()
})
})
cb = null
}

function gitAmAbort() {
// TODO(Fishrock123): this should probably just merge into wrapCP
let exited = false
debug(`aborting any previous backport attempt...`)

const cp = child_process.spawn('git', ['am', '--abort'], { cwd: global._node_repo_dir })
const argsString = 'git am --abort'

cp.on('error', function(err) {
debug(`child_process err: ${err}`)
if (!exited) onError()
})
cp.on('exit', function() {
exited = true
if (!cb) {
debug(`error before exit, code: ${code}, on '${argsString}'`)
return
}
gitRemoteUpdate()
})
}

function gitRemoteUpdate() {
debug(`updating git remotes...`)
wrapCP('git', ['remote', 'update', '-p'], gitCheckout)
}

function gitCheckout() {
debug(`checking out upstream/v${version}.x-staging...`)
wrapCP('git', ['checkout', `upstream/v${version}.x-staging`], gitReset)
}

function gitReset() {
debug(`resetting upstream/v${version}.x-staging...`)
wrapCP('git', ['reset', `upstream/v${version}.x-staging`, '--hard'], fetchDiff)
}

function fetchDiff() {
debug(`fetching diff from pr ${options.prId}...`)

const url = `https://patch-diff.githubusercontent.com/raw/${options.owner}/${options.repo}/pull/${options.prId}.patch`

const req = request(url)

req.on('error', function(err) {
debug(`request err: ${err}`)
return onError()
})
req.on('response', function(response) {
if (response.statusCode !== 200) {
debug(`request non-200 status: ${response.statusCode}`)
return onError()
}
})

gitAttemptBackport(req)
}

function gitAttemptBackport(req) {
debug(`attempting a backport to v${version}...`)
const cp = wrapCP('git', ['am'], { stdio: 'pipe' }, function done() {
// Success!
if (isLTS) updatePrWithLabels(options, [`lts-watch-v${version}.x`])

setImmediate(() => {
debug(`backport to v${version} successful`)
inProgress = false
cb()
})
})

req.pipe(cp.stdin)
}
}
15 changes: 15 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
'use strict'

const child_process = require('child_process')

if (process.env.NODE_REPO_DIR) {
const fs = require('fs')
global._node_repo_dir = fs.realpathSync(process.env.NODE_REPO_DIR)
const out = child_process.spawnSync('git', ['status'], { cwd: global._node_repo_dir })

if (out.status !== 0) {
logger.info(out.stdout)
logger.error(out.stderr)
logger.error('Bad NODE_REPO_DIR. Backport patch testing disabled.')
global._node_repo_dir = false
}
}

const app = require('./app')
const logger = require('./lib/logger')

Expand Down

0 comments on commit 663b410

Please sign in to comment.