UNPKG

4.71 kBPlain TextView Raw
1#!/usr/bin/env node
2
3import os from 'os'
4import path from 'path'
5// @ts-ignore
6import yargonaut from 'yargonaut'
7import yargs from 'yargs'
8import yaml from 'js-yaml'
9
10const VERSION = require('../package').version
11
12import DockerCompiler from './DockerCompiler'
13import { ApplicationError } from './errors'
14import CachingUrlFetcher from './CachingUrlFetcher'
15import nix from './cli-nix'
16
17const compiler = new DockerCompiler(new CachingUrlFetcher())
18
19yargonaut
20 .style('blue')
21 .helpStyle('green')
22 .errorsStyle('red')
23
24yargs
25 .scriptName('dockter')
26
27 // Help global option
28 .alias('h', 'help')
29 .usage('$0 <cmd> [args]')
30
31 // Version global option
32 .alias('v', 'version')
33 .version(VERSION)
34 .describe('version', 'Show version')
35
36 // Nix global option
37 .option('nix', {
38 describe: 'Use NixOS base image'
39 })
40
41 // Ensure at least one command
42 .demandCommand(1, 'Please provide a command.')
43 // Provide suggestions regarding similar commands if no matching command is found
44 .recommendCommands()
45 // Any command-line argument given that is not demanded, or does not have a corresponding description, will be reported as an error.
46 // Unrecognized commands will also be reported as errors.
47 .strict()
48
49 // Compile command
50 // @ts-ignore
51 .command('compile [folder]', 'Compile a project to a software environment', yargs => {
52 folderArg(yargs)
53 }, async (args: any) => {
54 const folder = path.resolve(args.folder)
55 let environ = await compiler.compile('file://' + folder, false).catch(error)
56 if (args.nix) {
57 nix.compile(environ, folder)
58 }
59 })
60
61 // Build command
62 // @ts-ignore
63 .command('build [folder]', 'Build a Docker image for project', yargs => {
64 folderArg(yargs)
65 }, async (args: any) => {
66 const folder = path.resolve(args.folder)
67 if (args.nix) {
68 await nix.build(folder)
69 } else {
70 await compiler.compile('file://' + folder, true).catch(error)
71 }
72 })
73
74 // Execute command
75 // @ts-ignore
76 .command('execute [folder] [command]', 'Execute a project', yargs => {
77 folderArg(yargs),
78 yargs.positional('command', {
79 type: 'string',
80 default: '',
81 describe: 'The command to execute'
82 })
83 }, async (args: any) => {
84 const folder = path.resolve(args.folder)
85 if (args.nix) {
86 await nix.execute(folder, args.command)
87 } else {
88 const node = await compiler.execute('file://' + folder, args.command).catch(error)
89 output(node, args.format)
90 }
91 })
92
93 // Who command
94 // @ts-ignore
95 .command('who [folder]', 'List the people your project depends upon', yargs => {
96 folderArg(yargs)
97 yargs.option('depth', {
98 alias: 'd',
99 type: 'integer',
100 default: 100,
101 describe: 'The maximum dependency recursion depth'
102 })
103 }, async (args: any) => {
104 const folder = path.resolve(args.folder)
105 const people = await compiler.who('file://' + folder, args.depth).catch(error)
106 if (!people) {
107 console.log('Nobody (?)')
108 } else {
109 // Sort by number of packages descending and then alphabetically ascending
110 let sorted = Object.entries(people).sort(([aName,aPkgs], [bName, bPkgs]) => {
111 if (aPkgs.length > bPkgs.length) return -1
112 if (aPkgs.length < bPkgs.length) return 1
113 if (aName < bName) return -1
114 if (aName > bName) return 1
115 return 0
116 })
117 // Output in a CLI friendly way
118 const output = sorted.map(([name, packages]) => {
119 return `${name} (${packages.join(', ')})`
120 }).join(', ')
121 console.log(output)
122 }
123 })
124
125 .parse()
126
127/**
128 * Specify the [folder] argument settings
129 *
130 * @param yargs The yargs object
131 */
132function folderArg (yargs: yargs.Argv) {
133 yargs.positional('folder', {
134 type: 'string',
135 default: '.',
136 describe: 'The path to the project folder'
137 })
138}
139
140/**
141 * Print output to stdout
142 *
143 * @param object The object to print
144 * @param format The format use: `json` or `yaml`
145 */
146function output (object: any, format: string = 'json') {
147 if (object) console.log(format === 'yaml' ? yaml.safeDump(object, { lineWidth: 120 }) : JSON.stringify(object, null, ' '))
148}
149
150/**
151 * Print an error to stderr
152 *
153 * @param error The error to print
154 */
155function error (error: Error) {
156 if (error instanceof ApplicationError) {
157 console.error(error.message)
158 } else {
159 console.error('Woops, sorry something went wrong :(')
160 console.error('Please help us fix this issue by posting this output to https://github.com/stencila/dockter/issues/new')
161 console.error(` args: ${process.argv.slice(2).join(' ')}`)
162 console.error(` version: ${VERSION}`)
163 console.error(` platform: ${os.platform()}`)
164 console.error(error)
165 process.exit(1)
166 }
167}