@toa.io/cli 0.20.0-dev.3 → 0.20.0-dev.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/cli",
3
- "version": "0.20.0-dev.3",
3
+ "version": "0.20.0-dev.33",
4
4
  "description": "Toa CLI",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -22,13 +22,14 @@
22
22
  "@toa.io/runtime": "*"
23
23
  },
24
24
  "dependencies": {
25
- "@toa.io/console": "0.20.0-dev.3",
26
- "@toa.io/generic": "0.20.0-dev.3",
27
- "@toa.io/kubernetes": "0.20.0-dev.3",
28
- "@toa.io/norm": "0.20.0-dev.3",
29
- "@toa.io/yaml": "0.20.0-dev.3",
25
+ "@toa.io/console": "0.20.0-dev.34",
26
+ "@toa.io/generic": "0.20.0-dev.34",
27
+ "@toa.io/kubernetes": "0.20.0-dev.34",
28
+ "@toa.io/norm": "0.20.0-dev.34",
29
+ "@toa.io/yaml": "0.20.0-dev.34",
30
+ "dotenv": "16.1.1",
30
31
  "find-up": "5.0.0",
31
32
  "yargs": "17.6.2"
32
33
  },
33
- "gitHead": "3eabf45761f4cf211e1e4c331933ec60b4a828a7"
34
+ "gitHead": "7035b1985fe9bb844069308a272d061bfbd38bf0"
34
35
  }
package/readme.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Toa Command Line Interface
2
2
 
3
+ ## Common Options
4
+
5
+ <dl>
6
+ <dt><code>--env</code></dt>
7
+ <dd>Path to the environment variables file (`.env` format)</dd>
8
+ </dl>
9
+
3
10
  ## Development
4
11
 
5
12
  ### compose
@@ -9,14 +16,15 @@ Run composition.
9
16
  <dl>
10
17
  <dt><code>toa compose [paths]</code></dt>
11
18
  <dd>
12
- <code>paths</code> glob patterns to look for components<br/>
13
- <code>--kill</code> shutdown composition after it's started<br/>
14
- <code>--dock</code> run in Docker using current <code>.env</code><br/>
15
- <code>--bindnings</code> override bindings (obsolete)<br/>
19
+ <code>paths</code> Glob patterns to look for components.<br/>
20
+ <code>--kill</code> Shutdown composition after it's started<br/>
21
+ <code>--dock</code> Run in Docker using current <code>.env</code>.<br/>
22
+ <code>--context</code> Path to the Context root (default <code>.</code>).<br/>
23
+ <code>--bindnings</code> Override bindings (obsolete).
16
24
  </dd>
17
25
  </dl>
18
26
 
19
- > Note that your `localhost` it is accessible from the container as `host.docker.internal`.
27
+ > Note that your `localhost` it is accessible from a container as `host.docker.internal`.
20
28
 
21
29
  ### env
22
30
 
@@ -26,13 +34,15 @@ Export environment to a `.env` file.
26
34
  <dt><code>toa env [environment]</code></dt>
27
35
  <dd>
28
36
  <code>environment</code> deployment environment name (default <code>local</code>).<br/>
29
- <code>--path</code> path to context (default <code>.</code>)<br/>
37
+ <code>--path</code> path to a Context (default <code>.</code>)<br/>
38
+ <code>--as</code> output file path (default <code>.env</code>)<br/>
39
+ <code>--interactive</code> prompt for secret values
30
40
  </dd>
31
41
  </dl>
32
42
 
33
- Credentials specified in `.env` file are preserved.
43
+ Credentials specified in the output file are preserved.
34
44
 
35
- > It is recommended to add `.env` to `.gitignore`.
45
+ > It is recommended to add `.env*` to `.gitignore`.
36
46
 
37
47
  ### replay
38
48
 
@@ -42,9 +52,10 @@ format.
42
52
  <dl>
43
53
  <dt><code>toa replay [paths...]</code></dt>
44
54
  <dd>
