@agilecustoms/envctl 0.35.3 → 0.36.1

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.
@@ -58,4 +58,17 @@ export class EnvApiClient {
58
58
  env.status = EnvStatus.Deleting;
59
59
  console.log(result.statusText);
60
60
  }
61
+ async getLogs(key) {
62
+ let result;
63
+ try {
64
+ result = await this.httpClient.fetch(`/ci/env/${key}/logs`);
65
+ }
66
+ catch (error) {
67
+ if (error instanceof NotFoundException) {
68
+ throw new KnownException(`Environment ${key} is not found`);
69
+ }
70
+ throw error;
71
+ }
72
+ return result.url;
73
+ }
61
74
  }
@@ -33,6 +33,13 @@ export class TerraformAdapter {
33
33
  throw new KnownException(`Failed to read terraform lock file: ${lockPath}`, { cause: err });
34
34
  }
35
35
  }
36
+ getKey(key, cwd) {
37
+ if (!key) {
38
+ console.log('Key is not provided, inferring from state file');
39
+ key = this.getTerraformBackend(cwd).getKey();
40
+ }
41
+ return key;
42
+ }
36
43
  getTerraformBackend(cwd) {
37
44
  if (this.backend) {
38
45
  return this.backend;
@@ -1,5 +1,5 @@
1
1
  import { Command } from 'commander';
2
- import { envCtl } from '../container.js';
2
+ import { envService } from '../container.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
5
  export function createEphemeral(program) {
@@ -11,6 +11,6 @@ export function createEphemeral(program) {
11
11
  }
12
12
  async function handler(options) {
13
13
  const { key } = options;
14
- const token = await envCtl.createEphemeral(key);
14
+ const token = await envService.createEphemeral(key);
15
15
  console.log(token);
16
16
  }
@@ -1,5 +1,5 @@
1
1
  import { Command } from 'commander';
2
- import { envCtl } from '../container.js';
2
+ import { envService } from '../container.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
5
  export function deleteIt(program) {
@@ -13,5 +13,5 @@ export function deleteIt(program) {
13
13
  }
14
14
  async function handler(options) {
15
15
  const { key, force, cwd } = options;
16
- await envCtl.delete(Boolean(force), key, cwd);
16
+ await envService.delete(Boolean(force), key, cwd);
17
17
  }
@@ -1,5 +1,5 @@
1
1
  import { Command } from 'commander';
2
- import { awsCredsHelper, envCtl } from '../container.js';
2
+ import { awsCredsHelper, envService } from '../container.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
5
  export function deploy(program) {
@@ -14,5 +14,5 @@ export function deploy(program) {
14
14
  async function handler(tfArgs, options) {
15
15
  await awsCredsHelper.ensureCredentials();
16
16
  const { cwd } = options;
17
- await envCtl.deploy(tfArgs, cwd);
17
+ await envService.deploy(tfArgs, cwd);
18
18
  }
@@ -1,5 +1,5 @@
1
1
  import { Command } from 'commander';
2
- import { envCtl } from '../container.js';
2
+ import { envService } from '../container.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
5
  export function destroy(program) {
@@ -17,5 +17,5 @@ export function destroy(program) {
17
17
  }
18
18
  async function handler(tfArgs, options) {
19
19
  const { force, cwd } = options;
20
- await envCtl.destroy(tfArgs, Boolean(force), cwd);
20
+ await envService.destroy(tfArgs, Boolean(force), cwd);
21
21
  }
@@ -3,5 +3,6 @@ export { createEphemeral } from './createEphemeral.js';
3
3
  export { deleteIt } from './delete.js';
4
4
  export { deploy } from './deploy.js';
5
5
  export { destroy } from './destroy.js';
6
+ export { logs } from './logs.js';
6
7
  export { plan } from './plan.js';
7
8
  export { status } from './status.js';
@@ -0,0 +1,16 @@
1
+ import { Command } from 'commander';
2
+ import { logService } from '../container.js';
3
+ import { _keys } from './_keys.js';
4
+ import { wrap } from './_utils.js';
5
+ export function logs(program) {
6
+ program
7
+ .command('logs')
8
+ .description('Get most recent env destroy logs')
9
+ .option('--key <key>', _keys.KEY)
10
+ .option('--cwd <cwd>', _keys.CWD)
11
+ .action(wrap(handler));
12
+ }
13
+ async function handler(options) {
14
+ const { key, cwd } = options;
15
+ await logService.getLogs(key, cwd);
16
+ }
@@ -1,5 +1,5 @@
1
1
  import { Command } from 'commander';
2
- import { awsCredsHelper, envCtl } from '../container.js';
2
+ import { awsCredsHelper, envService } from '../container.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
5
  export function plan(program) {
@@ -15,5 +15,5 @@ export function plan(program) {
15
15
  async function handler(tfArgs, options) {
16
16
  await awsCredsHelper.ensureCredentials();
17
17
  const { cwd } = options;
18
- await envCtl.plan(tfArgs, cwd);
18
+ await envService.plan(tfArgs, cwd);
19
19
  }
@@ -1,5 +1,5 @@
1
1
  import { Command } from 'commander';
2
- import { envCtl } from '../container.js';
2
+ import { envService } from '../container.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
5
  export function status(program) {
@@ -12,5 +12,5 @@ export function status(program) {
12
12
  }
13
13
  async function handler(options) {
14
14
  const { key, cwd } = options;
15
- await envCtl.status(key, cwd);
15
+ await envService.status(key, cwd);
16
16
  }
package/dist/container.js CHANGED
@@ -2,7 +2,8 @@ import { S3Backend } from './backend/S3Backend.js';
2
2
  import { AwsCredsHelper } from './client/AwsCredsHelper.js';
3
3
  import { CliHelper, EnvApiClient, HttpClient, TerraformAdapter } from './client/index.js';
4
4
  import { ProcessRunner } from './client/ProcessRunner.js';
5
- import { ConfigService, EnvCtl } from './service/index.js';
5
+ import { ConfigService, EnvService } from './service/index.js';
6
+ import { LogService } from './service/LogService.js';
6
7
  const cliHelper = new CliHelper();
7
8
  const configService = new ConfigService();
8
9
  const awsCredsHelper = new AwsCredsHelper();
@@ -13,5 +14,6 @@ const httpClient = new HttpClient(awsCredsHelper);
13
14
  const envApiClient = new EnvApiClient(httpClient);
14
15
  const processRunner = new ProcessRunner();
15
16
  const terraformAdapter = new TerraformAdapter(processRunner, cliHelper, backends);
16
- const envCtl = new EnvCtl(cliHelper, envApiClient, terraformAdapter);
17
- export { awsCredsHelper, configService, envCtl };
17
+ const envService = new EnvService(cliHelper, envApiClient, terraformAdapter);
18
+ const logService = new LogService(envApiClient, terraformAdapter, processRunner);
19
+ export { awsCredsHelper, configService, envService, logService };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { createRequire } from 'module';
3
3
  import { Command } from 'commander';
4
4
  import updateNotifier from 'update-notifier';
5
- import { configure, createEphemeral, deleteIt, deploy, destroy, plan, status } from './commands/index.js';
5
+ import { configure, createEphemeral, deleteIt, deploy, destroy, logs, plan, status } from './commands/index.js';
6
6
  const require = createRequire(import.meta.url);
7
7
  const pkg = require('../package.json');
8
8
  updateNotifier({ pkg, updateCheckInterval: 0 }).notify();
@@ -16,6 +16,7 @@ createEphemeral(program);
16
16
  deleteIt(program);
17
17
  deploy(program);
18
18
  destroy(program);
19
+ logs(program);
19
20
  plan(program);
20
21
  status(program);
21
22
  program.parse(process.argv);
@@ -1,6 +1,6 @@
1
1
  import { KnownException } from '../exceptions.js';
2
2
  import { EnvStatus, } from '../model/index.js';
3
- export class EnvCtl {
3
+ export class EnvService {
4
4
  cliHelper;
5
5
  envApi;
6
6
  terraformAdapter;
@@ -9,15 +9,8 @@ export class EnvCtl {
9
9
  this.envApi = envApi;
10
10
  this.terraformAdapter = terraformAdapter;
11
11
  }
12
- getKey(key, cwd) {
13
- if (!key) {
14
- console.log('Key is not provided, inferring from state file');
15
- key = this.terraformAdapter.getTerraformBackend(cwd).getKey();
16
- }
17
- return key;
18
- }
19
12
  async status(key, cwd) {
20
- key = this.getKey(key, cwd);
13
+ key = this.terraformAdapter.getKey(key, cwd);
21
14
  console.log(`Retrieve env ${key}`);
22
15
  const env = await this.envApi.get(key);
23
16
  if (env === null) {
@@ -150,7 +143,7 @@ export class EnvCtl {
150
143
  await this.envApi.activate(env);
151
144
  }
152
145
  async delete(force, key, cwd) {
153
- key = this.getKey(key, cwd);
146
+ key = this.terraformAdapter.getKey(key, cwd);
154
147
  const env = await this.get(key);
155
148
  if (env.status === EnvStatus.Init) {
156
149
  await this.envApi.delete(env);
@@ -0,0 +1,36 @@
1
+ import { createWriteStream } from 'node:fs';
2
+ import { mkdir } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { basename, join } from 'node:path';
5
+ import { Readable } from 'node:stream';
6
+ import { pipeline } from 'node:stream/promises';
7
+ import { TerraformAdapter } from '../client/index.js';
8
+ import { KnownException } from '../exceptions.js';
9
+ export class LogService {
10
+ envApi;
11
+ terraformAdapter;
12
+ processRunner;
13
+ constructor(envApi, terraformAdapter, processRunner) {
14
+ this.envApi = envApi;
15
+ this.terraformAdapter = terraformAdapter;
16
+ this.processRunner = processRunner;
17
+ }
18
+ async getLogs(key, cwd) {
19
+ key = this.terraformAdapter.getKey(key, cwd);
20
+ const url = await this.envApi.getLogs(key);
21
+ const outDir = join(tmpdir(), 'envctl');
22
+ await mkdir(outDir, { recursive: true });
23
+ const urlPath = new URL(url).pathname;
24
+ const gzName = basename(urlPath);
25
+ const outName = gzName.slice(0, -3);
26
+ const outPath = join(outDir, outName);
27
+ const res = await fetch(url);
28
+ if (!res.ok) {
29
+ const msg = await res.text();
30
+ throw new KnownException(`Failed to download logs: ${res.status} ${res.statusText}${msg ? ` - ${msg}` : ''}`);
31
+ }
32
+ await pipeline(Readable.fromWeb(res.body), createWriteStream(outPath, { flags: 'w' }));
33
+ console.log(`Logs saved to: ${outPath}`);
34
+ await this.processRunner.run('open', ['-R', outPath]);
35
+ }
36
+ }
@@ -1,2 +1,2 @@
1
- export { EnvCtl } from './EnvCtl.js';
1
+ export { EnvService } from './EnvService.js';
2
2
  export { ConfigService } from './ConfigService.js';
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@agilecustoms/envctl",
3
3
  "description": "node.js CLI client for manage environments",
4
- "version": "0.35.3",
4
+ "version": "0.36.1",
5
5
  "author": "Alex Chekulaev",
6
6
  "type": "module",
7
+ "engines": {
8
+ "node": ">=22.0.0"
9
+ },
7
10
  "bin": {
8
11
  "envctl": "dist/index.js"
9
12
  },
@@ -35,6 +38,7 @@
35
38
  "run-core-deploy": " tsc --sourceMap true && npm run run -- deploy --cwd ../tt-core -var=\"env_size=min\"",
36
39
  "run-core-delete": " tsc --sourceMap true && npm run run -- delete --cwd ../tt-core",
37
40
  "run-core-destroy": "tsc --sourceMap true && npm run run -- destroy --cwd ../tt-core",
41
+ "run-core-logs": " tsc --sourceMap true && npm run run -- logs --cwd ../tt-core",
38
42
  "*": "",
39
43
  "run-init": "cd ../tt-gitops && terraform init -upgrade -backend-config=key=laxa1986 -reconfigure",
40
44
  "run-status": " tsc --sourceMap true && npm run run -- status --cwd ../tt-gitops",