Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add help flag to CLI and error handling for unknown flags and arguments #377

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/cli/flags.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
let { statSync } = require('fs')
let minimist = require('minimist')

let allowedFlags = [
'_',
'direct',
'dirty',
'debug',
'd',
'dry-run',
'eject',
'help',
'h',
'hydrate',
'no-hydrate',
'production',
'p',
'prune',
'static',
's',
'verbose',
'v',
]

/**
* Read CLI flags and populate userland options
*/
Expand All @@ -13,11 +34,13 @@ module.exports = function getFlags () {
tag: [ 'tags', 't' ],
debug: [ 'd' ],
verbose: [ 'v' ],
help: [ 'h' ],
}
let boolean = [ 'direct', 'debug', 'dry-run', 'eject', 'no-hydrate', 'production', 'static', 'verbose' ]
let boolean = [ 'direct', 'debug', 'dry-run', 'eject', 'help', 'no-hydrate', 'production', 'static', 'verbose' ]
let def = { hydrate: true }
let args = minimist(process.argv.slice(2), { alias, boolean, default: def })
if (args._[0] === 'deploy') args._.splice(0, 1)
let unknownFlags = Object.keys(args).filter(key => !allowedFlags.includes(key))

// Log levels
let logLevel = 'normal'
Expand All @@ -38,6 +61,9 @@ module.exports = function getFlags () {
isDryRun: args['dry-run'] || args.eject,
isStatic: args.static,
shouldHydrate: args.hydrate,
help: args.help,
unknownFlags: unknownFlags,
unknownArgs: args._,
}
}

Expand Down
110 changes: 110 additions & 0 deletions src/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ let update = updater('Deploy')
*/
async function main (/* opts = {} */) {
let flags = _flags()

if (flags.help) {
helpMessage()
return
}

if (flags.unknownArgs.length > 0) {
unknownArgMessage(flags)
return
}

if (flags.unknownFlags.length > 0) {
unknownFlagMessage(flags)
return
}

let { deployStage } = flags
// Ignore Inventory if passed, and re-Inventory with deployStage set
let inventory = await _inventory({ deployStage, env: true })
Expand Down Expand Up @@ -67,6 +83,23 @@ module.exports = main
if (require.main === module) {
(async function () {
try {
let flags = _flags()

if (flags.help) {
helpMessage()
return
}

if (flags.unknownArgs.length > 0) {
unknownArgMessage(flags)
return
}

if (flags.unknownFlags.length > 0) {
unknownFlagMessage(flags)
return
}

let inventory = await _inventory({})
banner({ inventory, version: `Deploy ${version}` })
await main({ inventory })
Expand All @@ -79,3 +112,80 @@ if (require.main === module) {
}
})()
}

function helpMessage () {
let output = `Deploy an Architect project to AWS.

For more information, see the documentation:
<https://arc.codes/docs/en/reference/cli/deploy>

\x1b[1mUSAGE\x1b[0m
arc deploy [flags]

\x1b[1mFLAGS\x1b[0m
-d, --direct path/to/function Directly deploy a specific function, code, or config
--dry-run Create a CloudFormation template but do not deploy it (useful for testing)
-n, --name string Deploy a custom named stack
--no-hydrate Do not automatically run npm, bundle or pip before deploying
-p, --production Deploy a CloudFormation stack to a production stack
--prune Remove assets that exist in the static S3 bucket but do not exist in the local /public folder
-s, --static Deploy only the files in the static folder
-t, --tag key=value Add a resource tag to the CloudFormation stack
-v, --verbose Display the full deploy status messages

\x1b[1mEXAMPLES\x1b[0m
Deploy a staging stack
$ arc deploy

Deploy a production stack
$ arc deploy --production

Deploy a custom named stack
$ arc deploy --name mycustomstackname

Deploy a stack with resource tags
$ arc deploy --tag tagA=foo --tag tagB=bar --tag tagC=baz

Deploy static assets to S3
$ arc deploy --static

Deploy a specific function, code, or config
$ arc deploy --direct src/http/get-index

Run deploy without deploying
$ arc deploy --dry-run
`

console.log(output)
}

function unknownFlagMessage (flags) {
let unknownFlag = flags.unknownFlags[0]

let prefix
if (unknownFlag.length > 1) {
prefix = '--'
}
else {
prefix = '-'
}

let output = `unknown flag: ${prefix}${unknownFlag}

try 'arc deploy --help' for more information
`

console.log(output)
}

function unknownArgMessage (flags) {
let unknownArg = flags.unknownArgs[0]

let output = `unknown argument: ${unknownArg}

try 'arc deploy --help' for more information
`

console.log(output)
}

30 changes: 30 additions & 0 deletions test/unit/flags-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,36 @@ test('Tags', t => {
t.deepEqual(flags().tags, [ tagA, tagB ], '"-t" flags returns multiple tags')
})

test('Help', t => {
t.plan(3)

args(`-h`)
t.equal(flags().help, true, '"-h" flag sets help to true')

args(`--help`)
t.equal(flags().help, true, '"--help" flag sets help to true')

args(``)
t.equal(flags().help, false, 'Lack of "-h" or "--help flag sets help to false')
})

test('Unkown flags', t => {
t.plan(2)

args(`-z -v --prune --foo --no-hydrate --bar`)
t.deepEqual(flags().unknownFlags, [ 'z', 'foo', 'bar' ], 'Unknown flags are returned in the unknownFlags array')

args(`--direct --dirty --debug -d --dry-run --eject --help -h --hydrate --no-hydrate --production -p --prune --static -s --verbose -v`)
t.deepEqual(flags().unknownFlags, [], 'Known flags are not returned in the unknownFlags array')
})

test('Unkown arguments', t => {
t.plan(1)

args(`foo bar`)
t.deepEqual(flags().unknownArgs, [ 'foo', 'bar' ], 'Unknown arguments are returned in the unknownArgs array')
})

test('Teardown', t => {
t.plan(1)
process.argv = argv
Expand Down