hereya-cli 0.64.2 → 0.65.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.
Files changed (41) hide show
  1. package/README.md +133 -43
  2. package/dist/backend/cloud/cloud-backend.d.ts +71 -0
  3. package/dist/backend/cloud/cloud-backend.js +96 -0
  4. package/dist/backend/common.d.ts +4 -0
  5. package/dist/backend/common.js +1 -0
  6. package/dist/backend/index.d.ts +5 -1
  7. package/dist/backend/index.js +18 -2
  8. package/dist/commands/add/index.js +109 -2
  9. package/dist/commands/deploy/index.js +8 -2
  10. package/dist/commands/docker/run/index.js +1 -0
  11. package/dist/commands/down/index.js +111 -3
  12. package/dist/commands/env/index.js +1 -0
  13. package/dist/commands/executor/start/index.d.ts +11 -0
  14. package/dist/commands/executor/start/index.js +176 -0
  15. package/dist/commands/remove/index.js +138 -4
  16. package/dist/commands/run/index.js +1 -0
  17. package/dist/commands/undeploy/index.js +4 -1
  18. package/dist/commands/up/index.js +102 -5
  19. package/dist/commands/workspace/executor/install/index.d.ts +9 -0
  20. package/dist/commands/workspace/executor/install/index.js +110 -0
  21. package/dist/commands/workspace/executor/token/index.d.ts +8 -0
  22. package/dist/commands/workspace/executor/token/index.js +41 -0
  23. package/dist/commands/workspace/executor/uninstall/index.d.ts +9 -0
  24. package/dist/commands/workspace/executor/uninstall/index.js +102 -0
  25. package/dist/executor/context.d.ts +2 -0
  26. package/dist/executor/context.js +39 -0
  27. package/dist/executor/delegating.d.ts +15 -0
  28. package/dist/executor/delegating.js +50 -0
  29. package/dist/executor/index.d.ts +12 -3
  30. package/dist/executor/index.js +13 -2
  31. package/dist/executor/remote.d.ts +16 -0
  32. package/dist/executor/remote.js +168 -0
  33. package/dist/infrastructure/index.js +55 -22
  34. package/dist/lib/config/common.d.ts +5 -0
  35. package/dist/lib/config/simple.js +43 -24
  36. package/dist/lib/env/index.d.ts +9 -0
  37. package/dist/lib/env/index.js +101 -15
  38. package/dist/lib/package/index.d.ts +12 -0
  39. package/dist/lib/package/index.js +4 -0
  40. package/oclif.manifest.json +159 -1
  41. package/package.json +1 -1
