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