diff --git a/src/cli.ts b/src/cli.ts index 98aea3c..c334733 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,267 +1,44 @@ import { Command } from 'commander' -import { existsSync, mkdirSync, readFileSync } from 'fs' -import { capture, run } from '~/shell' -import { exit, parseAndRemoveWildcardOptions, pp, requireDockerVersion } from '~/utils' +import { existsSync, mkdirSync } from 'fs' +import { parseAndRemoveWildcardOptions } from '~/utils' import { StaxConfig } from '~/types' import { version } from 'process' -import icons from '~/icons' -import Stax from '~/stax' +import { registerCommands } from '~/commands' import * as path from 'path' import tmp from 'tmp' -import settings from './settings' -import setupWizard from './setup_wizard' -const DEFAULT_CONTEXT_NAME = 'stax' +function buildArgs() { + let [ args, overrides ] = parseAndRemoveWildcardOptions(process.argv, '--stax.') + const commandSeparator = args.indexOf('--') -const editor = process.env.STAX_EDITOR || 'code' -const stax = new Stax(DEFAULT_CONTEXT_NAME) -const program = new Command() - -program.name('stax') -program.version(version.replace('v', '')) - -program.command('alias') - .argument('[name]', 'Name of application') - .argument('[alias]', 'Name of alias for application') - .description('Create an alias for an application that can be used in place of the application\'s name when running commands') - .action((name, alias) => { - if (name && alias) - return stax.find(name).addAlias(alias) - - const aliases = settings.read('aliases', null) - - if (aliases && Object.keys(aliases).length > 0) { - console.log('Current aliases:') - pp(aliases) - console.log('\nUsage: stax alias [options] [name] [alias]') - } else - program.commands.find(cmd => cmd.name() === 'alias').help() - }) - -program.command('cat') - .argument('', 'Name of application') - .argument('', 'Path to a file in the container') - .option('-s, --service ', 'Name of service to act on') - .description('Show contents of a file from the container') - .action(async (name, file, options) => { - const container = stax.findContainer(name, options) - await container.exec(`sh -c 'cat ${file} 2>/dev/null'`, { quiet: true }) - }) - -program.command('config') - .argument('', 'Name of application') - .option('-s, --service ', 'Name of service to act on') - .option('-g, --get ', 'Get the value of a config variable') - .description('Show config variables for the container.') - .action((name, options) => { - const container = stax.findContainer(name, options) - - if (options.get) - console.log(container.config.fetch(options.get) || '') - else - pp({ ...container.config, labels: container.labels }) - }) - -program.command('copy') - .alias('cp') - .argument('', 'Name of application') - .argument('', 'Path to a local file or directory') - .argument('', 'Path to a destination file or directory in the container') - .option('-s, --service ', 'Name of service to act on') - .option('-n, --dont-overwrite', 'Don\'t overwrite if file already exists') - .description('Copy a file to the container') - .action(async (name, source, destination, options) => stax.findContainer(name, options).copy(source, destination, options)) - -program.command('down') - .argument('', 'Name of application') - .option('-s, --service ', 'Name of service to act on') - .description('Stop an application') - .action(async (name, options) => { - const target = options.service ? stax.findContainer(name, options) : stax.find(name) - target.down() - }) - -program.command('duplicate') - .argument('', 'Name of application') - .argument('', 'Name of new application') - .option('-i, --inspect', 'Show the compose file') - .description('Duplicate an application') - .action(async (name, newName, options) => { await stax.find(name).duplicate(newName, overrides as unknown as StaxConfig, options) }) - -program.command('edit') - .argument('', 'Name of application') - .description(`Open application in a vscode based editor`) - .action(async name => { - const app = stax.find(name) - let starting = false - - if (!app.primary.running) { - starting = true - console.log(`🚀 ${name} container(s) are not running. Starting them now."`) - await app.up() - } - - if (!app.primary.config.workspace) - return exit(0, { message: `${name} has no 'workspace' defined.` }) - - // kill vscode servers to fix problem where vscode can't access any files sometimes - // when starting and trying to attach to the container - if (!starting && (editor === 'code' || editor === 'cursor')) { - const processToKill = `[${editor[0]}]${editor.slice(1)}-server` - await app.primary.exec(`sh -c 'pkill --echo --full "${processToKill}" || true'`) - } - - const hex = Buffer.from(JSON.stringify({ containerName: app.primary.containerName })).toString('hex') - run(`${editor} --folder-uri=vscode-remote://attached-container+${hex}%${app.primary.config.workspace}`) - }) - -program.command('exec') - .alias('run') - .argument('', 'Name of application') - .argument('', 'Command to execute. Use "--" before your command if it has more than one word.') - .option('-s, --service ', 'Name of service to act on') - .option('-q, --quiet', 'Don\'t print logging and info messages') - .option('-h, --hook', 'Run a hook where the command is the name of the hook to run') - .description('Execute a command (or hook) in a running application') - .action(async (name, command, options) => { - const container = await stax.findContainer(name, options) - - if (options.hook) - container.runHook(command) - else - container.exec(command, { quiet: options.quiet }) - }) - -program.command('get') - .argument('', 'Name of application') - .argument('', 'File to copy from the container') - .argument('', 'Local destination to copy the file to') - .option('-s, --service ', 'Name of service to act on') - .description('Copy a from the container') - .action(async (name, source, destination, options) => stax.findContainer(name, options).get(source, destination)) - -program.command('inspect') - .argument('', 'Name of application') - .option('-c, --compose', 'Show the compose file') - .option('-d, --dockerfile', 'Show the Dockerfile build (if any)') - .option('-l, --labels', 'Show container labels') - .description('Inspect the container or build files.') - .action((name, options) => { - const app = stax.find(name) - - if (options.compose) - pp(readFileSync(stax.find(name).primary.composeFile, 'utf-8')) - else if (options.dockerfile) - console.log('TODO') - else if (options.labels) - console.log(app.primary.labels) - else - pp(JSON.parse(capture(`docker inspect ${app.primary.containerName}`))) - }) - -program.command('ls') - .alias('ps').alias('list') - .description('List applications') - .option('-f, --field ', 'Include specified config field', (value, previous) => previous.concat([value]), []) - .action((options) => stax.list({ fields: options.field || [] })) - -program.command('logs') - .argument('', 'Name of application') - .option('-s, --service ', 'Name of service to act on') - .option('-f, --follow', 'Follow log output') - .option('-t, --tail ', 'Number of lines to show from the end of the logs') - .option('--since ', 'A time or duration string accepted by \'docker container logs\'') - .description('Tail logs for an application') - .action(async (name, options) => { - const follow = options.follow || false - const tail = options.tail ? parseInt(options.tail) : undefined - const since = options.since - await stax.findContainer(name, options).logs({ follow, tail, since }) - }) - -program.command('rebuild') - .argument('', 'Name of application') - .option('-i, --inspect', 'Show the compose file') - .option('--no-cache', 'Don\'t use cache when building images') - .option('--progress ', 'Set type of progress output') - .description('Rebuild an application') - .action(async (name, options) => { await stax.find(name).rebuild(overrides as unknown as StaxConfig, options) }) - -program.command('remove') - .alias('rm') - .argument('', 'Name of application') - .description('Remove application') - .action(async name => { await stax.find(name).remove() }) - -program.command('restart') - .argument('') - .description('Restart an application') - .action(async name => { await stax.find(name).restart() }) - -program.command('settings') - .argument('', 'Name of setting') - .argument('[value]', 'Value of setting') - .option('-s, --set', 'Set the value of the setting') - .description('Get or set stax settings') - .action((name, value, options) => { - if (options.set) { - value = settings.write(name, value) - console.log(`${icons.saved} Setting for '${name}' set to '${value}'`) - } else - console.log(settings.read(name)) - }) - -program.command('setup') - .argument('[location]', 'Path to a local directory or git repo of application') - .option('-s, --staxfile ', 'Staxfile to use for setup') - .option('-i, --inspect', 'Show the compose file') - .option('--no-cache', 'Don\'t use cache when building images') - .option('--progress ', 'Set type of progress output') - .description('Setup an application') - .action(async (location, options) => { - requireDockerVersion(27.0, 2.29) - - if (location) { - const app = await stax.setup({ source: location, ...options }, { ...options, overrides: overrides }) - console.log('\n' + app.installedMessage()) - } else if (settings.read('services_home')) - await setupWizard(stax) - else - console.log('Please specify an application location or set the services home directory.') - }) - -program.command('shell') - .alias('sh') - .argument('', 'Name of application') - .option('-s, --service ', 'Name of service to act on') - .description('Shell into application\' primary container') - .action(async (name, options) => stax.findContainer(name, options).shell()) - -program.command('up') - .argument('', 'Name of application') - .option('-s, --service ', 'Name of service to act on') - .description('Start an application') - .action(async (name, options) => { - const target = options.service ? stax.findContainer(name, options) : stax.find(name) - target.up() - }) + if (commandSeparator >= 0) { + const command = args.slice(commandSeparator+1).join(' ') + args = args.slice(0, commandSeparator) + args.push(command) + } + return [ args, overrides ] +} -let [ args, overrides ] = parseAndRemoveWildcardOptions(process.argv, '--stax.') -const commandSeparator = args.indexOf('--') +function runProgram() { + const program = new Command() + const [ args, overrides ] = buildArgs() -if (commandSeparator >= 0) { - const command = args.slice(commandSeparator+1).join(' ') - args = args.slice(0, commandSeparator) - args.push(command) + registerCommands(program, overrides as unknown as StaxConfig) + program.version(version.replace('v', '')) + program.name('stax') + program.parse(args as string[]) } -tmp.setGracefulCleanup() +function init() { + process.env.STAX_HOME = path.join(process.env.HOME, '.stax') + process.env.STAX_HOST_SERVICES = path.join(process.env.STAX_HOME, 'host-services') -process.env.STAX_HOME = path.join(process.env.HOME, '.stax') -process.env.STAX_HOST_SERVICES = path.join(process.env.STAX_HOME, 'host-services') + if (!existsSync(process.env.STAX_HOME)) mkdirSync(process.env.STAX_HOME) + if (!existsSync(process.env.STAX_HOST_SERVICES)) mkdirSync(process.env.STAX_HOST_SERVICES) -if (!existsSync(process.env.STAX_HOME)) mkdirSync(process.env.STAX_HOME) -if (!existsSync(process.env.STAX_HOST_SERVICES)) mkdirSync(process.env.STAX_HOST_SERVICES) + process.on('SIGINT', () => { tmp.setGracefulCleanup(); process.exit() }) + tmp.setGracefulCleanup() +} -process.on('SIGINT', () => { tmp.setGracefulCleanup(); process.exit() }) -program.parse(args) +init() +runProgram() diff --git a/src/commands/alias.ts b/src/commands/alias.ts new file mode 100644 index 0000000..65896ba --- /dev/null +++ b/src/commands/alias.ts @@ -0,0 +1,24 @@ +import { Command } from 'commander' +import { pp } from '~/utils' +import settings from '~/settings' +import Stax from '~/stax' + +export function registerAliasCommand(program: Command, stax: Stax) { + program.command('alias') + .argument('[name]', 'Name of application') + .argument('[alias]', 'Name of alias for application') + .description('Create an alias for an application that can be used in place of the application\'s name when running commands') + .action((name, alias) => { + if (name && alias) + return stax.find(name).addAlias(alias) + + const aliases = settings.read('aliases', null) + + if (aliases && Object.keys(aliases).length > 0) { + console.log('Current aliases:') + pp(aliases) + console.log('\nUsage: stax alias [options] [name] [alias]') + } else + program.commands.find(cmd => cmd.name() === 'alias').help() + }) +} diff --git a/src/commands/cat.ts b/src/commands/cat.ts new file mode 100644 index 0000000..f927441 --- /dev/null +++ b/src/commands/cat.ts @@ -0,0 +1,14 @@ +import { Command } from 'commander' +import Stax from '~/stax' + +export function registerCatCommand(program: Command, stax: Stax) { + program.command('cat') + .argument('', 'Name of application') + .argument('', 'Path to a file in the container') + .option('-s, --service ', 'Name of service to act on') + .description('Show contents of a file from the container') + .action(async (name, file, options) => { + const container = stax.findContainer(name, options) + await container.exec(`sh -c 'cat ${file} 2>/dev/null'`, { quiet: true }) + }) +} diff --git a/src/commands/config.ts b/src/commands/config.ts new file mode 100644 index 0000000..d419b7c --- /dev/null +++ b/src/commands/config.ts @@ -0,0 +1,19 @@ +import { Command } from 'commander' +import { pp } from '~/utils' +import Stax from '~/stax' + +export function registerConfigCommand(program: Command, stax: Stax) { + program.command('config') + .argument('', 'Name of application') + .option('-s, --service ', 'Name of service to act on') + .option('-g, --get ', 'Get the value of a config variable') + .description('Show config variables for the container.') + .action((name, options) => { + const container = stax.findContainer(name, options) + + if (options.get) + console.log(container.config.fetch(options.get) || '') + else + pp({ ...container.config, labels: container.labels }) + }) +} diff --git a/src/commands/copy.ts b/src/commands/copy.ts new file mode 100644 index 0000000..88cef03 --- /dev/null +++ b/src/commands/copy.ts @@ -0,0 +1,14 @@ +import { Command } from 'commander' +import Stax from '~/stax' + +export function registerCopyCommand(program: Command, stax: Stax) { + program.command('copy') + .alias('cp') + .argument('', 'Name of application') + .argument('', 'Path to a local file or directory') + .argument('', 'Path to a destination file or directory in the container') + .option('-s, --service ', 'Name of service to act on') + .option('-n, --dont-overwrite', 'Don\'t overwrite if file already exists') + .description('Copy a file to the container') + .action(async (name, source, destination, options) => stax.findContainer(name, options).copy(source, destination, options)) +} diff --git a/src/commands/down.ts b/src/commands/down.ts new file mode 100644 index 0000000..8b92a7f --- /dev/null +++ b/src/commands/down.ts @@ -0,0 +1,13 @@ +import { Command } from 'commander' +import Stax from '~/stax' + +export function registerDownCommand(program: Command, stax: Stax) { + program.command('down') + .argument('', 'Name of application') + .option('-s, --service ', 'Name of service to act on') + .description('Stop an application') + .action(async (name, options) => { + const target = options.service ? stax.findContainer(name, options) : stax.find(name) + target.down() + }) +} diff --git a/src/commands/duplicate.ts b/src/commands/duplicate.ts new file mode 100644 index 0000000..f4e6434 --- /dev/null +++ b/src/commands/duplicate.ts @@ -0,0 +1,12 @@ +import { Command } from 'commander' +import { StaxConfig } from '~/types' +import Stax from '~/stax' + +export function registerDuplicateCommand(program: Command, stax: Stax, overrides: StaxConfig) { + program.command('duplicate') + .argument('', 'Name of application') + .argument('', 'Name of new application') + .option('-i, --inspect', 'Show the compose file') + .description('Duplicate an application') + .action(async (name, newName, options) => { await stax.find(name).duplicate(newName, overrides as unknown as StaxConfig, options) }) +} diff --git a/src/commands/edit.ts b/src/commands/edit.ts new file mode 100644 index 0000000..9eb291b --- /dev/null +++ b/src/commands/edit.ts @@ -0,0 +1,35 @@ +import { exit } from "~/utils" +import { Command } from 'commander' +import { run } from "~/shell" +import Stax from '~/stax' + +export function registerEditCommand(program: Command, stax: Stax) { + const editor = process.env.STAX_EDITOR || 'code' + + program.command('edit') + .argument('', 'Name of application') + .description(`Open application in a vscode based editor`) + .action(async name => { + const app = stax.find(name) + let starting = false + + if (!app.primary.running) { + starting = true + console.log(`🚀 ${name} container(s) are not running. Starting them now."`) + await app.up() + } + + if (!app.primary.config.workspace) + return exit(0, { message: `${name} has no 'workspace' defined.` }) + + // kill vscode servers to fix problem where vscode can't access any files sometimes + // when starting and trying to attach to the container + if (!starting && (editor === 'code' || editor === 'cursor')) { + const processToKill = `[${editor[0]}]${editor.slice(1)}-server` + await app.primary.exec(`sh -c 'pkill --echo --full "${processToKill}" || true'`) + } + + const hex = Buffer.from(JSON.stringify({ containerName: app.primary.containerName })).toString('hex') + run(`${editor} --folder-uri=vscode-remote://attached-container+${hex}%${app.primary.config.workspace}`) + }) +} diff --git a/src/commands/exec.ts b/src/commands/exec.ts new file mode 100644 index 0000000..443684c --- /dev/null +++ b/src/commands/exec.ts @@ -0,0 +1,22 @@ +import { Command } from 'commander' +import Stax from '~/stax' + +export function registerExecCommand(program: Command, stax: Stax) { + program.command('exec') + .alias('run') + .argument('', 'Name of application') + .argument('', 'Command to execute. Use "--" before your command if it has more than one word.') + .option('-s, --service ', 'Name of service to act on') + .option('-q, --quiet', 'Don\'t print logging and info messages') + .option('-h, --hook', 'Run a hook where the command is the name of the hook to run') + .description('Execute a command (or hook) in a running application') + .action(async (name, command, options) => { + const container = await stax.findContainer(name, options) + + if (options.hook) + container.runHook(command) + else + container.exec(command, { quiet: options.quiet }) + }) +} + diff --git a/src/commands/get.ts b/src/commands/get.ts new file mode 100644 index 0000000..2b8a45d --- /dev/null +++ b/src/commands/get.ts @@ -0,0 +1,12 @@ +import { Command } from 'commander' +import Stax from '~/stax' + +export function registerGetCommand(program: Command, stax: Stax) { + program.command('get') + .argument('', 'Name of application') + .argument('', 'File to copy from the container') + .argument('', 'Local destination to copy the file to') + .option('-s, --service ', 'Name of service to act on') + .description('Copy a from the container') + .action(async (name, source, destination, options) => stax.findContainer(name, options).get(source, destination)) +} diff --git a/src/commands/index.ts b/src/commands/index.ts new file mode 100644 index 0000000..6e988b1 --- /dev/null +++ b/src/commands/index.ts @@ -0,0 +1,48 @@ +import { Command } from 'commander' +import { registerAliasCommand } from './alias' +import { registerCatCommand } from './cat' +import { registerLsCommand } from './ls' +import { registerConfigCommand } from './config' +import { registerCopyCommand } from './copy' +import { registerDownCommand } from './down' +import { registerDuplicateCommand } from './duplicate' +import { registerEditCommand } from './edit' +import { registerExecCommand } from './exec' +import { registerGetCommand } from './get' +import { registerInspectCommand } from './inspect' +import { registerLogsCommand } from './logs' +import { registerRebuildCommand } from './rebuild' +import { registerRemoveCommand } from './remove' +import { registerRestartCommand } from './restart' +import { registerSettingsCommand } from './settings' +import { registerSetupCommand } from './setup' +import { registerShellCommand } from './shell' +import { registerUpCommand } from './up' +import { StaxConfig } from '~/types' +import Stax from '~/stax' + +const DEFAULT_CONTEXT_NAME = 'stax' + +export function registerCommands(program: Command, overrides: StaxConfig) { + const stax = new Stax(DEFAULT_CONTEXT_NAME) + + registerAliasCommand(program, stax) + registerCatCommand(program, stax) + registerLsCommand(program, stax) + registerConfigCommand(program, stax) + registerCopyCommand(program, stax) + registerDownCommand(program, stax) + registerDuplicateCommand(program, stax, overrides) + registerEditCommand(program, stax) + registerExecCommand(program, stax) + registerGetCommand(program, stax) + registerInspectCommand(program, stax) + registerLogsCommand(program, stax) + registerRebuildCommand(program, stax, overrides) + registerRemoveCommand(program, stax) + registerRestartCommand(program, stax) + registerSettingsCommand(program) + registerSetupCommand(program, stax, overrides) + registerShellCommand(program, stax) + registerUpCommand(program, stax) +} diff --git a/src/commands/inspect.ts b/src/commands/inspect.ts new file mode 100644 index 0000000..3b0d187 --- /dev/null +++ b/src/commands/inspect.ts @@ -0,0 +1,26 @@ +import { Command } from 'commander' +import { readFileSync } from 'fs' +import { capture } from '~/shell' +import { pp } from '~/utils' +import Stax from '~/stax' + +export function registerInspectCommand(program: Command, stax: Stax) { + program.command('inspect') + .argument('', 'Name of application') + .option('-c, --compose', 'Show the compose file') + .option('-d, --dockerfile', 'Show the Dockerfile build (if any)') + .option('-l, --labels', 'Show container labels') + .description('Inspect the container or build files.') + .action((name, options) => { + const app = stax.find(name) + + if (options.compose) + pp(readFileSync(stax.find(name).primary.composeFile, 'utf-8')) + else if (options.dockerfile) + console.log('TODO') + else if (options.labels) + console.log(app.primary.labels) + else + pp(JSON.parse(capture(`docker inspect ${app.primary.containerName}`))) + }) +} diff --git a/src/commands/logs.ts b/src/commands/logs.ts new file mode 100644 index 0000000..6c42ba3 --- /dev/null +++ b/src/commands/logs.ts @@ -0,0 +1,18 @@ +import { Command } from 'commander' +import Stax from '~/stax' + +export function registerLogsCommand(program: Command, stax: Stax) { + program.command('logs') + .argument('', 'Name of application') + .option('-s, --service ', 'Name of service to act on') + .option('-f, --follow', 'Follow log output') + .option('-t, --tail ', 'Number of lines to show from the end of the logs') + .option('--since ', 'A time or duration string accepted by \'docker container logs\'') + .description('Tail logs for an application') + .action(async (name, options) => { + const follow = options.follow || false + const tail = options.tail ? parseInt(options.tail) : undefined + const since = options.since + await stax.findContainer(name, options).logs({ follow, tail, since }) + }) +} diff --git a/src/commands/ls.ts b/src/commands/ls.ts new file mode 100644 index 0000000..0edea5a --- /dev/null +++ b/src/commands/ls.ts @@ -0,0 +1,10 @@ +import { Command } from 'commander' +import Stax from '~/stax' + +export function registerLsCommand(program: Command, stax: Stax) { + program.command('ls') + .alias('ps').alias('list') + .description('List applications') + .option('-f, --field ', 'Include specified config field', (value, previous) => previous.concat([value]), []) + .action((options) => stax.list({ fields: options.field || [] })) +} diff --git a/src/commands/rebuild.ts b/src/commands/rebuild.ts new file mode 100644 index 0000000..006a865 --- /dev/null +++ b/src/commands/rebuild.ts @@ -0,0 +1,13 @@ +import { Command } from 'commander' +import { StaxConfig } from '~/types' +import Stax from '~/stax' + +export function registerRebuildCommand(program: Command, stax: Stax, overrides: StaxConfig) { + program.command('rebuild') + .argument('', 'Name of application') + .option('-i, --inspect', 'Show the compose file') + .option('--no-cache', 'Don\'t use cache when building images') + .option('--progress ', 'Set type of progress output') + .description('Rebuild an application') + .action(async (name, options) => { await stax.find(name).rebuild(overrides, options) }) +} diff --git a/src/commands/remove.ts b/src/commands/remove.ts new file mode 100644 index 0000000..9c8f738 --- /dev/null +++ b/src/commands/remove.ts @@ -0,0 +1,10 @@ +import { Command } from 'commander' +import Stax from '~/stax' + +export function registerRemoveCommand(program: Command, stax: Stax) { + program.command('remove') + .alias('rm') + .argument('', 'Name of application') + .description('Remove application') + .action(async name => { await stax.find(name).remove() }) +} diff --git a/src/commands/restart.ts b/src/commands/restart.ts new file mode 100644 index 0000000..0af43d4 --- /dev/null +++ b/src/commands/restart.ts @@ -0,0 +1,9 @@ +import { Command } from 'commander' +import Stax from '~/stax' + +export function registerRestartCommand(program: Command, stax: Stax) { + program.command('restart') + .argument('') + .description('Restart an application') + .action(async name => { await stax.find(name).restart() }) +} diff --git a/src/commands/settings.ts b/src/commands/settings.ts new file mode 100644 index 0000000..5d534de --- /dev/null +++ b/src/commands/settings.ts @@ -0,0 +1,17 @@ +import { Command } from 'commander' +import settings from '~/settings' + +export function registerSettingsCommand(program: Command) { + program.command('settings') + .argument('', 'Name of setting') + .argument('[value]', 'Value of setting') + .option('-s, --set', 'Set the value of the setting') + .description('Get or set stax settings') + .action((name, value, options) => { + if (options.set) { + value = settings.write(name, value) + console.log(`${icons.saved} Setting for '${name}' set to '${value}'`) + } else + console.log(settings.read(name)) + }) +} diff --git a/src/commands/setup.ts b/src/commands/setup.ts new file mode 100644 index 0000000..31473d1 --- /dev/null +++ b/src/commands/setup.ts @@ -0,0 +1,27 @@ +import { Command } from 'commander' +import { StaxConfig } from '~/types' +import { requireDockerVersion } from '~/utils' +import setupWizard from '~/setup_wizard' +import Stax from '~/stax' +import settings from '~/settings' + +export function registerSetupCommand(program: Command, stax: Stax, overrides: StaxConfig) { + program.command('setup') + .argument('[location]', 'Path to a local directory or git repo of application') + .option('-s, --staxfile ', 'Staxfile to use for setup') + .option('-i, --inspect', 'Show the compose file') + .option('--no-cache', 'Don\'t use cache when building images') + .option('--progress ', 'Set type of progress output') + .description('Setup an application') + .action(async (location, options) => { + requireDockerVersion(27.0, 2.29) + + if (location) { + const app = await stax.setup({ source: location, ...options }, { ...options, overrides: overrides }) + console.log('\n' + app.installedMessage()) + } else if (settings.read('services_home')) + await setupWizard(stax) + else + console.log('Please specify an application location or set the services home directory.') + }) +} diff --git a/src/commands/shell.ts b/src/commands/shell.ts new file mode 100644 index 0000000..ca49ba2 --- /dev/null +++ b/src/commands/shell.ts @@ -0,0 +1,11 @@ +import { Command } from 'commander' +import Stax from '~/stax' + +export function registerShellCommand(program: Command, stax: Stax) { + program.command('shell') + .alias('sh') + .argument('', 'Name of application') + .option('-s, --service ', 'Name of service to act on') + .description('Shell into application\' primary container') + .action(async (name, options) => stax.findContainer(name, options).shell()) +} diff --git a/src/commands/up.ts b/src/commands/up.ts new file mode 100644 index 0000000..dbf39e4 --- /dev/null +++ b/src/commands/up.ts @@ -0,0 +1,13 @@ +import { Command } from 'commander' +import Stax from '~/stax' + +export function registerUpCommand(program: Command, stax: Stax) { + program.command('up') + .argument('', 'Name of application') + .option('-s, --service ', 'Name of service to act on') + .description('Start an application') + .action(async (name, options) => { + const target = options.service ? stax.findContainer(name, options) : stax.find(name) + target.up() + }) +}