@@ -0,0 +1,176 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { CloudBackend } from '../../../backend/cloud/cloud-backend.js';
3
+ import { getBackend } from '../../../backend/index.js';
4
+ import { LocalExecutor } from '../../../executor/local.js';
5
+ export default class ExecutorStart extends Command {
6
+ static description = `Start the remote executor process (polls for jobs from hereya cloud).
7
+
8
+ Jobs are executed in parallel up to the concurrency limit (default: 10). Use --concurrency to adjust.
9
+
10
+ Set the HEREYA_TOKEN environment variable to authenticate without running \`hereya login\`.
11
+ Set the HEREYA_CLOUD_URL environment variable to target a specific hereya cloud instance (defaults to https://cloud.hereya.dev).`;
12
+ static examples = [
13
+ '<%= config.bin %> <%= command.id %> -w my-workspace',
14
+ '<%= config.bin %> <%= command.id %> -w my-workspace --concurrency 5',
15
+ 'HEREYA_TOKEN=<token> <%= config.bin %> <%= command.id %> -w my-workspace',
16
+ 'HEREYA_TOKEN=<token> HEREYA_CLOUD_URL=https://my-cloud.example.com <%= config.bin %> <%= command.id %> -w my-workspace',
17
+ ];
18
+ static flags = {
19
+ concurrency: Flags.integer({
20
+ char: 'c',
21
+ default: 10,
22
+ description: 'maximum number of parallel jobs',
23
+ min: 1,
24
+ }),
25
+ workspace: Flags.string({
26
+ char: 'w',
27
+ description: 'name of the workspace to poll jobs for',
28
+ required: true,
29
+ }),
30
+ };
31
+ async run() {
32
+ const { flags } = await this.parse(ExecutorStart);
33
+ const { concurrency } = flags;
34
+ const executor = new LocalExecutor();
35
+ const backend = await getBackend({
36
+ token: process.env.HEREYA_TOKEN,
37
+ url: process.env.HEREYA_CLOUD_URL,
38
+ });
39
+ if (!(backend instanceof CloudBackend)) {
40
+ this.error('Remote executor requires cloud backend. Set HEREYA_TOKEN or run `hereya login` first.');
41
+ }
42
+ const cloudBackend = backend;
43
+ this.log(`Starting executor for workspace: ${flags.workspace} (concurrency: ${concurrency})`);
44
+ this.log('Polling for jobs...');
45
+ // Handle graceful shutdown
46
+ const state = { shuttingDown: false };
47
+ const shutdown = () => {
48
+ this.log('Shutting down executor...');
49
+ state.shuttingDown = true;
50
+ };
51
+ process.on('SIGINT', shutdown);
52
+ process.on('SIGTERM', shutdown);
53
+ const activeJobs = new Set();
54
+ while (!state.shuttingDown) {
55
+ try {
56
+ // Wait for a slot to open up if at concurrency limit
57
+ if (activeJobs.size >= concurrency) {
58
+ // eslint-disable-next-line no-await-in-loop
59
+ await Promise.race(activeJobs);
60
+ continue;
61
+ }
62
+ // eslint-disable-next-line no-await-in-loop
63
+ const pollResult = await cloudBackend.pollExecutorJobs({
64
+ workspace: flags.workspace,
65
+ });
66
+ if (!pollResult.success) {
67
+ this.warn(`Poll error: ${pollResult.reason}`);
68
+ // eslint-disable-next-line no-await-in-loop
69
+ await new Promise(resolve => {
70
+ setTimeout(resolve, 5000);
71
+ });
72
+ continue;
73
+ }
74
+ if (!pollResult.job) {
75
+ // No job available, continue polling
76
+ continue;
77
+ }
78
+ const { job } = pollResult;
79
+ this.log(`Received job ${job.id} (${job.type}) [${activeJobs.size + 1}/${concurrency} slots used]`);
80
+ // Fire off the job without awaiting it
81
+ const jobPromise = this.executeJob(job, executor, cloudBackend).finally(() => {
82
+ activeJobs.delete(jobPromise);
83
+ });
84
+ activeJobs.add(jobPromise);
85
+ }
86
+ catch (error) {
87
+ this.warn(`Executor error: ${error.message}`);
88
+ // Wait before retrying
89
+ // eslint-disable-next-line no-await-in-loop
90
+ await new Promise(resolve => {
91
+ setTimeout(resolve, 5000);
92
+ });
93
+ }
94
+ }
95
+ // Wait for all active jobs to complete before exiting
96
+ if (activeJobs.size > 0) {
97
+ this.log(`Waiting for ${activeJobs.size} active job(s) to complete...`);
98
+ await Promise.allSettled(activeJobs);
99
+ }
100
+ this.log('Executor stopped.');
101
+ }
102
+ async executeJob(job, executor, cloudBackend) {
103
+ // Set up buffered logging
104
+ let logBuffer = '';
105
+ const logInterval = setInterval(async () => {
106
+ if (logBuffer) {
107
+ const logs = logBuffer;
108
+ logBuffer = '';
109
+ try {
110
+ await cloudBackend.updateExecutorJob({
111
+ jobId: job.id,
112
+ logs,
113
+ });
114
+ }
115
+ catch {
116
+ // Log flush errors are non-fatal
117
+ }
118
+ }
119
+ }, 10_000);
120
+ const logger = {
121
+ debug(msg) {
122
+ logBuffer += msg + '\n';
123
+ },
124
+ error(msg) {
125
+ logBuffer += msg + '\n';
126
+ },
127
+ info(msg) {
128
+ logBuffer += msg + '\n';
129
+ },
130
+ };
131
+ try {
132
+ if (job.type === 'resolve-env') {
133
+ // resolve-env job: resolve env values using local executor
134
+ const resolved = await executor.resolveEnvValues(job.payload);
135
+ // Send resolved env as result
136
+ clearInterval(logInterval);
137
+ await cloudBackend.updateExecutorJob({
138
+ jobId: job.id,
139
+ logs: logBuffer,
140
+ result: { env: resolved, success: true },
141
+ status: 'completed',
142
+ });
143
+ this.log(`Job ${job.id} completed (resolve-env)`);
144
+ return;
145
+ }
146
+ const result = job.type === 'provision'
147
+ ? await executor.provision({
148
+ ...job.payload,
149
+ logger,
150
+ })
151
+ : await executor.destroy({
152
+ ...job.payload,
153
+ logger,
154
+ });
155
+ // Flush remaining logs and send result
156
+ clearInterval(logInterval);
157
+ await cloudBackend.updateExecutorJob({
158
+ jobId: job.id,
159
+ logs: logBuffer,
160
+ result,
161
+ status: result.success ? 'completed' : 'failed',
162
+ });
163
+ this.log(`Job ${job.id} ${result.success ? 'completed' : 'failed'}`);
164
+ }
165
+ catch (error) {
166
+ clearInterval(logInterval);
167
+ await cloudBackend.updateExecutorJob({
168
+ jobId: job.id,
169
+ logs: logBuffer + `\nError: ${error.message}\n`,
170
+ result: { reason: error.message, success: false },
171
+ status: 'failed',
172
+ });
173
+ this.warn(`Job ${job.id} failed: ${error.message}`);
174
+ }
175
+ }
176
+ }
@@ -1,7 +1,7 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import { Listr, ListrLogger, ListrLogLevels } from 'listr2';
3
3
  import { getBackend } from '../../backend/index.js';