45
- <code>paths</code> Path(s) to component(s) or a context (default <code>.</code>).<br/>
46
- <code>--integration</code> Replay integration tests only.<br/>
55
+ <code>paths</code> Path(s) to Component(s) or a Context (default <code>.</code>).<br/>
47
56
  <code>--component &lt;id&gt;</code> Replay samples for a specified component <code>id</code>.<br/>
57
+ <code>--integration</code> Replay integration tests only.<br/>
58
+ <code>--autonomous</code> Replay autonomous tests only.<br/>
48
59
  <code>--operation &lt;name&gt;</code> Replay samples for specified operation.<br/>
49
60
  <code>--title &lt;regexp&gt;</code> Regexp to match sample titles.<br/>
50
61
  <code>--dock</code> Run in Docker. Applicable only for component samples.
@@ -62,8 +73,9 @@ $ toa replay ./path/to/context
62
73
  $ toa replay --title "should add numbers"
63
74
  ```
64
75
 
65
- If a path is a context directory (containing `context.toa.yaml` file), samples for components within
66
- the context will be found and replayed sequentially.
76
+ If the path is a Context root (containing `context.toa.yaml` file), samples for components within
77
+ the Context will be
78
+ found and replayed sequentially.
67
79
 
68
80
  ### export manifest
69
81
 
@@ -72,54 +84,96 @@ the context will be found and replayed sequentially.
72
84
  <dd>Print normalized manifest.
73
85
 
74
86
  <code>--path</code> path to component (default <code>.</code>)<br/>
75
- <code>--error</code> print errors only<br/>
87
+ <code>--error</code> print errors only
76
88
  </dd>
77
89
  </dl>
78
90
 
79
91
  ## Operations
80
92
 
81
- > Commands use current Kubernetes context.
93
+ > Some commands use current `kubectl` and `docker` context.
94
+
95
+ ### build
96
+
97
+ Build Docker images.
98
+
99
+ <dl>
100
+ <dt><code>toa build</code></dt>
101
+ <dd>
102
+ <code>--path</code> path to a Context (default <code>.</code>)
103
+ </dd>
104
+ </dl>
82
105
 
83
106
  ### deploy
84
107
 
108
+ Deploy a Context.
109
+
110
+ - Build Docker images.
111
+ - Push Docker images to the registry.
112
+ - Build a Helm chart.
113
+ - Apply the Helm chart to the current Kubernetes context.
114
+
85
115
  <dl>
86
116
  <dt><code>toa deploy [environment]</code></dt>
87
- <dd>Deploy context.
88
-
117
+ <dd>
89
118
  <code>environment</code> deployment environment name (default <code>default</code>).<br/>
119
+ <code>--path</code> path to a Context (default <code>.</code>)<br/>
120
+ <code>--namespace</code> Kubernetes namespace to apply the Helm chat to<br/>
121
+ <code>--wait</code> wait until all
122
+ Pods [are ready](https://helm.sh/docs/intro/using_helm/#helpful-options-for-installupgraderollback)<br/>
123
+ <code>--dry</code> do not apply the Helm chart
90
124
  </dd>
91
125
  </dl>
92
126
 
93
127
  ### conceal
94
128
 
129
+ Deploy a generic Kubernetes secret with the prefix `toa-`.
130
+
95
131
  <dl>
96
- <dt><code>toa conceal &lt;secret&gt; &lt;key&gt; &lt;value&gt;</code></dt>
97
- <dd>Deploy a <code>key</code> with a <code>value</code> to a secret named <code>toa-{secret}</code>.</dd>
132
+ <dt><code>toa conceal &lt;secret&gt; &lt;key-values...&gt;</code></dt>
133
+ <dd>
134
+ <code>secret</code> Secret name.<br/>
135
+ <code>key-values</code> List of keys and values of the secret as <code>key=value</code>.<br/>
136
+ <code>--namespace</code> Kubernetes namespace where the secret should be deployed.<br/>
137
+ <code>--interactive</code> prompt for secret values<br/>
138
+ <code>--environment</code> environment name for interactive mode<br/>
139
+ <code>--path</code> path to a context for interactive mode
140
+ </dd>
98
141
  </dl>
99
142
 
143
+ > If a secret already exists, then given `key-values` will be added to it.
144
+
145
+ #### Example
146
+
147
+ ```shell
148
+ $ toa conceal bindings-amqp-default username=developer password=secret
149
+ ```
150
+
100
151
  ### reveal
101
152
 
153
+ Outputs keys and values of a secret.
154
+
102
155
  <dl>
103
156
  <dt>
104
157
  <code>toa reveal &lt;secret&gt;</code>
105
158
  </dt>
106
- <dd>Print keys and values of a secret.</dd>
107
159
  </dl>
108
160
 
109
161
  ### shell
110
162
 
163
+ Run interactive shell inside a disposable pod inside a Kubernetes cluster.
164
+
111
165
  <dl>
112
166
  <dt>
113
167
  <code>toa shell [image]</code>
114
168
  </dt>
115
- <dd>Run interactive shell inside a disposable pod.
116
-
117
- <code>image</code> Docker image<br/>
169
+ <dd>
170
+ <code>image</code> Docker image to Run (default <code>alpine</code>).<br/>
118
171
  </dd>
119
172
  </dl>
120
173
 
121
- Extra arguments can be passed:
174
+ #### Examples
122
175
 
123
176
  ```shell
