hereya-cli 0.3.3 → 0.4.0
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/README.md +61 -16
- package/dist/commands/add/index.js +2 -0
- package/dist/commands/deploy/index.d.ts +10 -0
- package/dist/commands/deploy/index.js +108 -0
- package/dist/commands/remote/exec/index.d.ts +1 -0
- package/dist/commands/remote/exec/index.js +17 -0
- package/dist/commands/remove/index.js +7 -3
- package/dist/commands/undeploy/index.d.ts +10 -0
- package/dist/commands/undeploy/index.js +75 -0
- package/dist/commands/up/index.js +1 -5
- package/dist/infrastructure/aws.d.ts +6 -2
- package/dist/infrastructure/aws.js +122 -4
- package/dist/infrastructure/common.d.ts +12 -0
- package/dist/infrastructure/index.d.ts +5 -0
- package/dist/infrastructure/index.js +34 -2
- package/dist/infrastructure/local.d.ts +3 -1
- package/dist/infrastructure/local.js +16 -0
- package/dist/lib/config/common.d.ts +6 -0
- package/dist/lib/config/simple.js +22 -6
- package/dist/lib/env/index.d.ts +1 -0
- package/dist/lib/env/index.js +1 -1
- package/dist/lib/env-utils.d.ts +2 -0
- package/dist/lib/env-utils.js +7 -4
- package/dist/lib/filesystem.d.ts +1 -0
- package/dist/lib/filesystem.js +9 -0
- package/dist/lib/package/index.d.ts +3 -0
- package/dist/lib/package/index.js +1 -0
- package/oclif.manifest.json +94 -1
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ $ npm install -g hereya-cli
|
|
|
20
20
|
$ hereya COMMAND
|
|
21
21
|
running command...
|
|
22
22
|
$ hereya (--version)
|
|
23
|
-
hereya-cli/0.
|
|
23
|
+
hereya-cli/0.4.0 linux-x64 node-v20.13.1
|
|
24
24
|
$ hereya --help [COMMAND]
|
|
25
25
|
USAGE
|
|
26
26
|
$ hereya COMMAND
|
|
@@ -31,6 +31,7 @@ USAGE
|
|
|
31
31
|
<!-- commands -->
|
|
32
32
|
* [`hereya add PACKAGE`](#hereya-add-package)
|
|
33
33
|
* [`hereya bootstrap INFRASTRUCTURETYPE`](#hereya-bootstrap-infrastructuretype)
|
|
34
|
+
* [`hereya deploy`](#hereya-deploy)
|
|
34
35
|
* [`hereya down`](#hereya-down)
|
|
35
36
|
* [`hereya env`](#hereya-env)
|
|
36
37
|
* [`hereya help [COMMAND]`](#hereya-help-command)
|
|
@@ -38,6 +39,7 @@ USAGE
|
|
|
38
39
|
* [`hereya remote exec [PKGPATH]`](#hereya-remote-exec-pkgpath)
|
|
39
40
|
* [`hereya remove PACKAGE`](#hereya-remove-package)
|
|
40
41
|
* [`hereya run CMD`](#hereya-run-cmd)
|
|
42
|
+
* [`hereya undeploy`](#hereya-undeploy)
|
|
41
43
|
* [`hereya up`](#hereya-up)
|
|
42
44
|
* [`hereya workspace create NAME`](#hereya-workspace-create-name)
|
|
43
45
|
* [`hereya workspace delete NAME`](#hereya-workspace-delete-name)
|
|
@@ -68,7 +70,7 @@ EXAMPLES
|
|
|
68
70
|
$ hereya add cloudy/docker_postgres
|
|
69
71
|
```
|
|
70
72
|
|
|
71
|
-
_See code: [src/commands/add/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
73
|
+
_See code: [src/commands/add/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/add/index.ts)_
|
|
72
74
|
|
|
73
75
|
## `hereya bootstrap INFRASTRUCTURETYPE`
|
|
74
76
|
|
|
@@ -93,7 +95,28 @@ EXAMPLES
|
|
|
93
95
|
$ hereya bootstrap local
|
|
94
96
|
```
|
|
95
97
|
|
|
96
|
-
_See code: [src/commands/bootstrap/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
98
|
+
_See code: [src/commands/bootstrap/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/bootstrap/index.ts)_
|
|
99
|
+
|
|
100
|
+
## `hereya deploy`
|
|
101
|
+
|
|
102
|
+
Deploy a hereya project using the project deployment package
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
USAGE
|
|
106
|
+
$ hereya deploy [--chdir <value>] [-w <value>]
|
|
107
|
+
|
|
108
|
+
FLAGS
|
|
109
|
+
-w, --workspace=<value> name of the workspace to deploy the packages for
|
|
110
|
+
--chdir=<value> directory to run command in
|
|
111
|
+
|
|
112
|
+
DESCRIPTION
|
|
113
|
+
Deploy a hereya project using the project deployment package
|
|
114
|
+
|
|
115
|
+
EXAMPLES
|
|
116
|
+
$ hereya deploy
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
_See code: [src/commands/deploy/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/deploy/index.ts)_
|
|
97
120
|
|
|
98
121
|
## `hereya down`
|
|
99
122
|
|
|
@@ -114,7 +137,7 @@ EXAMPLES
|
|
|
114
137
|
$ hereya down
|
|
115
138
|
```
|
|
116
139
|
|
|
117
|
-
_See code: [src/commands/down/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
140
|
+
_See code: [src/commands/down/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/down/index.ts)_
|
|
118
141
|
|
|
119
142
|
## `hereya env`
|
|
120
143
|
|
|
@@ -137,7 +160,7 @@ EXAMPLES
|
|
|
137
160
|
$ hereya env -w dev
|
|
138
161
|
```
|
|
139
162
|
|
|
140
|
-
_See code: [src/commands/env/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
163
|
+
_See code: [src/commands/env/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/env/index.ts)_
|
|
141
164
|
|
|
142
165
|
## `hereya help [COMMAND]`
|
|
143
166
|
|
|
@@ -183,7 +206,7 @@ EXAMPLES
|
|
|
183
206
|
$ hereya init myProject -w=defaultWorkspace --chdir=./myProject
|
|
184
207
|
```
|
|
185
208
|
|
|
186
|
-
_See code: [src/commands/init/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
209
|
+
_See code: [src/commands/init/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/init/index.ts)_
|
|
187
210
|
|
|
188
211
|
## `hereya remote exec [PKGPATH]`
|
|
189
212
|
|
|
@@ -191,13 +214,14 @@ remotely provision or destroy a package
|
|
|
191
214
|
|
|
192
215
|
```
|
|
193
216
|
USAGE
|
|
194
|
-
$ hereya remote exec [PKGPATH] [-o <value>]
|
|
217
|
+
$ hereya remote exec [PKGPATH] [-o <value>] [-s <value>]
|
|
195
218
|
|
|
196
219
|
ARGUMENTS
|
|
197
220
|
PKGPATH The path to the package to provision or destroy
|
|
198
221
|
|
|
199
222
|
FLAGS
|
|
200
223
|
-o, --output=<value> The path to store the output env in
|
|
224
|
+
-s, --source=<value> The source of the project to provision or destroy the package for
|
|
201
225
|
|
|
202
226
|
DESCRIPTION
|
|
203
227
|
remotely provision or destroy a package
|
|
@@ -206,7 +230,7 @@ EXAMPLES
|
|
|
206
230
|
$ hereya remote exec
|
|
207
231
|
```
|
|
208
232
|
|
|
209
|
-
_See code: [src/commands/remote/exec/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
233
|
+
_See code: [src/commands/remote/exec/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/remote/exec/index.ts)_
|
|
210
234
|
|
|
211
235
|
## `hereya remove PACKAGE`
|
|
212
236
|
|
|
@@ -229,7 +253,7 @@ EXAMPLES
|
|
|
229
253
|
$ hereya remove cloudy/docker_postgres
|
|
230
254
|
```
|
|
231
255
|
|
|
232
|
-
_See code: [src/commands/remove/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
256
|
+
_See code: [src/commands/remove/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/remove/index.ts)_
|
|
233
257
|
|
|
234
258
|
## `hereya run CMD`
|
|
235
259
|
|
|
@@ -255,7 +279,28 @@ EXAMPLES
|
|
|
255
279
|
$ hereya run -w uat -- node index.js
|
|
256
280
|
```
|
|
257
281
|
|
|
258
|
-
_See code: [src/commands/run/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
282
|
+
_See code: [src/commands/run/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/run/index.ts)_
|
|
283
|
+
|
|
284
|
+
## `hereya undeploy`
|
|
285
|
+
|
|
286
|
+
Undeploy a hereya project by removing all resources.
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
USAGE
|
|
290
|
+
$ hereya undeploy [--chdir <value>] [-w <value>]
|
|
291
|
+
|
|
292
|
+
FLAGS
|
|
293
|
+
-w, --workspace=<value> name of the workspace to undeploy the packages for
|
|
294
|
+
--chdir=<value> directory to run command in
|
|
295
|
+
|
|
296
|
+
DESCRIPTION
|
|
297
|
+
Undeploy a hereya project by removing all resources.
|
|
298
|
+
|
|
299
|
+
EXAMPLES
|
|
300
|
+
$ hereya undeploy
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
_See code: [src/commands/undeploy/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/undeploy/index.ts)_
|
|
259
304
|
|
|
260
305
|
## `hereya up`
|
|
261
306
|
|
|
@@ -276,7 +321,7 @@ EXAMPLES
|
|
|
276
321
|
$ hereya up
|
|
277
322
|
```
|
|
278
323
|
|
|
279
|
-
_See code: [src/commands/up/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
324
|
+
_See code: [src/commands/up/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/up/index.ts)_
|
|
280
325
|
|
|
281
326
|
## `hereya workspace create NAME`
|
|
282
327
|
|
|
@@ -296,7 +341,7 @@ EXAMPLES
|
|
|
296
341
|
$ hereya workspace create dev
|
|
297
342
|
```
|
|
298
343
|
|
|
299
|
-
_See code: [src/commands/workspace/create/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
344
|
+
_See code: [src/commands/workspace/create/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/workspace/create/index.ts)_
|
|
300
345
|
|
|
301
346
|
## `hereya workspace delete NAME`
|
|
302
347
|
|
|
@@ -316,7 +361,7 @@ EXAMPLES
|
|
|
316
361
|
$ hereya workspace delete dev
|
|
317
362
|
```
|
|
318
363
|
|
|
319
|
-
_See code: [src/commands/workspace/delete/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
364
|
+
_See code: [src/commands/workspace/delete/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/workspace/delete/index.ts)_
|
|
320
365
|
|
|
321
366
|
## `hereya workspace env`
|
|
322
367
|
|
|
@@ -336,7 +381,7 @@ EXAMPLES
|
|
|
336
381
|
$ hereya workspace env -w dev
|
|
337
382
|
```
|
|
338
383
|
|
|
339
|
-
_See code: [src/commands/workspace/env/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
384
|
+
_See code: [src/commands/workspace/env/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/workspace/env/index.ts)_
|
|
340
385
|
|
|
341
386
|
## `hereya workspace install PACKAGE`
|
|
342
387
|
|
|
@@ -362,7 +407,7 @@ EXAMPLES
|
|
|
362
407
|
$ hereya workspace install hereya/aws-cognito
|
|
363
408
|
```
|
|
364
409
|
|
|
365
|
-
_See code: [src/commands/workspace/install/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
410
|
+
_See code: [src/commands/workspace/install/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/workspace/install/index.ts)_
|
|
366
411
|
|
|
367
412
|
## `hereya workspace uninstall PACKAGE`
|
|
368
413
|
|
|
@@ -388,5 +433,5 @@ EXAMPLES
|
|
|
388
433
|
$ hereya workspace uninstall hereya/aws-cognito
|
|
389
434
|
```
|
|
390
435
|
|
|
391
|
-
_See code: [src/commands/workspace/uninstall/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
436
|
+
_See code: [src/commands/workspace/uninstall/index.ts](https://github.com/hereya/hereya-cli/blob/v0.4.0/src/commands/workspace/uninstall/index.ts)_
|
|
392
437
|
<!-- commandsstop -->
|
|
@@ -61,6 +61,7 @@ export default class Add extends Command {
|
|
|
61
61
|
package: args.package,
|
|
62
62
|
parameters,
|
|
63
63
|
project: config.project,
|
|
64
|
+
skipDeploy: true,
|
|
64
65
|
workspace: config.workspace,
|
|
65
66
|
});
|
|
66
67
|
if (!provisionOutput.success) {
|
|
@@ -77,6 +78,7 @@ export default class Add extends Command {
|
|
|
77
78
|
workspace: config.workspace,
|
|
78
79
|
});
|
|
79
80
|
await configManager.addPackage({
|
|
81
|
+
deploy: metadata.deploy,
|
|
80
82
|
package: args.package,
|
|
81
83
|
projectRootDir,
|
|
82
84
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Deploy extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
chdir: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
7
|
+
workspace: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getBackend } from '../../backend/index.js';
|
|
4
|
+
import { destroyPackage, provisionPackage } from '../../infrastructure/index.js';
|
|
5
|
+
import { getConfigManager } from '../../lib/config/index.js';
|
|
6
|
+
import { getEnvManager } from '../../lib/env/index.js';
|
|
7
|
+
import { getParameterManager } from '../../lib/parameter/index.js';
|
|
8
|
+
import Up from '../up/index.js';
|
|
9
|
+
export default class Deploy extends Command {
|
|
10
|
+
static description = 'Deploy a hereya project using the project deployment package';
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> <%= command.id %>',
|
|
13
|
+
];
|
|
14
|
+
static flags = {
|
|
15
|
+
chdir: Flags.string({
|
|
16
|
+
description: 'directory to run command in',
|
|
17
|
+
required: false,
|
|
18
|
+
}),
|
|
19
|
+
workspace: Flags.string({
|
|
20
|
+
char: 'w',
|
|
21
|
+
description: 'name of the workspace to deploy the packages for',
|
|
22
|
+
required: false,
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
async run() {
|
|
26
|
+
const { flags } = await this.parse(Deploy);
|
|
27
|
+
const projectRootDir = path.resolve(flags.chdir || process.env.HEREYA_PROJECT_ROOT_DIR || process.cwd());
|
|
28
|
+
const configManager = getConfigManager();
|
|
29
|
+
const loadConfigOutput = await configManager.loadConfig({ projectRootDir });
|
|
30
|
+
if (!loadConfigOutput.found) {
|
|
31
|
+
this.warn(`Project not initialized. Run 'hereya init' first.`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const { config } = loadConfigOutput;
|
|
35
|
+
const deployPackages = Object.keys(config.deploy ?? {});
|
|
36
|
+
const backend = await getBackend();
|
|
37
|
+
const savedStateOutput = await backend.getState({
|
|
38
|
+
project: config.project,
|
|
39
|
+
});
|
|
40
|
+
let savedPackages = [];
|
|
41
|
+
if (savedStateOutput.found) {
|
|
42
|
+
savedPackages = Object.keys(savedStateOutput.config.deploy ?? {});
|
|
43
|
+
}
|
|
44
|
+
const removedPackages = savedPackages.filter((packageName) => !deployPackages.includes(packageName));
|
|
45
|
+
const workspace = flags.workspace || config.workspace;
|
|
46
|
+
const getWorkspaceEnvOutput = await backend.getWorkspaceEnv({
|
|
47
|
+
project: config.project,
|
|
48
|
+
workspace,
|
|
49
|
+
});
|
|
50
|
+
if (!getWorkspaceEnvOutput.success) {
|
|
51
|
+
this.error(getWorkspaceEnvOutput.reason);
|
|
52
|
+
}
|
|
53
|
+
const { env: workspaceEnv } = getWorkspaceEnvOutput;
|
|
54
|
+
const parameterManager = getParameterManager();
|
|
55
|
+
const envManager = getEnvManager();
|
|
56
|
+
const { env: projectEnv } = await envManager.getProjectEnv({
|
|
57
|
+
markSecret: true,
|
|
58
|
+
projectRootDir,
|
|
59
|
+
workspace,
|
|
60
|
+
});
|
|
61
|
+
await Promise.all(removedPackages.map(async (packageName) => {
|
|
62
|
+
const { parameters } = await parameterManager.getPackageParameters({
|
|
63
|
+
package: packageName,
|
|
64
|
+
projectRootDir,
|
|
65
|
+
workspace,
|
|
66
|
+
});
|
|
67
|
+
const destroyOutput = await destroyPackage({
|
|
68
|
+
env: workspaceEnv,
|
|
69
|
+
package: packageName,
|
|
70
|
+
parameters,
|
|
71
|
+
project: config.project,
|
|
72
|
+
projectEnv,
|
|
73
|
+
projectRootDir,
|
|
74
|
+
workspace,
|
|
75
|
+
});
|
|
76
|
+
if (!destroyOutput.success) {
|
|
77
|
+
this.error(destroyOutput.reason);
|
|
78
|
+
}
|
|
79
|
+
this.log(`Package ${packageName} un-deployed successfully`);
|
|
80
|
+
}));
|
|
81
|
+
await Up.run(['--chdir', projectRootDir, '--workspace', workspace]);
|
|
82
|
+
const { env: newProjectEnv } = await envManager.getProjectEnv({
|
|
83
|
+
markSecret: true,
|
|
84
|
+
projectRootDir,
|
|
85
|
+
workspace,
|
|
86
|
+
});
|
|
87
|
+
await Promise.all(deployPackages.map(async (packageName) => {
|
|
88
|
+
const { parameters } = await parameterManager.getPackageParameters({
|
|
89
|
+
package: packageName,
|
|
90
|
+
projectRootDir,
|
|
91
|
+
workspace,
|
|
92
|
+
});
|
|
93
|
+
const provisionOutput = await provisionPackage({
|
|
94
|
+
env: workspaceEnv,
|
|
95
|
+
package: packageName,
|
|
96
|
+
parameters,
|
|
97
|
+
project: config.project,
|
|
98
|
+
projectEnv: newProjectEnv,
|
|
99
|
+
projectRootDir,
|
|
100
|
+
workspace,
|
|
101
|
+
});
|
|
102
|
+
if (!provisionOutput.success) {
|
|
103
|
+
this.error(provisionOutput.reason);
|
|
104
|
+
}
|
|
105
|
+
this.log(`Package ${packageName} deployed successfully`);
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -7,6 +7,7 @@ export default class RemoteExec extends Command {
|
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
9
|
output: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
10
|
+
source: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
10
11
|
};
|
|
11
12
|
run(): Promise<void>;
|
|
12
13
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
import { getIac } from '../../../iac/index.js';
|
|
3
4
|
import { getInfrastructure } from '../../../infrastructure/index.js';
|
|
4
5
|
import { save } from '../../../lib/yaml-utils.js';
|
|
@@ -19,6 +20,11 @@ export default class RemoteExec extends Command {
|
|
|
19
20
|
description: 'The path to store the output env in',
|
|
20
21
|
required: false,
|
|
21
22
|
}),
|
|
23
|
+
source: Flags.string({
|
|
24
|
+
char: 's',
|
|
25
|
+
description: 'The source of the project to provision or destroy the package for',
|
|
26
|
+
required: false,
|
|
27
|
+
}),
|
|
22
28
|
};
|
|
23
29
|
async run() {
|
|
24
30
|
const { args, flags } = await this.parse(RemoteExec);
|
|
@@ -28,6 +34,11 @@ export default class RemoteExec extends Command {
|
|
|
28
34
|
const iacType = process.env.HEREYA_IAC_TYPE;
|
|
29
35
|
const destroy = process.env.HEREYA_DESTROY === 'true';
|
|
30
36
|
const infraType = process.env.HEREYA_INFRA_TYPE;
|
|
37
|
+
const deploy = process.env.HEREYA_DEPLOY === 'true';
|
|
38
|
+
const source = flags.source ? path.resolve(flags.source) : '';
|
|
39
|
+
if (deploy && !source) {
|
|
40
|
+
return this.error('Deploy packages provisioning requires a source path');
|
|
41
|
+
}
|
|
31
42
|
if (!id || !iacType || !infraType) {
|
|
32
43
|
return this.error(`
|
|
33
44
|
Missing required environment variables:
|
|
@@ -39,6 +50,12 @@ export default class RemoteExec extends Command {
|
|
|
39
50
|
const input = {
|
|
40
51
|
env: workspaceEnv, id, parameters, pkgPath: args.pkgPath || process.cwd(),
|
|
41
52
|
};
|
|
53
|
+
if (deploy) {
|
|
54
|
+
input.parameters = {
|
|
55
|
+
...input.parameters,
|
|
56
|
+
hereyaProjectRootDir: source,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
42
59
|
const iac$ = getIac({ type: iacType });
|
|
43
60
|
if (!iac$.supported) {
|
|
44
61
|
return this.error(iac$.reason);
|
|
@@ -31,7 +31,7 @@ export default class Remove extends Command {
|
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
33
|
const { config } = loadConfigOutput;
|
|
34
|
-
if (!(args.package in (config.packages ?? {}))) {
|
|
34
|
+
if (!(args.package in (config.packages ?? {})) && !(args.package in (config.deploy ?? {}))) {
|
|
35
35
|
this.warn(`Package ${args.package} not found in project.`);
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
@@ -55,13 +55,16 @@ export default class Remove extends Command {
|
|
|
55
55
|
package: args.package,
|
|
56
56
|
parameters,
|
|
57
57
|
project: config.project,
|
|
58
|
+
skipDeploy: true,
|
|
58
59
|
workspace: config.workspace,
|
|
59
60
|
});
|
|
60
61
|
if (!destroyOutput.success) {
|
|
61
62
|
this.error(destroyOutput.reason);
|
|
62
63
|
}
|
|
63
64
|
const { env, metadata } = destroyOutput;
|
|
64
|
-
|
|
65
|
+
if (!metadata.deploy) {
|
|
66
|
+
this.log(`Infrastructure resources for ${args.package} have been destroyed`);
|
|
67
|
+
}
|
|
65
68
|
this.log('removing package env vars from project');
|
|
66
69
|
const envManager = getEnvManager();
|
|
67
70
|
await envManager.removeProjectEnv({
|
|
@@ -71,8 +74,9 @@ export default class Remove extends Command {
|
|
|
71
74
|
workspace: config.workspace
|
|
72
75
|
});
|
|
73
76
|
await configManager.removePackage({
|
|
77
|
+
deploy: metadata.deploy,
|
|
74
78
|
package: args.package,
|
|
75
|
-
projectRootDir
|
|
79
|
+
projectRootDir,
|
|
76
80
|
});
|
|
77
81
|
const { config: newConfig } = await configManager.loadConfig({ projectRootDir });
|
|
78
82
|
await backend.saveState(newConfig);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Undeploy extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
chdir: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
7
|
+
workspace: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getBackend } from '../../backend/index.js';
|
|
4
|
+
import { destroyPackage } from '../../infrastructure/index.js';
|
|
5
|
+
import { getConfigManager } from '../../lib/config/index.js';
|
|
6
|
+
import { getEnvManager } from '../../lib/env/index.js';
|
|
7
|
+
import { getParameterManager } from '../../lib/parameter/index.js';
|
|
8
|
+
import Down from '../down/index.js';
|
|
9
|
+
export default class Undeploy extends Command {
|
|
10
|
+
static description = 'Undeploy a hereya project by removing all resources.';
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> <%= command.id %>',
|
|
13
|
+
];
|
|
14
|
+
static flags = {
|
|
15
|
+
chdir: Flags.string({
|
|
16
|
+
description: 'directory to run command in',
|
|
17
|
+
required: false,
|
|
18
|
+
}),
|
|
19
|
+
workspace: Flags.string({
|
|
20
|
+
char: 'w',
|
|
21
|
+
description: 'name of the workspace to undeploy the packages for',
|
|
22
|
+
required: false,
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
async run() {
|
|
26
|
+
const { flags } = await this.parse(Undeploy);
|
|
27
|
+
const projectRootDir = path.resolve(flags.chdir || process.env.HEREYA_PROJECT_ROOT_DIR || process.cwd());
|
|
28
|
+
const configManager = getConfigManager();
|
|
29
|
+
const loadConfigOutput = await configManager.loadConfig({ projectRootDir });
|
|
30
|
+
if (!loadConfigOutput.found) {
|
|
31
|
+
this.warn(`Project not initialized. Run 'hereya init' first.`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const { config } = loadConfigOutput;
|
|
35
|
+
const deployPackages = Object.keys(config.deploy ?? {});
|
|
36
|
+
const workspace = flags.workspace || config.workspace;
|
|
37
|
+
const backend = await getBackend();
|
|
38
|
+
const getWorkspaceEnvOutput = await backend.getWorkspaceEnv({
|
|
39
|
+
project: config.project,
|
|
40
|
+
workspace,
|
|
41
|
+
});
|
|
42
|
+
if (!getWorkspaceEnvOutput.success) {
|
|
43
|
+
this.error(getWorkspaceEnvOutput.reason);
|
|
44
|
+
}
|
|
45
|
+
const { env: workspaceEnv } = getWorkspaceEnvOutput;
|
|
46
|
+
const parameterManager = getParameterManager();
|
|
47
|
+
const envManager = getEnvManager();
|
|
48
|
+
const { env: projectEnv } = await envManager.getProjectEnv({
|
|
49
|
+
markSecret: true,
|
|
50
|
+
projectRootDir,
|
|
51
|
+
workspace,
|
|
52
|
+
});
|
|
53
|
+
await Promise.all(deployPackages.map(async (packageName) => {
|
|
54
|
+
const { parameters } = await parameterManager.getPackageParameters({
|
|
55
|
+
package: packageName,
|
|
56
|
+
projectRootDir,
|
|
57
|
+
workspace,
|
|
58
|
+
});
|
|
59
|
+
const destroyOutput = await destroyPackage({
|
|
60
|
+
env: workspaceEnv,
|
|
61
|
+
package: packageName,
|
|
62
|
+
parameters,
|
|
63
|
+
project: config.project,
|
|
64
|
+
projectEnv,
|
|
65
|
+
projectRootDir,
|
|
66
|
+
workspace,
|
|
67
|
+
});
|
|
68
|
+
if (!destroyOutput.success) {
|
|
69
|
+
this.error(destroyOutput.reason);
|
|
70
|
+
}
|
|
71
|
+
this.log(`Package ${packageName} un-deployed successfully`);
|
|
72
|
+
}));
|
|
73
|
+
await Down.run(['--chdir', projectRootDir, '--workspace', workspace]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -91,7 +91,7 @@ export default class Up extends Command {
|
|
|
91
91
|
return { env, metadata, packageName };
|
|
92
92
|
}));
|
|
93
93
|
const envManager = getEnvManager();
|
|
94
|
-
for (const { env, metadata
|
|
94
|
+
for (const { env, metadata } of removed) {
|
|
95
95
|
// eslint-disable-next-line no-await-in-loop
|
|
96
96
|
await Promise.all([
|
|
97
97
|
envManager.removeProjectEnv({
|
|
@@ -100,10 +100,6 @@ export default class Up extends Command {
|
|
|
100
100
|
projectRootDir,
|
|
101
101
|
workspace,
|
|
102
102
|
}),
|
|
103
|
-
configManager.removePackage({
|
|
104
|
-
package: packageName,
|
|
105
|
-
projectRootDir,
|
|
106
|
-
})
|
|
107
103
|
]);
|
|
108
104
|
}
|
|
109
105
|
for (const { env, metadata } of added) {
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import { BootstrapInput, DestroyInput, DestroyOutput, Infrastructure, ProvisionInput, ProvisionOutput, ResolveEnvInput, ResolveEnvOutput, SaveEnvInput, SaveEnvOutput } from './common.js';
|
|
1
|
+
import { BootstrapInput, DeployInput, DeployOutput, DestroyInput, DestroyOutput, Infrastructure, ProvisionInput, ProvisionOutput, ResolveEnvInput, ResolveEnvOutput, SaveEnvInput, SaveEnvOutput, UndeployInput, UndeployOutput } from './common.js';
|
|
2
2
|
export declare class AwsInfrastructure implements Infrastructure {
|
|
3
3
|
bootstrap(_: BootstrapInput): Promise<void>;
|
|
4
|
+
deploy(input: DeployInput): Promise<DeployOutput>;
|
|
4
5
|
destroy(input: DestroyInput): Promise<DestroyOutput>;
|
|
5
6
|
provision(input: ProvisionInput): Promise<ProvisionOutput>;
|
|
6
7
|
resolveEnv(input: ResolveEnvInput): Promise<ResolveEnvOutput>;
|
|
7
8
|
saveEnv(input: SaveEnvInput): Promise<SaveEnvOutput>;
|
|
9
|
+
undeploy(input: UndeployInput): Promise<UndeployOutput>;
|
|
8
10
|
private getEnv;
|
|
11
|
+
private getFilesToUpload;
|
|
9
12
|
private removeEnv;
|
|
10
|
-
private
|
|
13
|
+
private runCodeBuild;
|
|
14
|
+
private uploadProjectFiles;
|
|
11
15
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { BatchGetBuildsCommand, CodeBuildClient, StartBuildCommand } from '@aws-sdk/client-codebuild';
|
|
2
|
+
import { DeleteObjectsCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
2
3
|
import { DeleteParameterCommand, GetParameterCommand, PutParameterCommand, SSMClient } from '@aws-sdk/client-ssm';
|
|
3
4
|
import { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';
|
|
5
|
+
import { glob } from 'glob';
|
|
6
|
+
import ignore from 'ignore';
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
import fs from 'node:fs/promises';
|
|
9
|
+
import path from 'node:path';
|
|
4
10
|
import { IacType } from '../iac/common.js';
|
|
11
|
+
import { fileExists, getAnyPath } from '../lib/filesystem.js';
|
|
5
12
|
import { runShell } from '../lib/shell.js';
|
|
6
13
|
import { InfrastructureType } from './common.js';
|
|
7
14
|
import { provisionPackage } from './index.js';
|
|
@@ -17,9 +24,42 @@ export class AwsInfrastructure {
|
|
|
17
24
|
throw new Error(output.reason);
|
|
18
25
|
}
|
|
19
26
|
}
|
|
27
|
+
async deploy(input) {
|
|
28
|
+
let files = [];
|
|
29
|
+
let s3Bucket = '';
|
|
30
|
+
let s3Client = new S3Client({});
|
|
31
|
+
let s3Key = '';
|
|
32
|
+
try {
|
|
33
|
+
({ files, s3Bucket, s3Client, s3Key } = await this.uploadProjectFiles(input));
|
|
34
|
+
input.parameters = {
|
|
35
|
+
...input.parameters,
|
|
36
|
+
hereyaProjectEnv: JSON.stringify(input.projectEnv ?? {}),
|
|
37
|
+
};
|
|
38
|
+
const output = await this.runCodeBuild({
|
|
39
|
+
...input,
|
|
40
|
+
deploy: true,
|
|
41
|
+
sourceS3Key: s3Key
|
|
42
|
+
});
|
|
43
|
+
if (!output.success) {
|
|
44
|
+
return output;
|
|
45
|
+
}
|
|
46
|
+
const env = await this.getEnv(input.id);
|
|
47
|
+
return { env, success: true };
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
if (s3Key && files.length > 0) {
|
|
51
|
+
await s3Client.send(new DeleteObjectsCommand({
|
|
52
|
+
Bucket: s3Bucket,
|
|
53
|
+
Delete: {
|
|
54
|
+
Objects: files.map(file => ({ Key: `${s3Key}/${file}` }))
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
20
60
|
async destroy(input) {
|
|
21
61
|
const env = await this.getEnv(input.id);
|
|
22
|
-
const output = await this.
|
|
62
|
+
const output = await this.runCodeBuild({ ...input, destroy: true });
|
|
23
63
|
if (!output.success) {
|
|
24
64
|
return output;
|
|
25
65
|
}
|
|
@@ -27,7 +67,7 @@ export class AwsInfrastructure {
|
|
|
27
67
|
return { env, success: true };
|
|
28
68
|
}
|
|
29
69
|
async provision(input) {
|
|
30
|
-
const output = await this.
|
|
70
|
+
const output = await this.runCodeBuild(input);
|
|
31
71
|
if (!output.success) {
|
|
32
72
|
return output;
|
|
33
73
|
}
|
|
@@ -42,7 +82,10 @@ export class AwsInfrastructure {
|
|
|
42
82
|
Name: input.value,
|
|
43
83
|
WithDecryption: true,
|
|
44
84
|
}));
|
|
45
|
-
return {
|
|
85
|
+
return {
|
|
86
|
+
isSecret: response.Parameter?.Type === 'SecureString',
|
|
87
|
+
value: response.Parameter?.Value ?? input.value
|
|
88
|
+
};
|
|
46
89
|
}
|
|
47
90
|
return { value: input.value };
|
|
48
91
|
}
|
|
@@ -63,6 +106,46 @@ export class AwsInfrastructure {
|
|
|
63
106
|
return { reason: error.message, success: false };
|
|
64
107
|
}
|
|
65
108
|
}
|
|
109
|
+
async undeploy(input) {
|
|
110
|
+
let files = [];
|
|
111
|
+
let s3Bucket = '';
|
|
112
|
+
let s3Client = new S3Client({});
|
|
113
|
+
let s3Key = '';
|
|
114
|
+
let env = {};
|
|
115
|
+
try {
|
|
116
|
+
env = await this.getEnv(input.id);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.log(`Could not get env for ${input.id}: ${error.message}. Continuing with undeployment...`);
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
({ files, s3Bucket, s3Client, s3Key } = await this.uploadProjectFiles(input));
|
|
123
|
+
input.parameters = {
|
|
124
|
+
...input.parameters,
|
|
125
|
+
hereyaProjectEnv: JSON.stringify(input.projectEnv ?? {}),
|
|
126
|
+
};
|
|
127
|
+
const output = await this.runCodeBuild({
|
|
128
|
+
...input,
|
|
129
|
+
deploy: true,
|
|
130
|
+
destroy: true,
|
|
131
|
+
sourceS3Key: s3Key
|
|
132
|
+
});
|
|
133
|
+
if (!output.success) {
|
|
134
|
+
return output;
|
|
135
|
+
}
|
|
136
|
+
return { env, success: true };
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
if (s3Key && files.length > 0) {
|
|
140
|
+
await s3Client.send(new DeleteObjectsCommand({
|
|
141
|
+
Bucket: s3Bucket,
|
|
142
|
+
Delete: {
|
|
143
|
+
Objects: files.map(file => ({ Key: `${s3Key}/${file}` }))
|
|
144
|
+
}
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
66
149
|
async getEnv(id) {
|
|
67
150
|
const ssmClient = new SSMClient({});
|
|
68
151
|
const ssmParameterName = `/hereya/${id}`;
|
|
@@ -71,6 +154,16 @@ export class AwsInfrastructure {
|
|
|
71
154
|
}));
|
|
72
155
|
return JSON.parse(ssmParameter.Parameter?.Value ?? '{}');
|
|
73
156
|
}
|
|
157
|
+
async getFilesToUpload(rootDir) {
|
|
158
|
+
const ig = ignore.default();
|
|
159
|
+
const ignoreFilePath = await getAnyPath(`${rootDir}/.hereyaignore`, `${rootDir}/.gitignore`);
|
|
160
|
+
if (await fileExists(ignoreFilePath)) {
|
|
161
|
+
const ignoreFileContent = await fs.readFile(ignoreFilePath, 'utf8');
|
|
162
|
+
ig.add(ignoreFileContent);
|
|
163
|
+
}
|
|
164
|
+
const files = glob.sync('**/*', { cwd: rootDir, nodir: true });
|
|
165
|
+
return files.filter(file => !ig.ignores(file));
|
|
166
|
+
}
|
|
74
167
|
async removeEnv(id) {
|
|
75
168
|
const ssmClient = new SSMClient({});
|
|
76
169
|
const ssmParameterName = `/hereya/${id}`;
|
|
@@ -78,7 +171,7 @@ export class AwsInfrastructure {
|
|
|
78
171
|
Name: ssmParameterName,
|
|
79
172
|
}));
|
|
80
173
|
}
|
|
81
|
-
async
|
|
174
|
+
async runCodeBuild(input) {
|
|
82
175
|
const codebuildClient = new CodeBuildClient({});
|
|
83
176
|
let codebuildProjectName = '';
|
|
84
177
|
switch (input.iacType) {
|
|
@@ -127,6 +220,16 @@ export class AwsInfrastructure {
|
|
|
127
220
|
type: 'PLAINTEXT',
|
|
128
221
|
value: input.destroy ? 'true' : '',
|
|
129
222
|
},
|
|
223
|
+
{
|
|
224
|
+
name: 'HEREYA_DEPLOY',
|
|
225
|
+
type: 'PLAINTEXT',
|
|
226
|
+
value: input.deploy ? 'true' : '',
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
name: 'HEREYA_PROJECT_S3_KEY',
|
|
230
|
+
type: 'PLAINTEXT',
|
|
231
|
+
value: input.deploy ? input.sourceS3Key : '',
|
|
232
|
+
}
|
|
130
233
|
],
|
|
131
234
|
projectName: codebuildProjectName,
|
|
132
235
|
}));
|
|
@@ -152,4 +255,19 @@ export class AwsInfrastructure {
|
|
|
152
255
|
}
|
|
153
256
|
return { success: true };
|
|
154
257
|
}
|
|
258
|
+
async uploadProjectFiles(input) {
|
|
259
|
+
const s3Key = `${input.id}/${randomUUID()}`;
|
|
260
|
+
const s3Bucket = 'hereya-projects-source-code';
|
|
261
|
+
const files = await this.getFilesToUpload(input.projectRootDir);
|
|
262
|
+
const s3Client = new S3Client({});
|
|
263
|
+
await Promise.all(files.map(async (file) => {
|
|
264
|
+
console.log(`Uploading ${file} to s3://${s3Bucket}/${s3Key}`);
|
|
265
|
+
await s3Client.send(new PutObjectCommand({
|
|
266
|
+
Body: await fs.readFile(path.join(input.projectRootDir, file)),
|
|
267
|
+
Bucket: s3Bucket,
|
|
268
|
+
Key: `${s3Key}/${file}`,
|
|
269
|
+
}));
|
|
270
|
+
}));
|
|
271
|
+
return { files, s3Bucket, s3Client, s3Key };
|
|
272
|
+
}
|
|
155
273
|
}
|
|
@@ -7,10 +7,12 @@ export declare enum InfrastructureType {
|
|
|
7
7
|
}
|
|
8
8
|
export interface Infrastructure {
|
|
9
9
|
bootstrap(input: BootstrapInput): Promise<void>;
|
|
10
|
+
deploy(input: DeployInput): Promise<DeployOutput>;
|
|
10
11
|
destroy(input: DestroyInput): Promise<DestroyOutput>;
|
|
11
12
|
provision(input: ProvisionInput): Promise<ProvisionOutput>;
|
|
12
13
|
resolveEnv(input: ResolveEnvInput): Promise<ResolveEnvOutput>;
|
|
13
14
|
saveEnv(input: SaveEnvInput): Promise<SaveEnvOutput>;
|
|
15
|
+
undeploy(input: UndeployInput): Promise<UndeployOutput>;
|
|
14
16
|
}
|
|
15
17
|
export type BootstrapInput = {
|
|
16
18
|
force?: boolean;
|
|
@@ -39,10 +41,20 @@ export type ProvisionOutput = {
|
|
|
39
41
|
};
|
|
40
42
|
export type DestroyInput = ProvisionInput;
|
|
41
43
|
export type DestroyOutput = ProvisionOutput;
|
|
44
|
+
export type DeployInput = {
|
|
45
|
+
projectEnv: {
|
|
46
|
+
[key: string]: string;
|
|
47
|
+
};
|
|
48
|
+
projectRootDir: string;
|
|
49
|
+
} & ProvisionInput;
|
|
50
|
+
export type DeployOutput = ProvisionOutput;
|
|
51
|
+
export type UndeployInput = DeployInput;
|
|
52
|
+
export type UndeployOutput = DeployOutput;
|
|
42
53
|
export type ResolveEnvInput = {
|
|
43
54
|
value: string;
|
|
44
55
|
};
|
|
45
56
|
export type ResolveEnvOutput = {
|
|
57
|
+
isSecret?: boolean;
|
|
46
58
|
value: string;
|
|
47
59
|
};
|
|
48
60
|
export type SaveEnvInput = {
|
|
@@ -19,6 +19,11 @@ export type ProvisionPackageInput = {
|
|
|
19
19
|
[key: string]: string;
|
|
20
20
|
};
|
|
21
21
|
project?: string;
|
|
22
|
+
projectEnv?: {
|
|
23
|
+
[key: string]: string;
|
|
24
|
+
};
|
|
25
|
+
projectRootDir?: string;
|
|
26
|
+
skipDeploy?: boolean;
|
|
22
27
|
workspace?: string;
|
|
23
28
|
};
|
|
24
29
|
export type ProvisionPackageOutput = {
|
|
@@ -36,8 +36,24 @@ export async function destroyPackage(input) {
|
|
|
36
36
|
if (!infrastructure$.supported) {
|
|
37
37
|
return { reason: infrastructure$.reason, success: false };
|
|
38
38
|
}
|
|
39
|
+
if (metadata.deploy && input.skipDeploy) {
|
|
40
|
+
console.log(`Skipping un-deployment of ${input.package}...`);
|
|
41
|
+
return { env: {}, metadata, success: true };
|
|
42
|
+
}
|
|
39
43
|
const { infrastructure } = infrastructure$;
|
|
40
|
-
const destroyOutput = await infrastructure.
|
|
44
|
+
const destroyOutput = metadata.deploy ? await infrastructure.undeploy({
|
|
45
|
+
canonicalName,
|
|
46
|
+
env: input.env,
|
|
47
|
+
iacType: metadata.iac,
|
|
48
|
+
id: ((input.project || input.workspace) ? [input.project, input.workspace, canonicalName] : [canonicalName])
|
|
49
|
+
.filter(Boolean).join('')
|
|
50
|
+
.replaceAll(/[^\dA-Za-z]/g, ''),
|
|
51
|
+
parameters: input.parameters,
|
|
52
|
+
pkgName: input.package,
|
|
53
|
+
pkgUrl: packageUri,
|
|
54
|
+
projectEnv: input.projectEnv ?? {},
|
|
55
|
+
projectRootDir: input.projectRootDir,
|
|
56
|
+
}) : await infrastructure.destroy({
|
|
41
57
|
canonicalName,
|
|
42
58
|
env: input.env,
|
|
43
59
|
iacType: metadata.iac,
|
|
@@ -63,8 +79,24 @@ export async function provisionPackage(input) {
|
|
|
63
79
|
if (!infrastructure$.supported) {
|
|
64
80
|
return { reason: infrastructure$.reason, success: false };
|
|
65
81
|
}
|
|
82
|
+
if (metadata.deploy && input.skipDeploy) {
|
|
83
|
+
console.log(`Skipping deployment of ${input.package}...`);
|
|
84
|
+
return { env: {}, metadata, success: true };
|
|
85
|
+
}
|
|
66
86
|
const { infrastructure } = infrastructure$;
|
|
67
|
-
const provisionOutput = await infrastructure.
|
|
87
|
+
const provisionOutput = metadata.deploy ? await infrastructure.deploy({
|
|
88
|
+
canonicalName,
|
|
89
|
+
env: input.env,
|
|
90
|
+
iacType: metadata.iac,
|
|
91
|
+
id: ((input.project || input.workspace) ? [input.project, input.workspace, canonicalName] : [canonicalName])
|
|
92
|
+
.filter(Boolean).join('')
|
|
93
|
+
.replaceAll(/[^\dA-Za-z]/g, ''),
|
|
94
|
+
parameters: input.parameters,
|
|
95
|
+
pkgName: input.package,
|
|
96
|
+
pkgUrl: packageUri,
|
|
97
|
+
projectEnv: input.projectEnv ?? {},
|
|
98
|
+
projectRootDir: input.projectRootDir,
|
|
99
|
+
}) : await infrastructure.provision({
|
|
68
100
|
canonicalName,
|
|
69
101
|
env: input.env,
|
|
70
102
|
iacType: metadata.iac,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Infrastructure, ProvisionInput, ProvisionOutput, SaveEnvInput, SaveEnvOutput } from './common.js';
|
|
1
|
+
import { DeployInput, DeployOutput, Infrastructure, ProvisionInput, ProvisionOutput, SaveEnvInput, SaveEnvOutput, UndeployInput, UndeployOutput } from './common.js';
|
|
2
2
|
export declare class LocalInfrastructure implements Infrastructure {
|
|
3
3
|
bootstrap(): Promise<void>;
|
|
4
|
+
deploy(input: DeployInput): Promise<DeployOutput>;
|
|
4
5
|
destroy(input: ProvisionInput): Promise<ProvisionOutput>;
|
|
5
6
|
provision(input: ProvisionInput): Promise<ProvisionOutput>;
|
|
6
7
|
resolveEnv(input: {
|
|
@@ -9,6 +10,7 @@ export declare class LocalInfrastructure implements Infrastructure {
|
|
|
9
10
|
value: string;
|
|
10
11
|
}>;
|
|
11
12
|
saveEnv(input: SaveEnvInput): Promise<SaveEnvOutput>;
|
|
13
|
+
undeploy(input: UndeployInput): Promise<UndeployOutput>;
|
|
12
14
|
private download;
|
|
13
15
|
private isNotEmpty;
|
|
14
16
|
}
|
|
@@ -7,6 +7,14 @@ export class LocalInfrastructure {
|
|
|
7
7
|
async bootstrap() {
|
|
8
8
|
console.log('Bootstrapping local infrastructure');
|
|
9
9
|
}
|
|
10
|
+
async deploy(input) {
|
|
11
|
+
input.parameters = {
|
|
12
|
+
...input.parameters,
|
|
13
|
+
hereyaProjectEnv: JSON.stringify(input.projectEnv ?? {}),
|
|
14
|
+
hereyaProjectRootDir: input.projectRootDir
|
|
15
|
+
};
|
|
16
|
+
return this.provision(input);
|
|
17
|
+
}
|
|
10
18
|
async destroy(input) {
|
|
11
19
|
// noinspection DuplicatedCode
|
|
12
20
|
const destPath = path.join(os.homedir(), '.hereya', input.id, input.canonicalName);
|
|
@@ -56,6 +64,14 @@ export class LocalInfrastructure {
|
|
|
56
64
|
console.log(`Saving env to ${input.id}`);
|
|
57
65
|
return { success: true };
|
|
58
66
|
}
|
|
67
|
+
async undeploy(input) {
|
|
68
|
+
input.parameters = {
|
|
69
|
+
...input.parameters,
|
|
70
|
+
hereyaProjectEnv: JSON.stringify(input.projectEnv ?? {}),
|
|
71
|
+
hereyaProjectRootDir: input.projectRootDir
|
|
72
|
+
};
|
|
73
|
+
return this.destroy(input);
|
|
74
|
+
}
|
|
59
75
|
async download(pkgUrl, destPath) {
|
|
60
76
|
if (await this.isNotEmpty(destPath)) {
|
|
61
77
|
console.log(`Package already downloaded at ${destPath}`);
|
|
@@ -15,6 +15,11 @@ export type LoadConfigOutput = {
|
|
|
15
15
|
found: true;
|
|
16
16
|
};
|
|
17
17
|
export interface Config {
|
|
18
|
+
deploy?: {
|
|
19
|
+
[key: string]: {
|
|
20
|
+
version: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
18
23
|
packages?: {
|
|
19
24
|
[key: string]: {
|
|
20
25
|
version: string;
|
|
@@ -28,6 +33,7 @@ export interface SaveConfigInput {
|
|
|
28
33
|
projectRootDir?: string;
|
|
29
34
|
}
|
|
30
35
|
export type AddPackageInput = {
|
|
36
|
+
deploy?: boolean;
|
|
31
37
|
package: string;
|
|
32
38
|
projectRootDir?: string;
|
|
33
39
|
};
|
|
@@ -6,12 +6,21 @@ export class SimpleConfigManager {
|
|
|
6
6
|
const { config } = await this.loadConfig({ projectRootDir: input.projectRootDir });
|
|
7
7
|
await yaml.save({
|
|
8
8
|
...config,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
...(input.deploy ? {
|
|
10
|
+
deploy: {
|
|
11
|
+
...config.deploy,
|
|
12
|
+
[input.package]: {
|
|
13
|
+
version: '',
|
|
14
|
+
}
|
|
13
15
|
}
|
|
14
|
-
}
|
|
16
|
+
} : {
|
|
17
|
+
packages: {
|
|
18
|
+
...config.packages,
|
|
19
|
+
[input.package]: {
|
|
20
|
+
version: '',
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}),
|
|
15
24
|
}, await this.getConfigPath(input.projectRootDir));
|
|
16
25
|
}
|
|
17
26
|
async loadConfig(input) {
|
|
@@ -25,9 +34,16 @@ export class SimpleConfigManager {
|
|
|
25
34
|
async removePackage(input) {
|
|
26
35
|
const { config } = await this.loadConfig({ projectRootDir: input.projectRootDir });
|
|
27
36
|
const newPackages = { ...config.packages };
|
|
28
|
-
|
|
37
|
+
const newDeploy = { ...config.deploy };
|
|
38
|
+
if (input.deploy) {
|
|
39
|
+
delete newDeploy[input.package];
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
delete newPackages[input.package];
|
|
43
|
+
}
|
|
29
44
|
await yaml.save({
|
|
30
45
|
...config,
|
|
46
|
+
deploy: newDeploy,
|
|
31
47
|
packages: newPackages,
|
|
32
48
|
}, await this.getConfigPath(input.projectRootDir));
|
|
33
49
|
}
|
package/dist/lib/env/index.d.ts
CHANGED
package/dist/lib/env/index.js
CHANGED
|
@@ -17,7 +17,7 @@ export class EnvManager {
|
|
|
17
17
|
if (!found) {
|
|
18
18
|
return { env: {} };
|
|
19
19
|
}
|
|
20
|
-
const resolvedEnv = await resolveEnvValues(env);
|
|
20
|
+
const resolvedEnv = await resolveEnvValues(env, { markSecret: input.markSecret });
|
|
21
21
|
return { env: resolvedEnv };
|
|
22
22
|
}
|
|
23
23
|
async removeProjectEnv(input) {
|
package/dist/lib/env-utils.d.ts
CHANGED
package/dist/lib/env-utils.js
CHANGED
|
@@ -4,17 +4,20 @@ export function logEnv(env, logFn = console.log) {
|
|
|
4
4
|
logFn(`${key}=${value}`);
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
|
-
export async function resolveEnvValues(env) {
|
|
7
|
+
export async function resolveEnvValues(env, options = {}) {
|
|
8
8
|
return Object.fromEntries(await Promise.all(Object.entries(env)
|
|
9
9
|
.map(async ([key, value]) => {
|
|
10
10
|
const infraType = value.split(':')[0];
|
|
11
|
-
const infra$ =
|
|
11
|
+
const infra$ = getInfrastructure({ type: infraType });
|
|
12
12
|
if (!infra$.supported) {
|
|
13
13
|
throw new Error(infra$.reason);
|
|
14
14
|
}
|
|
15
15
|
const { infrastructure } = infra$;
|
|
16
16
|
const valueWithoutInfra = value.split(':').slice(1).join(':');
|
|
17
|
-
const { value: resolvedValue } = await infrastructure.resolveEnv({
|
|
18
|
-
|
|
17
|
+
const { isSecret, value: resolvedValue } = await infrastructure.resolveEnv({
|
|
18
|
+
value: valueWithoutInfra
|
|
19
|
+
});
|
|
20
|
+
const finalValue = options.markSecret && isSecret ? `secret://${resolvedValue}` : resolvedValue;
|
|
21
|
+
return [key, finalValue];
|
|
19
22
|
})));
|
|
20
23
|
}
|
package/dist/lib/filesystem.d.ts
CHANGED
package/dist/lib/filesystem.js
CHANGED
|
@@ -19,12 +19,15 @@ export type ResolvePackageOutput = {
|
|
|
19
19
|
reason: string;
|
|
20
20
|
};
|
|
21
21
|
export declare const PackageMetadata: z.ZodObject<{
|
|
22
|
+
deploy: z.ZodOptional<z.ZodBoolean>;
|
|
22
23
|
iac: z.ZodNativeEnum<typeof IacType>;
|
|
23
24
|
infra: z.ZodNativeEnum<typeof InfrastructureType>;
|
|
24
25
|
}, "strip", z.ZodTypeAny, {
|
|
25
26
|
iac: IacType;
|
|
26
27
|
infra: InfrastructureType;
|
|
28
|
+
deploy?: boolean | undefined;
|
|
27
29
|
}, {
|
|
28
30
|
iac: IacType;
|
|
29
31
|
infra: InfrastructureType;
|
|
32
|
+
deploy?: boolean | undefined;
|
|
30
33
|
}>;
|
|
@@ -40,6 +40,7 @@ export function getPackageCanonicalName(packageName) {
|
|
|
40
40
|
return packageName.replace('/', '-');
|
|
41
41
|
}
|
|
42
42
|
export const PackageMetadata = z.object({
|
|
43
|
+
deploy: z.boolean().optional(),
|
|
43
44
|
iac: z.nativeEnum(IacType),
|
|
44
45
|
infra: z.nativeEnum(InfrastructureType),
|
|
45
46
|
});
|
package/oclif.manifest.json
CHANGED
|
@@ -87,6 +87,48 @@
|
|
|
87
87
|
"index.js"
|
|
88
88
|
]
|
|
89
89
|
},
|
|
90
|
+
"deploy": {
|
|
91
|
+
"aliases": [],
|
|
92
|
+
"args": {},
|
|
93
|
+
"description": "Deploy a hereya project using the project deployment package",
|
|
94
|
+
"examples": [
|
|
95
|
+
"<%= config.bin %> <%= command.id %>"
|
|
96
|
+
],
|
|
97
|
+
"flags": {
|
|
98
|
+
"chdir": {
|
|
99
|
+
"description": "directory to run command in",
|
|
100
|
+
"name": "chdir",
|
|
101
|
+
"required": false,
|
|
102
|
+
"hasDynamicHelp": false,
|
|
103
|
+
"multiple": false,
|
|
104
|
+
"type": "option"
|
|
105
|
+
},
|
|
106
|
+
"workspace": {
|
|
107
|
+
"char": "w",
|
|
108
|
+
"description": "name of the workspace to deploy the packages for",
|
|
109
|
+
"name": "workspace",
|
|
110
|
+
"required": false,
|
|
111
|
+
"hasDynamicHelp": false,
|
|
112
|
+
"multiple": false,
|
|
113
|
+
"type": "option"
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"hasDynamicHelp": false,
|
|
117
|
+
"hiddenAliases": [],
|
|
118
|
+
"id": "deploy",
|
|
119
|
+
"pluginAlias": "hereya-cli",
|
|
120
|
+
"pluginName": "hereya-cli",
|
|
121
|
+
"pluginType": "core",
|
|
122
|
+
"strict": true,
|
|
123
|
+
"enableJsonFlag": false,
|
|
124
|
+
"isESM": true,
|
|
125
|
+
"relativePath": [
|
|
126
|
+
"dist",
|
|
127
|
+
"commands",
|
|
128
|
+
"deploy",
|
|
129
|
+
"index.js"
|
|
130
|
+
]
|
|
131
|
+
},
|
|
90
132
|
"down": {
|
|
91
133
|
"aliases": [],
|
|
92
134
|
"args": {},
|
|
@@ -309,6 +351,48 @@
|
|
|
309
351
|
"index.js"
|
|
310
352
|
]
|
|
311
353
|
},
|
|
354
|
+
"undeploy": {
|
|
355
|
+
"aliases": [],
|
|
356
|
+
"args": {},
|
|
357
|
+
"description": "Undeploy a hereya project by removing all resources.",
|
|
358
|
+
"examples": [
|
|
359
|
+
"<%= config.bin %> <%= command.id %>"
|
|
360
|
+
],
|
|
361
|
+
"flags": {
|
|
362
|
+
"chdir": {
|
|
363
|
+
"description": "directory to run command in",
|
|
364
|
+
"name": "chdir",
|
|
365
|
+
"required": false,
|
|
366
|
+
"hasDynamicHelp": false,
|
|
367
|
+
"multiple": false,
|
|
368
|
+
"type": "option"
|
|
369
|
+
},
|
|
370
|
+
"workspace": {
|
|
371
|
+
"char": "w",
|
|
372
|
+
"description": "name of the workspace to undeploy the packages for",
|
|
373
|
+
"name": "workspace",
|
|
374
|
+
"required": false,
|
|
375
|
+
"hasDynamicHelp": false,
|
|
376
|
+
"multiple": false,
|
|
377
|
+
"type": "option"
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
"hasDynamicHelp": false,
|
|
381
|
+
"hiddenAliases": [],
|
|
382
|
+
"id": "undeploy",
|
|
383
|
+
"pluginAlias": "hereya-cli",
|
|
384
|
+
"pluginName": "hereya-cli",
|
|
385
|
+
"pluginType": "core",
|
|
386
|
+
"strict": true,
|
|
387
|
+
"enableJsonFlag": false,
|
|
388
|
+
"isESM": true,
|
|
389
|
+
"relativePath": [
|
|
390
|
+
"dist",
|
|
391
|
+
"commands",
|
|
392
|
+
"undeploy",
|
|
393
|
+
"index.js"
|
|
394
|
+
]
|
|
395
|
+
},
|
|
312
396
|
"up": {
|
|
313
397
|
"aliases": [],
|
|
314
398
|
"args": {},
|
|
@@ -373,6 +457,15 @@
|
|
|
373
457
|
"hasDynamicHelp": false,
|
|
374
458
|
"multiple": false,
|
|
375
459
|
"type": "option"
|
|
460
|
+
},
|
|
461
|
+
"source": {
|
|
462
|
+
"char": "s",
|
|
463
|
+
"description": "The source of the project to provision or destroy the package for",
|
|
464
|
+
"name": "source",
|
|
465
|
+
"required": false,
|
|
466
|
+
"hasDynamicHelp": false,
|
|
467
|
+
"multiple": false,
|
|
468
|
+
"type": "option"
|
|
376
469
|
}
|
|
377
470
|
},
|
|
378
471
|
"hasDynamicHelp": false,
|
|
@@ -606,5 +699,5 @@
|
|
|
606
699
|
]
|
|
607
700
|
}
|
|
608
701
|
},
|
|
609
|
-
"version": "0.
|
|
702
|
+
"version": "0.4.0"
|
|
610
703
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hereya-cli",
|
|
3
3
|
"description": "Developer's Package Manager for Seamless Infrastructure Integration.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"author": "The Hereya team",
|
|
6
6
|
"bin": {
|
|
7
7
|
"hereya": "./bin/run.js"
|
|
@@ -10,13 +10,17 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@aws-sdk/client-cloudformation": "^3.577.0",
|
|
12
12
|
"@aws-sdk/client-codebuild": "^3.583.0",
|
|
13
|
+
"@aws-sdk/client-s3": "^3.583.0",
|
|
13
14
|
"@aws-sdk/client-ssm": "^3.583.0",
|
|
14
15
|
"@aws-sdk/client-sts": "^3.582.0",
|
|
16
|
+
"@aws-sdk/lib-storage": "^3.583.0",
|
|
15
17
|
"@esm2cjs/execa": "^6.1.1-cjs.1",
|
|
16
18
|
"@oclif/core": "^3",
|
|
17
19
|
"@oclif/plugin-help": "^6",
|
|
18
20
|
"@oclif/plugin-plugins": "^5",
|
|
19
21
|
"@octokit/rest": "^20.1.1",
|
|
22
|
+
"glob": "^10.4.1",
|
|
23
|
+
"ignore": "^5.3.1",
|
|
20
24
|
"simple-git": "^3.24.0",
|
|
21
25
|
"yaml": "^2.4.2",
|
|
22
26
|
"zod": "^3.23.8"
|
|
@@ -73,7 +77,7 @@
|
|
|
73
77
|
},
|
|
74
78
|
"repository": "hereya/hereya-cli",
|
|
75
79
|
"scripts": {
|
|
76
|
-
"generate": "oclif generate command $COMMAND && shx mkdir src/commands/$COMMAND && shx mv src/commands/$COMMAND.ts src/commands/$COMMAND/index.ts && shx mv test/commands/$COMMAND.test.ts src/commands/$COMMAND/index.test.ts && shx rm -rf test",
|
|
80
|
+
"generate": "npx oclif generate command $COMMAND && shx mkdir src/commands/$COMMAND && shx mv src/commands/$COMMAND.ts src/commands/$COMMAND/index.ts && shx mv test/commands/$COMMAND.test.ts src/commands/$COMMAND/index.test.ts && shx rm -rf test",
|
|
77
81
|
"dev": "./bin/dev.js",
|
|
78
82
|
"build": "shx rm -rf dist && tsc -b",
|
|
79
83
|
"lint": "eslint .",
|