4
- import { getExecutor } from '../../executor/index.js';
4
+ import { getExecutorForWorkspace } from '../../executor/context.js';
5
5
  import { getConfigManager } from '../../lib/config/index.js';
6
6
  import { getEnvManager } from '../../lib/env/index.js';
7
7
  import { getLogger, getLogPath, isDebug, setDebug } from '../../lib/log.js';
@@ -71,7 +71,8 @@ export default class Remove extends Command {
71
71
  // Parse package name to extract name and version
72
72
  const [packageNameWithoutVersion, userSpecifiedVersion] = ctx.package.split('@');
73
73
  // Check if package exists in config (using clean name without version)
74
- const packageInfo = config.packages?.[packageNameWithoutVersion] || config.deploy?.[packageNameWithoutVersion];
74
+ ctx.isDevDeploy = Boolean(config.devDeploy?.[packageNameWithoutVersion]);
75
+ const packageInfo = config.packages?.[packageNameWithoutVersion] || config.deploy?.[packageNameWithoutVersion] || config.devDeploy?.[packageNameWithoutVersion];
75
76
  if (!packageInfo) {
76
77
  throw new Error(`Package ${packageNameWithoutVersion} not found in the project.`);
77
78
  }
@@ -102,12 +103,34 @@ export default class Remove extends Command {
102
103
  },
103
104
  title: 'Resolving package parameters',
104
105
  },
