bugsnag-build-reporter
Version:
A tool for reporting your application’s builds to Bugsnag
110 lines (101 loc) • 3.3 kB
JavaScript
const findNearest = require('find-nearest-file')
const readFile = require('fs').readFile
const exec = require('child_process').exec
const parallel = require('run-parallel')
const relative = require('path').relative
module.exports = (path, log, cb) => {
const pkg = findNearest('package.json', path)
if (pkg) log.info('found a manifest:', relative(path, pkg))
parallel([
cb => extractPackageJsonRepoInfo(pkg, log, cb),
cb => detectSourceInfoFromRepo(path, log, cb)
], (err, data) => {
if (err) {
log.error(err)
return cb(null, null)
}
if (!data) return cb(null, null)
const repository = (data[0] && data[0].repository)
? data[0].repository
: ((data[1] && data[1].repository)
? data[1].repository
: undefined)
const provider = getProviderFromUrl(repository)
const revision = data[1] ? data[1].revision : undefined
if (!provider || !repository || !revision) {
log.warn('no source control info found (looked in package.json, .git, .hg)')
return cb(null, null)
}
cb(null, { provider, repository, revision })
})
}
const getProviderFromUrl = repo => {
if (!repo) return null
if (/^git@github\.com|https:\/\/github.com/.test(repo)) return 'github'
if (/^git@gitlab\.com|https:\/\/gitlab.com/.test(repo)) return 'gitlab'
if (/^git@bitbucket\.org|https:\/\/bitbucket.org|ssh:\/\/hg@bitbucket\.org|https:\/\/\w+@bitbucket\.org\//.test(repo)) return 'bitbucket'
return null
}
const extractPackageJsonRepoInfo = (path, log, cb) => {
if (!path) return cb(null, null)
readFile(path, 'utf8', (err, data) => {
if (err) {
log.error(err)
return cb(null)
}
try {
const pkg = JSON.parse(data)
if (!pkg.repository) return cb(null, null)
log.debug('repository info in manifest:', { repository: pkg.repository.url })
cb(null, { repository: pkg.repository.url })
} catch (e) {
log.error(err)
cb(null)
}
})
}
const detectSourceInfoFromRepo = (path, log, cb) => {
const onSuccess = (type, data) => {
log.debug(`${type} repo info:`, data)
cb(null, data)
}
const onError = () => {
cb(null, null)
}
detectSourceInfoFromGit(path, (err, data) => {
if (!err && data) return onSuccess('git', data)
detectSourceInfoFromMercurial(path, (err, data) => {
if (!err && data) return onSuccess('mercurial', data)
onError()
})
})
}
const detectSourceInfoFromGit = (cwd, cb) => {
parallel([
cb => exec('git rev-parse HEAD', { cwd }, cb),
cb => {
// eslint-disable-next-line
exec('git remote get-url origin', { cwd }, (err, data) => {
// ignore this error in case origin doesn't exist
cb(null, data)
})
},
cb => exec('git remote -v', { cwd }, cb)
], (err, data) => {
cb(err, {
revision: data[0] ? data[0].trim() : null,
repository: data[1] ? data[1].trim() : (data[2] ? data[2].split(/\t|\s|\n/)[1] : null)
})
})
}
const detectSourceInfoFromMercurial = (cwd, cb) => {
parallel([
cb => exec('hg id -i', { cwd }, cb),
cb => exec('hg paths default', { cwd }, cb)
], (err, data) => {
cb(err, {
revision: data[0] ? data[0].trim() : null,
repository: data[1] ? data[1].trim() : null
})
})
}