Leo Lamprecht
8 years ago
2 changed files with 234 additions and 1 deletions
@ -0,0 +1,225 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
// Packages
|
|||
const minimist = require('minimist') |
|||
const chalk = require('chalk') |
|||
const ms = require('ms') |
|||
const table = require('text-table') |
|||
|
|||
// Ours
|
|||
const Now = require('../lib') |
|||
const login = require('../lib/login') |
|||
const cfg = require('../lib/cfg') |
|||
const { handleError, error } = require('../lib/error') |
|||
const logo = require('../lib/utils/output/logo') |
|||
const { normalizeURL } = require('../lib/utils/url') |
|||
|
|||
const help = () => { |
|||
console.log(` |
|||
${chalk.bold( |
|||
`${logo} now remove` |
|||
)} deploymentId|deploymentName [...deploymentId|deploymentName] |
|||
|
|||
${chalk.dim('Options:')} |
|||
|
|||
-h, --help Output usage information |
|||
-c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline( |
|||
'FILE' |
|||
)} Config file |
|||
-d, --debug Debug mode [off] |
|||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline( |
|||
'TOKEN' |
|||
)} Login token |
|||
-y, --yes Skip confirmation |
|||
--safe Skip deployments with an active alias |
|||
|
|||
${chalk.dim('Examples:')} |
|||
|
|||
${chalk.gray('–')} Remove a deployment identified by ${chalk.dim( |
|||
'`deploymentId`' |
|||
)}: |
|||
|
|||
${chalk.cyan('$ now rm deploymentId')} |
|||
|
|||
${chalk.gray('–')} Remove all deployments with name ${chalk.dim('`my-app`')}: |
|||
|
|||
${chalk.cyan('$ now rm my-app')} |
|||
|
|||
${chalk.gray('–')} Remove two deployments with IDs ${chalk.dim( |
|||
'`eyWt6zuSdeus`' |
|||
)} and ${chalk.dim('`uWHoA9RQ1d1o`')}: |
|||
|
|||
${chalk.cyan('$ now rm eyWt6zuSdeus uWHoA9RQ1d1o')} |
|||
|
|||
${chalk.dim('Alias:')} rm |
|||
`)
|
|||
} |
|||
|
|||
// Options
|
|||
let argv |
|||
let debug |
|||
let apiUrl |
|||
let hard |
|||
let skipConfirmation |
|||
let ids |
|||
|
|||
const main = async ctx => { |
|||
argv = minimist(ctx.argv.slice(2), { |
|||
string: ['config', 'token'], |
|||
boolean: ['help', 'debug', 'hard', 'yes', 'safe'], |
|||
alias: { |
|||
help: 'h', |
|||
config: 'c', |
|||
debug: 'd', |
|||
token: 't', |
|||
yes: 'y' |
|||
} |
|||
}) |
|||
|
|||
argv._ = argv._.slice(1) |
|||
|
|||
debug = argv.debug |
|||
apiUrl = argv.url || 'https://api.zeit.co' |
|||
hard = argv.hard || false |
|||
skipConfirmation = argv.yes || false |
|||
ids = argv._ |
|||
|
|||
if (argv.help || ids.length === 0) { |
|||
help() |
|||
process.exit(0) |
|||
} |
|||
|
|||
const config = await cfg.read({ token: argv.token }) |
|||
|
|||
let token |
|||
try { |
|||
token = config.token || (await login(apiUrl)) |
|||
} catch (err) { |
|||
error(`Authentication error – ${err.message}`) |
|||
process.exit(1) |
|||
} |
|||
|
|||
try { |
|||
await remove({ token, config }) |
|||
} catch (err) { |
|||
error(`Unknown error: ${err}\n${err.stack}`) |
|||
process.exit(1) |
|||
} |
|||
} |
|||
|
|||
module.exports = async ctx => { |
|||
try { |
|||
await main(ctx) |
|||
} catch (err) { |
|||
handleError(err) |
|||
process.exit(1) |
|||
} |
|||
} |
|||
|
|||
function readConfirmation(matches) { |
|||
return new Promise(resolve => { |
|||
process.stdout.write( |
|||
`> The following deployment${matches.length === 1 |
|||
? '' |
|||
: 's'} will be removed permanently:\n` |
|||
) |
|||
|
|||
const tbl = table( |
|||
matches.map(depl => { |
|||
const time = chalk.gray(ms(new Date() - depl.created) + ' ago') |
|||
const url = depl.url ? chalk.underline(`https://${depl.url}`) : '' |
|||
return [depl.uid, url, time] |
|||
}), |
|||
{ align: ['l', 'r', 'l'], hsep: ' '.repeat(6) } |
|||
) |
|||
process.stdout.write(tbl + '\n') |
|||
|
|||
for (const depl of matches) { |
|||
for (const alias of depl.aliases) { |
|||
process.stdout.write( |
|||
`> ${chalk.yellow('Warning!')} Deployment ${chalk.bold(depl.uid)} ` + |
|||
`is an alias for ${chalk.underline( |
|||
`https://${alias.alias}` |
|||
)} and will be removed.\n` |
|||
) |
|||
} |
|||
} |
|||
|
|||
process.stdout.write( |
|||
`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[y/N] ')}` |
|||
) |
|||
|
|||
process.stdin |
|||
.on('data', d => { |
|||
process.stdin.pause() |
|||
resolve(d.toString().trim()) |
|||
}) |
|||
.resume() |
|||
}) |
|||
} |
|||
|
|||
async function remove({ token, config: { currentTeam } }) { |
|||
const now = new Now({ apiUrl, token, debug, currentTeam }) |
|||
|
|||
const deployments = await now.list() |
|||
|
|||
let matches = deployments.filter(d => { |
|||
return ids.some(id => { |
|||
return d.uid === id || d.name === id || d.url === normalizeURL(id) |
|||
}) |
|||
}) |
|||
|
|||
const aliases = await Promise.all( |
|||
matches.map(depl => now.listAliases(depl.uid)) |
|||
) |
|||
|
|||
matches = matches.filter((match, i) => { |
|||
if (argv.safe && aliases[i].length > 0) { |
|||
return false |
|||
} |
|||
|
|||
match.aliases = aliases[i] |
|||
return true |
|||
}) |
|||
|
|||
if (matches.length === 0) { |
|||
error( |
|||
`Could not find ${argv.safe |
|||
? 'unaliased' |
|||
: 'any'} deployments matching ${ids |
|||
.map(id => chalk.bold(`"${id}"`)) |
|||
.join(', ')}. Run ${chalk.dim(`\`now ls\``)} to list.` |
|||
) |
|||
return process.exit(1) |
|||
} |
|||
|
|||
try { |
|||
if (!skipConfirmation) { |
|||
const confirmation = (await readConfirmation(matches)).toLowerCase() |
|||
|
|||
if (confirmation !== 'y' && confirmation !== 'yes') { |
|||
console.log('\n> Aborted') |
|||
process.exit(0) |
|||
} |
|||
} |
|||
|
|||
const start = new Date() |
|||
|
|||
await Promise.all(matches.map(depl => now.remove(depl.uid, { hard }))) |
|||
|
|||
const elapsed = ms(new Date() - start) |
|||
console.log(`${chalk.cyan('> Success!')} [${elapsed}]`) |
|||
console.log( |
|||
table( |
|||
matches.map(depl => { |
|||
return [`Deployment ${chalk.bold(depl.uid)} removed`] |
|||
}) |
|||
) |
|||
) |
|||
} catch (err) { |
|||
handleError(err) |
|||
process.exit(1) |
|||
} |
|||
|
|||
now.close() |
|||
} |
Loading…
Reference in new issue