106
+ {
107
+ skip: (ctx) => !ctx.isDevDeploy,
108
+ async task(ctx) {
109
+ const backend = await getBackend();
110
+ const profile = await getProfileFromWorkspace(backend, ctx.workspace, ctx.configOutput.config.project);
111
+ const envManager = getEnvManager();
112
+ const getProjectEnvOutput = await envManager.getProjectEnv({
113
+ excludeDevDeploy: true,
114
+ markSecret: false,
115
+ profile,
116
+ project: ctx.configOutput.config.project,
117
+ projectRootDir,
118
+ workspace: ctx.workspace,
119
+ });
120
+ if (!getProjectEnvOutput.success) {
121
+ throw new Error(getProjectEnvOutput.reason);
122
+ }
123
+ ctx.projectEnv = getProjectEnvOutput.env;
124
+ await delay(500);
125
+ },
126
+ title: 'Loading project environment for devDeploy',
127
+ },
105
128
  {
106
129
  rendererOptions: {
107
130
  persistentOutput: isDebug(),
108
131
  },
109
132
  async task(ctx, task) {
110
- const executor$ = getExecutor();
133
+ const executor$ = await getExecutorForWorkspace(ctx.workspace, ctx.configOutput.config.project);
111
134
  if (!executor$.success) {
112
135
  throw new Error(executor$.reason);
113
136
  }
@@ -117,8 +140,9 @@ export default class Remove extends Command {
117
140
  package: ctx.packageWithInstalledVersion,
118
141
  parameters: ctx.parametersOutput.parameters,
119
142
  project: ctx.configOutput.config.project,
143
+ projectEnv: ctx.projectEnv,
120
144
  projectRootDir,
121
- skipDeploy: true,
145
+ skipDeploy: !ctx.isDevDeploy,
122
146
  workspace: ctx.workspace,
123
147
  });
124
148
  if (!destroyOutput.success) {
@@ -134,7 +158,9 @@ export default class Remove extends Command {
134
158
  await envManager.removeProjectEnv({
135
159
  env: ctx.destroyOutput.env,
136
160
  infra: ctx.destroyOutput.metadata.infra,
161
+ pkg: ctx.destroyOutput.pkgName,
137
162
  projectRootDir,
163
+ snakeCase: ctx.destroyOutput.metadata.snakeCase,
138
164
  workspace: ctx.workspace,
139
165
  });
140
166
  await delay(500);
@@ -163,6 +189,114 @@ export default class Remove extends Command {
163
189
  },
164
190
  title: 'Saving state',
165
191
  },
192
+ {
193
+ skip(ctx) {
194
+ const devDeploy = ctx.configOutput.config.devDeploy ?? {};
195
+ return Object.keys(devDeploy).length === 0;
196
+ },
197
+ async task(ctx) {
198
+ const configManager = getConfigManager();
199
+ const { config } = await configManager.loadConfig({ projectRootDir });
200
+ const devDeploy = config.devDeploy ?? {};
201
+ if (Object.keys(devDeploy).length === 0) {
202
+ return;
203
+ }
204
+ const backend = await getBackend();
205
+ const profile = await getProfileFromWorkspace(backend, ctx.workspace, ctx.configOutput.config.project);
206
+ const envManager = getEnvManager();
207
+ const getProjectEnvOutput = await envManager.getProjectEnv({
208
+ excludeDevDeploy: true,
209
+ markSecret: false,
210
+ profile,
211
+ project: ctx.configOutput.config.project,
212
+ projectRootDir,
213
+ workspace: ctx.workspace,
214
+ });
215
+ if (!getProjectEnvOutput.success) {
216
+ throw new Error(getProjectEnvOutput.reason);
217
+ }
218
+ ctx.projectEnv = getProjectEnvOutput.env;
219
+ ctx.configOutput = { config, found: true };
220
+ await delay(500);
221
+ },
222
+ title: 'Loading project environment',
223
+ },
224
+ {
225
+ skip(ctx) {
226
+ const devDeploy = ctx.configOutput.config.devDeploy ?? {};
227
+ return Object.keys(devDeploy).length === 0;
228
+ },
229
+ async task(ctx, task) {
230
+ const configManager = getConfigManager();
231
+ const { config } = await configManager.loadConfig({ projectRootDir });
232
+ const devDeployPackages = config.devDeploy ?? {};
233
+ const devDeployEntries = Object.entries(devDeployPackages).map(([name, info]) => ({
234
+ name,
235
+ packageSpec: info.version ? `${name}@${info.version}` : name,
236
+ version: info.version || '',
237
+ }));
238
+ return task.newListr(devDeployEntries.map((pkg) => ({
239
+ rendererOptions: {
240
+ persistentOutput: isDebug(),
241
+ },
242
+ async task(_, task) {
243
+ const parameterManager = getParameterManager();
244
+ const backend = await getBackend();
245
+ const profile = await getProfileFromWorkspace(backend, ctx.workspace, ctx.configOutput.config.project);
246
+ const { parameters } = await parameterManager.getPackageParameters({
247
+ package: pkg.name,
248
+ profile,
249
+ projectRootDir,
250
+ });
251
+ const executor$ = await getExecutorForWorkspace(ctx.workspace, ctx.configOutput.config.project);
252
+ if (!executor$.success) {
253
+ throw new Error(executor$.reason);
254
+ }
255
+ const { executor } = executor$;
256
+ const provisionOutput = await executor.provision({
257
+ logger: getLogger(task),
258
+ package: pkg.packageSpec,
259
+ parameters,
260
+ project: ctx.configOutput.config.project,
261
+ projectEnv: ctx.projectEnv ?? {},
262
+ workspace: ctx.workspace,
263
+ });
264
+ if (!provisionOutput.success) {
265
+ throw new Error(provisionOutput.reason);
266
+ }
267
+ const { env, metadata } = provisionOutput;
268
+ const output = ctx.devDeployAdded || [];
269
+ output.push({ env, metadata, packageName: pkg.name });
270
+ ctx.devDeployAdded = output;
271
+ },
272
+ title: `Provisioning ${pkg.name}`,
273
+ })), { concurrent: true, rendererOptions: { collapseSubtasks: !isDebug() } });
274
+ },
275
+ title: 'Provisioning devDeploy packages',
276
+ },
277
+ {
278
+ skip: (ctx) => !ctx.devDeployAdded || ctx.devDeployAdded.length === 0,
279
+ async task(ctx) {
280
+ if (!ctx.devDeployAdded || ctx.devDeployAdded.length === 0) {
281
+ return;
282
+ }
283
+ const envManager = getEnvManager();
284
+ for (const { env, metadata, packageName } of ctx.devDeployAdded) {
285
+ // eslint-disable-next-line no-await-in-loop
286
+ await envManager.addProjectEnv({
287
+ env,
288
+ infra: metadata.originalInfra ?? metadata.infra,
289
+ isDevDeploy: true,
290
+ pkg: packageName,
291
+ projectRootDir,
292
+ snakeCase: metadata.snakeCase,
293
+ workspace: ctx.workspace,
294
+ });
295
+ }
296
+ await delay(500);
297
+ },
298
+ title: 'Adding env vars from devDeploy packages',
299
+ },
166
300
  ]);
