now-flow
Version:
Add deployment workflows to Zeit now
101 lines (94 loc) • 3.12 kB
JavaScript
//@flow
const { tmpdir } = require('os')
const { parse, format } = require('url')
const fetch = require('node-fetch')
const tar = require('tar-fs')
const pipeStreams = require('pipe-streams-to-promise')
const { mkdir } = require('fs-extra')
const uid = require('uid-promise')
const { createGunzip } = require('zlib')
const { join } = require('path')
const debug = require('debug')('now:resolvers:github')
// matches a parameter that can be `now`d like zeit/now-cli#master
const DEPLOY_PARAM_REGEX = /^([\w-]+)\/([\w-]+)(#\w+)?$/
// matches whether the parameter could be a github url
const GITHUB_TEST_REGEX = /^(https?:\/\/)(www\.)?github\.com/
// matches a github url pathname like: zeit/now-cli/tree/master
const URL_PATHNAME_REGEX = /^\/([\w-]+)\/([\w-]+)(\/tree\/(\w+))?$/
const resolveGitHub = (param) => {
// support simple `user/repo` syntax
const match = param.match(DEPLOY_PARAM_REGEX)
if (match) {
const [, user, repo, tree = 'master'] = match
return resolveGitHubByURL(`https://github.com/${user}/${repo}/tree/${tree}`)
} else if (GITHUB_TEST_REGEX.test(param)) {
return resolveGitHubByURL(param)
} else {
return null
}
}
const resolveGitHubByURL = async (url) => {
debug('resolving %s by github url', url)
if (/^https?/.test(url)) {
const parsed = parse(url)
if (parsed.hostname === 'github.com') {
const httpsUrl =
'https:' === parsed.protocol ? url : format(Object.assign({}, parsed))
const res = await fetch(httpsUrl)
if (res.ok) {
debug('attempting github clone')
const { pathname = '' } = parsed
const match = pathname.match(URL_PATHNAME_REGEX)
if (match) {
const [, user, repo, , tree] = match
const downloadURL = format({
protocol: 'https:',
hostname: 'codeload.github.com',
pathname: `/${user}/${repo}/tar.gz/${tree}`
})
debug('fetching download url', downloadURL)
const downloadRes = await fetch(downloadURL, { compress: false })
if (downloadRes.ok) {
const tmpDir = join(tmpdir(), `now-gh-${await uid(20)}`)
debug('creating tmp dir to extract', tmpDir)
try {
await mkdir(tmpDir)
} catch (err) {
throw new Error(
'Error occurred while trying to extract ' +
`GH tarball to tmp directory ${tmpDir}: ${err.stack}`
)
}
debug('unzipping and untarring stream')
await pipeStreams([
downloadRes.body,
createGunzip(),
tar.extract(tmpDir)
])
// instead of stripping a directory upon untar,
// we return the full path to the extracted project,
// so that now can take advantage of the name
return join(tmpDir, `${repo}-${tree}`)
} else {
throw new Error(
'An HTTP error ${res.status} was returned ' +
`by "${downloadURL}"`
)
}
} else {
debug('invalid github project url')
return null
}
} else {
debug('non-200 from github (%d)', res.status)
return null
}
} else {
debug('skipping non-github hostname')
return null
}
} else {
return null
}
}
module.exports = resolveGitHub