Browse Source

Merged GitHub support

master
Leo Lamprecht 8 years ago
parent
commit
124d308863
No known key found for this signature in database GPG Key ID: B08517883D5E0E10
  1. 2
      README.md
  2. 89
      bin/now-deploy.js
  3. 118
      lib/github.js
  4. 5
      package.json

2
README.md

@ -8,7 +8,7 @@ Realtime global deployments served over HTTP/2. You can find the FAQ [here](http
## Usage
Firstly, make sure to install the package:
Firstly, make sure to install the package globally:
```bash
$ npm install -g now

89
bin/now-deploy.js

@ -5,7 +5,7 @@ import {resolve} from 'path'
// Packages
import Progress from 'progress'
import {stat} from 'fs-promise'
import fs from 'fs-promise'
import bytes from 'bytes'
import chalk from 'chalk'
import minimist from 'minimist'
@ -21,6 +21,7 @@ import Now from '../lib'
import toHumanPath from '../lib/utils/to-human-path'
import promptOptions from '../lib/utils/prompt-options'
import {handleError, error} from '../lib/error'
import {onGitHub, isRepoPath, gitPathParts} from '../lib/github'
import readMetaData from '../lib/read-metadata'
const argv = minimist(process.argv.slice(2), {
@ -101,19 +102,15 @@ const help = () => {
${chalk.cyan('$ now /usr/src/project')}
${chalk.gray('–')} Lists all deployments with their IDs
${chalk.gray('–')} Deploys a GitHub repository
${chalk.cyan('$ now ls')}
${chalk.cyan('$ now user/repo#ref')}
${chalk.gray('–')} Associates deployment ${chalk.dim('`deploymentId`')} with ${chalk.dim('`custom-domain.com`')}
${chalk.gray('–')} Deploys a GitHub or GitLab repo using its URL
${chalk.cyan('$ now alias deploymentId custom-domain.com')}
${chalk.cyan('$ now https://gitlab.com/user/repo')}
${chalk.gray('–')} Stores a secret
${chalk.cyan('$ now secret add mysql-password 123456')}
${chalk.gray('–')} Deploys with ENV vars (using the ${chalk.dim('`mysql-password`')} secret stored above)
${chalk.gray('–')} Deploys with ENV vars
${chalk.cyan('$ now -e NODE_ENV=production -e MYSQL_PASSWORD=@mysql-password')}
@ -133,6 +130,9 @@ if (path) {
path = process.cwd()
}
// If the current deployment is a repo
const gitHubRepo = {}
const exit = code => {
// we give stdout some time to flush out
// because there's a node bug where
@ -192,16 +192,59 @@ if (argv.h || argv.help) {
async function sync(token) {
const start = Date.now()
const rawPath = argv._[0]
if (!quiet) {
console.log(`> Deploying ${chalk.bold(toHumanPath(path))}`)
const stopDeployment = msg => {
error(msg)
process.exit(1)
}
const isValidRepo = isRepoPath(rawPath)
try {
await stat(path)
await fs.stat(path)
} catch (err) {
error(`Could not read directory ${chalk.bold(path)}`)
process.exit(1)
let repo
if (isValidRepo && isValidRepo !== 'no-valid-url') {
const searchMessage = setTimeout(() => {
console.log('> Didn\'t find directory. Searching on GitHub...')
}, 500)
try {
repo = await onGitHub(rawPath, debug)
} catch (err) {}
clearTimeout(searchMessage)
const gitParts = gitPathParts(rawPath)
Object.assign(gitHubRepo, gitParts)
}
if (repo) {
// Tell now which directory to deploy
path = repo.path
// Set global variable for deleting tmp dir later
// once the deployment has finished
Object.assign(gitHubRepo, repo)
} else if (isValidRepo === 'no-valid-url') {
stopDeployment(`This URL is not a valid repository from GitHub or GitLab.`)
} else if (isValidRepo) {
const gitRef = gitHubRepo.ref ? `with "${chalk.bold(gitHubRepo.ref)}" ` : ''
stopDeployment(`There's no repository named "${chalk.bold(gitHubRepo.main)}" ${gitRef}on GitHub or GitLab`)
} else {
stopDeployment(`Could not read directory ${chalk.bold(path)}`)
}
}
if (!quiet) {
if (gitHubRepo) {
const gitRef = gitHubRepo.ref ? ` at "${chalk.bold(gitHubRepo.ref)}" ` : ''
console.log(`> Deploying GitHub repository "${chalk.bold(gitHubRepo.main)}"` + gitRef)
} else {
console.log(`> Deploying ${chalk.bold(toHumanPath(path))}`)
}
}
let deploymentType
@ -227,7 +270,7 @@ async function sync(token) {
isStatic = true
} else {
try {
await stat(resolve(path, 'package.json'))
await fs.stat(resolve(path, 'package.json'))
} catch (err) {
hasPackage = true
}
@ -235,7 +278,7 @@ async function sync(token) {
[hasPackage, hasDockerfile] = await Promise.all([
await (async () => {
try {
await stat(resolve(path, 'package.json'))
await fs.stat(resolve(path, 'package.json'))
} catch (err) {
return false
}
@ -243,7 +286,7 @@ async function sync(token) {
})(),
await (async () => {
try {
await stat(resolve(path, 'Dockerfile'))
await fs.stat(resolve(path, 'Dockerfile'))
} catch (err) {
return false
}
@ -485,6 +528,16 @@ function printLogs(host) {
if (!quiet) {
console.log(`${chalk.cyan('> Deployment complete!')}`)
}
if (gitHubRepo && gitHubRepo.cleanup) {
// Delete temporary directory that contains repository
gitHubRepo.cleanup()
if (debug) {
console.log(`> [debug] Removed temporary repo directory`)
}
}
process.exit(0)
})
}

118
lib/github.js

@ -0,0 +1,118 @@
// Native
import path from 'path'
import url from 'url'
// Packages
import fs from 'fs-promise'
import download from 'download'
import tmp from 'tmp-promise'
import isURL from 'is-url'
const downloadRepo = async repoPath => {
const pathParts = gitPathParts(repoPath)
const url = `https://api.github.com/repos/${pathParts.main}/tarball/${pathParts.ref}`
const tmpDir = await tmp.dir({
// We'll remove it manually once deployment is done
keep: true,
// Recursively remove directory when calling respective method
unsafeCleanup: true
})
try {
await download(url, tmpDir.path, {
extract: true
})
} catch (err) {
tmpDir.cleanup()
return false
}
const tmpContents = await fs.readdir(tmpDir.path)
tmpDir.path = path.join(tmpDir.path, tmpContents[0])
return tmpDir
}
const splittedURL = fullURL => {
const pathParts = url.parse(fullURL).path.split('/')
pathParts.shift()
// Set path to repo...
const main = pathParts[0] + '/' + pathParts[1]
// ...and then remove it from the parts
pathParts.splice(0, 2)
// Assign Git reference
let ref = pathParts.length >= 2 ? pathParts[1] : ''
// Shorten SHA for commits
if (pathParts[0] && pathParts[0] === 'commit') {
ref = ref.substring(0, 7)
}
// We're deploying master by default,
// so there's no need to indicate it explicitly
if (ref === 'master') {
ref = ''
}
return {main, ref}
}
export const gitPathParts = main => {
let ref = ''
if (isURL(main)) {
return splittedURL(main)
}
if (main.split('/')[1].includes('#')) {
const parts = main.split('#')
ref = parts[1]
main = parts[0]
}
return {main, ref}
}
export const isRepoPath = path => {
if (!path) {
return false
}
const allowedHosts = [
'github.com',
'gitlab.com'
]
if (isURL(path)) {
const urlParts = url.parse(path)
const slashSplitted = urlParts.path.split('/').filter(n => n)
const notBare = slashSplitted.length >= 2
if (allowedHosts.includes(urlParts.host) && notBare) {
return true
}
return 'no-valid-url'
}
return /[^\s\\]\/[^\s\\]/g.test(path)
}
export const onGitHub = async (path, debug) => {
let tmpDir = false
try {
tmpDir = await downloadRepo(path)
} catch (err) {
if (debug) {
console.log(`Could not download "${path}" repo from GitHub`)
}
}
return tmpDir
}

5
package.json

@ -74,6 +74,7 @@
"copy-paste": "1.3.0",
"cross-spawn": "5.0.1",
"docker-file-parser": "0.1.0",
"download": "5.0.2",
"email-prompt": "0.1.8",
"email-validator": "1.0.7",
"fs-promise": "1.0.0",
@ -81,6 +82,7 @@
"graceful-fs": "4.1.11",
"ignore": "3.2.0",
"ini": "1.3.4",
"is-url": "1.2.2",
"minimist": "1.2.0",
"ms": "0.7.2",
"node-fetch": "1.6.3",
@ -90,7 +92,8 @@
"socket.io-client": "1.7.1",
"spdy": "3.4.4",
"split-array": "1.0.1",
"text-table": "0.2.0"
"text-table": "0.2.0",
"tmp-promise": "1.0.2"
},
"devDependencies": {
"alpha-sort": "1.0.2",

Loading…
Cancel
Save