167
301
  },
168
302
  title: `Removing ${args.package}`,
@@ -53,6 +53,7 @@ export default class Run extends Command {
53
53
  const envManager = getEnvManager();
54
54
  const getProjectEnvOutput = await envManager.getProjectEnv({
55
55
  profile,
56
+ project: config.project,
56
57
  projectRootDir,
57
58
  workspace,
58
59
  });
@@ -70,6 +70,7 @@ export default class Undeploy extends Command {
70
70
  const getProjectEnvOutput = await envManager.getProjectEnv({
71
71
  markSecret: true,
72
72
  profile,
73
+ project: ctx.configOutput.config.project,
73
74
  projectRootDir,
74
75
  workspace: ctx.workspace,
75
76
  });
@@ -229,12 +230,14 @@ export default class Undeploy extends Command {
229
230
  return;
230
231
  }
231
232
  const envManager = getEnvManager();
232
- for (const { env, metadata } of destroyed) {
233
+ for (const { env, metadata, packageName } of destroyed) {
233
234
  // eslint-disable-next-line no-await-in-loop
234
235
  await envManager.removeProjectEnv({
235
236
  env,
236
237
  infra: metadata.originalInfra ?? metadata.infra,
238
+ pkg: packageName,
237
239
  projectRootDir,
240
+ snakeCase: metadata.snakeCase,
238
241
  workspace,
239
242
  });
240
243
  }
@@ -1,7 +1,7 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
2
  import { Listr, ListrLogger, ListrLogLevels } from 'listr2';
3
3
  import { getBackend } from '../../backend/index.js';
4
- import { getExecutor } from '../../executor/index.js';
4
+ import { getExecutorForWorkspace } from '../../executor/context.js';
5
5
  import { getConfigManager } from '../../lib/config/index.js';
6
6
  import { getEnvManager } from '../../lib/env/index.js';
7
7
  import { getLogger, getLogPath, isDebug, setDebug } from '../../lib/log.js';
@@ -142,7 +142,7 @@ export default class Up extends Command {
142
142
  profile,
143
143
  projectRootDir,
144
144
  });
145
- const executor$ = getExecutor();
145
+ const executor$ = await getExecutorForWorkspace(ctx.workspace, ctx.configOutput.config.project);
146
146
  if (!executor$.success) {
147
147
  throw new Error(executor$.reason);
148
148
  }
@@ -188,7 +188,7 @@ export default class Up extends Command {
188
188
  profile,
189
189
  projectRootDir,
190
190
  });
191
- const executor$ = getExecutor();
191
+ const executor$ = await getExecutorForWorkspace(ctx.workspace, ctx.configOutput.config.project);
192
192
  if (!executor$.success) {
193
193
  throw new Error(executor$.reason);
194
194
  }
@@ -223,12 +223,14 @@ export default class Up extends Command {
223
223
  return;
224
224
  }
225
225
  const envManager = getEnvManager();
226
- for (const { env, metadata } of removed) {
226
+ for (const { env, metadata, packageName } of removed) {
227
227
  // eslint-disable-next-line no-await-in-loop
228
228
  await envManager.removeProjectEnv({
229
229
  env,
230
230
  infra: metadata.originalInfra ?? metadata.infra,
231
+ pkg: packageName,
231
232
  projectRootDir,
233
+ snakeCase: metadata.snakeCase,
232
234
  workspace,
233
235
  });
234
236
  }
@@ -243,12 +245,14 @@ export default class Up extends Command {
243
245
  return;
244
246
  }
245
247
  const envManager = getEnvManager();
246
- for (const { env, metadata } of ctx.added) {
248
+ for (const { env, metadata, packageName } of ctx.added) {
247
249
  // eslint-disable-next-line no-await-in-loop
248
250
  await envManager.addProjectEnv({
249
251
  env,
250
252
  infra: metadata.originalInfra ?? metadata.infra,
253
+ pkg: packageName,
251
254
  projectRootDir,
255
+ snakeCase: metadata.snakeCase,
252
256
  workspace: ctx.workspace,
253
257
  });
254
258
  }
@@ -256,6 +260,99 @@ export default class Up extends Command {
256
260
  },
257
261
  title: 'Adding env vars from added packages',
258
262
  },
263
+ {
264
+ skip: (ctx) => !ctx.configOutput.config.devDeploy || Object.keys(ctx.configOutput.config.devDeploy).length === 0,
265
+ async task(ctx) {
266
+ const backend = await getBackend();
267
+ const profile = await getProfileFromWorkspace(backend, ctx.workspace, ctx.configOutput.config.project);
268
+ const envManager = getEnvManager();
269
+ const getProjectEnvOutput = await envManager.getProjectEnv({
270
+ excludeDevDeploy: true,
271
+ markSecret: false,
272
+ profile,
273
+ project: ctx.configOutput.config.project,
274
+ projectRootDir,
275
+ workspace: ctx.workspace,
276
+ });
277
+ if (!getProjectEnvOutput.success) {
278
+ throw new Error(getProjectEnvOutput.reason);
279
+ }
280
+ ctx.projectEnv = getProjectEnvOutput.env;
281
+ await delay(500);
282
+ },
283
+ title: 'Loading project environment',
284
+ },
285
+ {
286
+ skip: (ctx) => !ctx.configOutput.config.devDeploy || Object.keys(ctx.configOutput.config.devDeploy).length === 0,
287
+ async task(ctx) {
288
+ const devDeployPackages = ctx.configOutput.config.devDeploy ?? {};
289
+ const devDeployEntries = Object.entries(devDeployPackages).map(([name, info]) => ({
290
+ name,
291
+ packageSpec: info.version ? `${name}@${info.version}` : name,
292
+ version: info.version || '',
293
+ }));
294
+ return task.newListr(devDeployEntries.map((pkg) => ({
295
+ rendererOptions: {
296
+ persistentOutput: isDebug(),
297
+ },
298
+ async task(_, task) {
299
+ const parameterManager = getParameterManager();
300
+ const backend = await getBackend();
301
+ const profile = await getProfileFromWorkspace(backend, ctx.workspace, ctx.configOutput.config.project);
302
+ const { parameters } = await parameterManager.getPackageParameters({
303
+ package: pkg.name,
304
+ profile,
305
+ projectRootDir,
306
+ });
307
+ const executor$ = await getExecutorForWorkspace(ctx.workspace, ctx.configOutput.config.project);
308
+ if (!executor$.success) {
309
+ throw new Error(executor$.reason);
310
+ }
311
+ const { executor } = executor$;
312
+ const provisionOutput = await executor.provision({
313
+ logger: getLogger(task),
314
+ package: pkg.packageSpec,
315
+ parameters,
316
+ project: ctx.configOutput.config.project,
317
+ projectEnv: ctx.projectEnv ?? {},
318
+ workspace: ctx.workspace,
319
+ });
320
+ if (!provisionOutput.success) {
321
+ throw new Error(provisionOutput.reason);
322
+ }
323
+ const { env, metadata } = provisionOutput;
324
+ const output = ctx.devDeployAdded || [];
325
+ output.push({ env, metadata, packageName: pkg.name });
326
+ ctx.devDeployAdded = output;
327
+ },
328
+ title: `Provisioning ${pkg.name}`,
329
+ })), { concurrent: true, rendererOptions: { collapseSubtasks: !isDebug() } });
330
+ },
331
+ title: 'Provisioning devDeploy packages',
332
+ },
333
+ {
334
+ skip: (ctx) => !ctx.devDeployAdded || ctx.devDeployAdded.length === 0,
335
+ async task(ctx) {
336
+ if (!ctx.devDeployAdded || ctx.devDeployAdded.length === 0) {
337
+ return;
338
+ }
339
+ const envManager = getEnvManager();
340
+ for (const { env, metadata, packageName } of ctx.devDeployAdded) {
341
+ // eslint-disable-next-line no-await-in-loop
342
+ await envManager.addProjectEnv({
343
+ env,
344
+ infra: metadata.originalInfra ?? metadata.infra,
345
+ isDevDeploy: true,
346
+ pkg: packageName,
347
+ projectRootDir,
348
+ snakeCase: metadata.snakeCase,
349
+ workspace: ctx.workspace,
350
+ });
351
+ }
352
+ await delay(500);
353
+ },
354
+ title: 'Adding env vars from devDeploy packages',
355
+ },
259
356
  {
260
357
  async task(ctx) {
261
358
  const backend = await getBackend();
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class WorkspaceExecutorInstall extends Command {
3
+ static description: string;
4
+ static flags: {
5
+ debug: import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
+ workspace: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ };
8
+ run(): Promise<void>;
9
+ }