124
- $ toa shell -- ping 1.1
177
+ $ toa shell mongo
178
+ $ toa shell -- ping 1.1 # extra arguments can be passed
125
179
  ```
@@ -22,6 +22,12 @@ const builder = (yargs) => {
22
22
  type: 'boolean',
23
23
  desc: 'Run in Docker'
24
24
  })
25
+ .option('context', {
26
+ group: 'Command options:',
27
+ type: 'string',
28
+ desc: 'Path to the Context (used with --dock)',
29
+ default: '.'
30
+ })
25
31
  .option('bindings', {
26
32
  group: 'Command options:',
27
33
  type: 'string',
@@ -7,15 +7,46 @@ const builder = (yargs) => {
7
7
  .positional('secret', {
8
8
  type: 'string'
9
9
  })
10
- .positional('key', {
11
- type: 'string'
10
+ .positional('key-values', {
11
+ type: 'string',
12
+ array: true,
13
+ desc: 'Secret key-value pairs'
14
+ })
15
+ .option('namespace', {
16
+ alias: 'n',
17
+ group: 'Command options:',
18
+ type: 'string',
19
+ desc: 'Target Kubernetes namespace'
12
20
  })
13
- .positional('value', {
21
+ .option('interactive', {
22
+ alias: 'i',
23
+ group: 'Command options:',
24
+ describe: 'Prompt for secrets',
25
+ type: 'boolean',
26
+ default: false
27
+ })
28
+ .option('environment', {
29
+ alias: 'e',
30
+ group: 'Command options:',
31
+ describe: 'Environment name for interactive mode',
14
32
  type: 'string'
15
33
  })
34
+ .option('path', {
35
+ alias: 'p',
36
+ group: 'Command options:',
37
+ describe: 'Path to a Context for interactive mode',
38
+ type: 'string',
39
+ default: '.'
40
+ })
41
+ .example([
42
+ ['$0 conceal -i'],
43
+ ['$0 conceal credentials username=developer'],
44
+ ['$0 conceal credentials username=developer password=secret'],
45
+ ['$0 conceal credentials username=developer --namespace app']
46
+ ])
16
47
  }
17
48
 
18
- exports.command = 'conceal <secret> <key> <value>'
19
- exports.desc = 'Deploy a key with a value to a secret'
49
+ exports.command = 'conceal [secret] [key-values...]'
50
+ exports.desc = 'Deploy a secret'
20
51
  exports.builder = builder
21
52
  exports.handler = conceal
@@ -12,10 +12,23 @@ const builder = (yargs) => {
12
12
  .option('path', {
13
13
  alias: 'p',
14
14
  group: 'Command options:',
15
- describe: 'Path to component',
15
+ describe: 'Path to a Context',
16
16
  type: 'string',
17
17
  default: '.'
18
18
  })
19
+ .option('as', {
20
+ group: 'Command options:',
21
+ describe: 'Output file path',
22
+ type: 'string',
23
+ default: '.env'
24
+ })
25
+ .option('interactive', {
26
+ alias: 'i',
27
+ group: 'Command options:',
28
+ describe: 'Prompt for secrets',
29
+ type: 'boolean',
30
+ default: false
31
+ })
19
32
  }
20
33
 
21
34
  exports.command = 'env [environment]'
@@ -21,6 +21,12 @@ const builder = (yargs) => {
21
21
  group: 'Command options:',
22
22
  describe: 'Replay samples for specified component'
23
23
  })
24
+ .option('autonomous', {
25
+ alias: 'a',
26
+ type: 'boolean',
27
+ group: 'Command options:',
28
+ describe: 'Replay autonomous tests only'
29
+ })
24
30
  .option('integration', {
25
31
  alias: 'i',
26
32
  type: 'boolean',
@@ -46,6 +52,12 @@ const builder = (yargs) => {
46
52
  group: 'Command options:',
47
53
  describe: 'Replay inside Docker container'
48
54
  })
55
+ .option('context', {
56
+ group: 'Command options:',
57
+ type: 'string',
58
+ desc: 'Path to the Context (used with --dock)',
59
+ default: '.'
60
+ })
49
61
  }
50
62
 
51
63
  exports.command = 'replay [paths...]'
@@ -1,6 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const { file: { dot } } = require('@toa.io/filesystem')
4
3
  const { pick } = require('@toa.io/generic')
5
4
  const boot = require('@toa.io/boot')
6
5
 
@@ -27,12 +26,11 @@ async function compose (argv) {
27
26
  * @return {Promise<void>}
28
27
  */
29
28
  async function dock (argv) {
30
- const envFile = await dot('env')
31
- const repository = await docker.build(argv.paths)
29
+ const repository = await docker.build(argv.context, argv.paths)
32
30
  const args = pick(argv, ['kill', 'bindings'])
33
31
  const command = docker.command('toa compose *', args)
34
32
 
35
- await docker.run(repository, command, ['--env-file', envFile])
33
+ await docker.run(repository, command, argv.env)
36
34
  }
37
35
 
38
36
  exports.compose = compose
@@ -1,12 +1,58 @@
1
1
  'use strict'
2
2
 
3
3
  const { secrets } = require('@toa.io/kubernetes')
4
+ const boot = require('@toa.io/boot')
5
+ const { context: find } = require('../util/find')
6
+ const { promptSecrets } = require('./env')
4
7
 
5
8
  const conceal = async (argv) => {
6
- const { secret, key, value } = argv
7
- const prefixed = PREFIX + secret
9
+ if (argv.interactive) await concealValues(argv)
10
+ else await concealValue(argv)
11
+ }
12
+
13
+ async function concealValue (argv) {
14
+ if (argv['key-values'].length === 0) throw new Error('Key-values must be passed')
15
+
16
+ const values = argv['key-values'].reduce((values, pair) => {
17
+ const [key, value] = pair.split('=')
18
+
19
+ values[key] = value
20
+
21
+ return values
22
+ }, {})
23
+
24
+ const secret = PREFIX + argv.secret
25
+
26
+ await secrets.upsert(secret, values, argv.namespace)
27
+ }
28
+
29
+ async function concealValues (argv) {
30
+ const path = find(argv.path)
31
+ const operator = await boot.deployment(path, argv.environment)
32
+ const variables = operator.variables()
33
+ const values = await promptSecrets(variables)
34
+ const groups = groupValues(values)
35
+
36
+ for (const [secret, values] of Object.entries(groups)) {
37
+ await secrets.upsert(secret, values, argv.namespace)
38
+ }
39
+ }
40
+
41
+ /**
42
+ * @return {Record<string, Record<string, string>>}
43
+ */
44
+ function groupValues (values) {
45
+ const secrets = {}
46
+
47
+ for (const [key, value] of Object.entries(values)) {
48
+ const [secret, variable] = key.split('/')
49
+
50
+ if (!(secret in secrets)) secrets[secret] = {}
51
+
52
+ secrets[secret][variable] = value
53
+ }
8
54
 
9
- await secrets.store(prefixed, { [key]: value })
55
+ return secrets
10
56
  }
11
57
 
12
58
  const PREFIX = 'toa-'
@@ -1,18 +1,18 @@
1
1
  'use strict'
2
2
 
3
3
  const { newid } = require('@toa.io/generic')
4
- const { component: load } = require('@toa.io/norm')
4
+ const norm = require('@toa.io/norm')
5
5
  const { deployment: { Factory } } = require('@toa.io/operations')
6
- const runtime = require('@toa.io/runtime')
7
6
 
8
7
  const find = require('../../util/find')
9
8
 
10
9
  /**
11
- * @param {string[]} paths
10
+ * @param {string} contextPath
11
+ * @param {string[]} componentPatterns
12
12
  * @return {Promise<string>}
13
13
  */
14
- async function build (paths) {
15
- const context = await createContext(/** @type {string[]} */ paths)
14
+ async function build (contextPath, componentPatterns) {
15
+ const context = await createContext(contextPath, componentPatterns)
16
16
  const factory = new Factory(context)
17
17
  const registry = factory.registry()
18
18
 
@@ -20,32 +20,26 @@ async function build (paths) {
20
20
 
21
21
  const composition = context.compositions[0].name
22
22
 
23
- return `${context.name}/composition-${composition}`
23
+ return `${context.registry.base === undefined ? '' : context.registry.base + '/'}${context.name}/composition-${composition}`
24
24
  }
25
25
 
26
26
  /**
27
- * @param {string[]} patterns
27
+ * @param {string} contextPath
28
+ * @param {string[]} componentPatterns
28
29
  * @return {Promise<toa.norm.Context>}
29
30
  */
30
- async function createContext (patterns) {
31
- const paths = patterns.map((pattern) => find.components(pattern))
31
+ async function createContext (contextPath, componentPatterns) {
32
+ const contextRoot = find.context(contextPath)
33
+ const context = await norm.context(contextRoot)
34
+ const paths = componentPatterns.map((pattern) => find.components(pattern))
32
35
  const components = await loadComponents(paths)
33
36
  const rnd = newid().substring(0, 6)
34
37
  const name = 'replay-' + rnd
35
38
 
36
- return {
37
- name: 'toa-temp',
38
- runtime: {
39
- version: runtime.version
40
- },
41
- registry: {
42
- platforms: null
43
- },
44
- compositions: [{
45
- name,
46
- components
47
- }]
48
- }
39
+ context.name += '-' + rnd
40
+ context.compositions = [{ name, components }]
41
+
42
+ return context
49
43
  }
50
44
 
51
45
  /**
@@ -56,7 +50,7 @@ async function loadComponents (paths) {
56
50
  const components = []
57
51
 
58
52
  for (const path of paths) {
59
- const component = await load(path)
53
+ const component = await norm.component(path)
60
54
 
61
55
  components.push(component)
62
56
  }
@@ -4,22 +4,27 @@ const { spawn, exec } = require('node:child_process')
4
4
  const { promisify } = require('node:util')
5
5
 
6
6
  const { promex } = require('@toa.io/generic')
7
+ const { file: { dot } } = require('@toa.io/filesystem')
7
8
 
8
9
  const execute = promisify(exec)
9
10
 
10
11
  /**
11
12
  * @param {string} repository
12
13
  * @param {string} command
13
- * @param {string[]} runArguments
14
+ * @param {string} [envFile]
14
15
  * @return {Promise<void>}
15
16
  */
16
- async function run (repository, command, runArguments) {
17
- const imagesResult =
17
+ async function run (repository, command, envFile) {
18
+ if (envFile === undefined) envFile = await dot('env')
19
+
20
+ const envArgs = envFile === undefined ? [] : ['--env-file', envFile]
21
+
22
+ const found =
18
23
  /** @type {{ stdout: string }} */
19
24
  await execute(`docker images -q ${repository} | head -n 1`)
20
25
 
21
- const id = imagesResult.stdout.trim()
22
- const args = ['run', '--rm', ...(runArguments ?? []), id, 'sh', '-c', command]
26
+ const id = found.stdout.trim()
27
+ const args = ['run', '--rm', ...envArgs, id, 'sh', '-c', command]
23
28
  const done = promex()
24
29
 
25
30
  const running = await spawn('docker', args, { stdio: 'inherit' })
@@ -28,7 +33,7 @@ async function run (repository, command, runArguments) {
28
33
 
29
34
  await done
30
35
 
31
- await execute(`docker rmi ${id}`)
36
+ await execute(`docker rmi --force ${id}`)
32
37
  }
33
38
 
34
39
  exports.run = run
@@ -1,6 +1,9 @@
1
1
  'use strict'
2
2
 
3
3
  const { join } = require('node:path')
4
+ const readline = require('node:readline/promises')
5
+ const { stdin: input, stdout: output } = require('node:process')
6
+
4
7
  const dotenv = require('dotenv')
5
8
  const { file } = require('@toa.io/filesystem')
6
9
  const boot = require('@toa.io/boot')
@@ -8,17 +11,20 @@ const { context: find } = require('../util/find')
8
11
 
9
12
  async function env (argv) {
10
13
  const path = find(argv.path)
11
- const filepath = join(path, '.env')
14
+ const filepath = join(path, argv.as)
12
15
  const operator = await boot.deployment(path, argv.environment)
13
16
  const variables = operator.variables()
14
17
  const currentValues = await read(filepath)
15
- const values = []
16
18
 
17
- for (const scoped of Object.values(variables)) values.push(...scoped)
19
+ const result = merge(variables, currentValues)
20
+
21
+ if (argv.interactive) {
22
+ const secrets = await promptSecrets(result)
18
23
 
19
- const nextValues = merge(values, currentValues)
24
+ mergeSecrets(result, secrets)
25
+ }
20
26
 
21
- await write(filepath, nextValues)
27
+ await write(filepath, result)
22
28
  }
23
29
 
24
30
  /**
@@ -41,25 +47,71 @@ async function read (path) {
41
47
  * @return {Promise<void>}
42
48
  */
43
49
  async function write (path, values) {
44
- const contents = values.reduce((lines, { name, value }) => lines + `${name}=${value}\n`, '')
50
+ const contents = values.reduce((lines, { name, value }) => lines + `${name}=${value ?? ''}\n`, '')
45
51
 
46
52
  await file.write(path, contents)
47
53
  }
48
54
 
49
55
  /**
50
- * @param {toa.deployment.dependency.Variable[] } variables
56
+ * @param {toa.deployment.dependency.Variable[]} variables
51
57
  * @param {Record<string, string>} current
52
58
  * @return {toa.deployment.dependency.Variable[]}
53
59
  */
54
60
  function merge (variables, current) {
55
61
  return variables.map((variable) => {
56
- if (variable.secret === undefined) return variable
62
+ if (variable.secret === undefined || !current[variable.name]) return variable
57
63
 
58
64
  return {
59
65
  name: variable.name,
60
- value: current[variable.name] ?? ''
66
+ value: current[variable.name]
61
67
  }
62
68
  })
63
69
  }
64
70
 
71
+ async function promptSecrets (variables) {
72
+ const rl = readline.createInterface({ input, output })
73
+ const secrets = {}
74
+
75
+ for (const variable of variables) {
76
+ if (variable.secret === undefined) continue
77
+
78
+ const key = getKey(variable.secret)
79
+
80
+ secrets[key] = await promptSecret(key, rl)
81
+ }
82
+
83
+ rl.close()
84
+
85
+ return secrets
86
+ }
87
+
88
+ async function promptSecret (key, rl) {
89
+ if (SECRETS[key] === undefined) SECRETS[key] = await rl.question(`${key}: `)
90
+
91
+ return SECRETS[key]
92
+ }
93
+
94
+ /**
95
+ * @param {toa.deployment.dependency.Variable[]} variables
96
+ * @param {Record<string, string>} secrets
97
+ */
98
+ function mergeSecrets (variables, secrets) {
99
+ for (const variable of variables) {
100
+ if (variable.secret === undefined) continue
101
+
102
+ const key = getKey(variable.secret)
103
+
104
+ variable.value = secrets[key]
105
+
106
+ delete variable.secret
107
+ }
108
+ }
109
+
110
+ function getKey (secret) {
111
+ return `${secret.name}/${secret.key}`
112
+ }
113
+
114
+ const SECRETS = {}
115
+
65
116
  exports.env = env
117
+ exports.promptSecrets = promptSecrets
@@ -7,7 +7,7 @@ const find = require('../util/find')
7
7
  const docker = require('./docker')
8
8
 
9
9
  /**
10
- * @param {Record<string, string | boolean>} argv
10
+ * @param {Record<string, string | string[] | boolean>} argv
11
11
  * @return {Promise<void>}
12
12
  */
13
13
  async function replay (argv) {
@@ -21,6 +21,7 @@ async function replay (argv) {
21
21
  /** @type {toa.samples.suite.Options} */
22
22
  const options = {
23
23
  component: argv.component,
24
+ autonomous: argv.autonomous,
24
25
  integration: argv.integration,
25
26
  operation: argv.operation,
26
27
  title: argv.title,
@@ -49,11 +50,11 @@ async function replay (argv) {
49
50
  * @return {Promise<void>}
50
51
  */
51
52
  async function dock (argv) {
52
- const repository = await docker.build(argv.paths)
53
+ const repository = await docker.build(argv.context, argv.paths)
53
54
  const args = pick(argv, ['component', 'operation', 'integration', 'title'])
54
55
  const command = docker.command('toa replay *', args)
55
56
 
56
- await docker.run(repository, command)
57
+ await docker.run(repository, command, argv.env)
57
58
  }
58
59
 
59
60
  const GREEN = '\x1b[32m'
@@ -1,16 +1,16 @@
1
1
  'use strict'
2
2
 
3
3
  const { secrets } = require('@toa.io/kubernetes')
4
- const { remap, decode } = require('@toa.io/generic')
5
4
 
6
5
  const { PREFIX } = require('./conceal')
7
6
 
8
7
  const reveal = async (argv) => {
9
8
  const prefixed = PREFIX + argv.secret
10
- const secret = await secrets.get(prefixed)
11
- const values = remap(secret.data, decode)
9
+ const data = await secrets.get(prefixed)
12
10
 
13
- for (const [key, value] of Object.entries(values)) {
11
+ if (data === null) return
12
+
13
+ for (const [key, value] of Object.entries(data)) {
14
14
  const line = `${key}: ${value}`
15
15
 
16
16
  console.log(line)
package/src/program.js CHANGED
@@ -16,6 +16,11 @@ yargs(process.argv.slice(2))
16
16
 
17
17
  console.level(argv.log)
18
18
  })
19
+ .middleware(async (argv) => {
20
+ if (argv.env === undefined) return
21
+
22
+ require('dotenv').config({ path: /** @type {string} */ argv.env })
23
+ })
19
24
  .fail((msg, err) => {
20
25
  const actual = err || new Error(msg)
21
26
 
@@ -26,6 +31,10 @@ yargs(process.argv.slice(2))
26
31
  .option('log', {
27
32
  describe: 'Log level'
28
33
  })
34
+ .option('env', {
35
+ type: 'string',
36
+ describe: 'Path to environment variables file (.env format)'
37
+ })
29
38
  .commandDir('./commands')
30
39
  .demandCommand(1, 'A command is required. Pass --help to see all available commands and options.')
31
40
  .strict()