@agilecustoms/envctl 1.11.4 → 1.13.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.
@@ -1,5 +1,6 @@
1
1
  import { BusinessException, HttpException, NotFoundException } from '../exceptions.js';
2
2
  import { logger } from '../logger.js';
3
+ import { ConfigService } from '../service/index.js';
3
4
  const HOST = 'cli.maintenance.agilecustoms.com';
4
5
  export function toUrl(path) {
5
6
  return `https://${HOST}/env-api${path}`;
@@ -7,9 +8,11 @@ export function toUrl(path) {
7
8
  export class HttpClient {
8
9
  version;
9
10
  commandId;
10
- constructor(version, commandId) {
11
+ configService;
12
+ constructor(version, commandId, configService) {
11
13
  this.version = version;
12
14
  this.commandId = commandId;
15
+ this.configService = configService;
13
16
  }
14
17
  async post(path, body, options = {}) {
15
18
  options.method = 'POST';
@@ -28,13 +31,14 @@ export class HttpClient {
28
31
  const headers = new Headers(options.headers);
29
32
  headers.set('x-command-id', this.commandId);
30
33
  headers.set('x-client-version', this.version);
31
- const req = {
32
- ...options,
33
- method: options.method ?? 'GET',
34
- headers,
35
- };
36
- const reqJson = JSON.stringify({ ...req, headers: Object.fromEntries(headers.entries()) });
37
- logger.debug(`--> ${req.method} ${url} ${reqJson}`);
34
+ const apiKey = this.configService.getApiKey();
35
+ if (apiKey) {
36
+ headers.set('Authorization', apiKey);
37
+ }
38
+ options.method = options.method || 'GET';
39
+ options.headers = headers;
40
+ const reqJson = JSON.stringify({ ...options, headers: Object.fromEntries(headers.entries()) });
41
+ logger.debug(`--> ${options.method} ${url} ${reqJson}`);
38
42
  let response;
39
43
  try {
40
44
  response = await fetch(url, options);
@@ -10,12 +10,12 @@ export function configure(program, configService) {
10
10
  const answers = await inquirer.prompt([
11
11
  {
12
12
  type: 'input',
13
- name: 'userName',
14
- message: 'userName is required to deploy any env\n'
13
+ name: 'apiKey',
14
+ message: 'apiKey is required to deploy any env\n'
15
15
  + '(prefer GitHub username)\n'
16
- + 'userName:',
16
+ + 'apiKey:',
17
17
  },
18
18
  ]);
19
- configService.saveConfig({ userName: answers.userName });
19
+ configService.saveConfig({ apiKey: answers.apiKey });
20
20
  }));
21
21
  }
@@ -1,13 +1,14 @@
1
1
  import { Command } from 'commander';
2
- import { EnvService } from '../service/index.js';
2
+ import { ConfigService, EnvService } from '../service/index.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
- export function createEphemeral(program, envService) {
5
+ export function createEphemeral(program, configService, envService) {
6
6
  program
7
7
  .command('create-ephemeral')
8
8
  .description('Create bare env w/o resources. Used in CI as first step just to pre-generate token for extension')
9
9
  .requiredOption('--key <key>', _keys.KEY)
10
10
  .action(wrap(async (options) => {
11
+ configService.checkApiKey();
11
12
  const { key } = options;
12
13
  const token = await envService.createEphemeral(key);
13
14
  console.log(token);
@@ -1,14 +1,15 @@
1
1
  import { Command } from 'commander';
2
- import { EnvService } from '../service/index.js';
2
+ import { ConfigService, EnvService } from '../service/index.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
- export function deleteIt(program, envService) {
5
+ export function deleteIt(program, configService, envService) {
6
6
  program
7
7
  .command('delete')
8
8
  .description('Delete a development environment')
9
9
  .option('--key <key>', _keys.KEY)
10
10
  .option('--force', _keys.FORCE)
11
11
  .action(wrap(async (options) => {
12
+ configService.checkApiKey();
12
13
  const { key, force } = options;
13
14
  await envService.delete(Boolean(force), key);
14
15
  }));
@@ -1,13 +1,14 @@
1
1
  import { Command } from 'commander';
2
- import { EnvService } from '../service/index.js';
2
+ import { ConfigService, EnvService } from '../service/index.js';
3
3
  import { wrap } from './_utils.js';
4
- export function deploy(program, envService) {
4
+ export function deploy(program, configService, envService) {
5
5
  program
6
6
  .command('deploy')
7
7
  .description('Create new or update existing environment')
8
8
  .allowUnknownOption(true)
9
9
  .argument('[args...]')
10
10
  .action(wrap(async (tfArgs) => {
11
+ configService.checkApiKey();
11
12
  await envService.deploy(tfArgs);
12
13
  }));
13
14
  }
@@ -1,8 +1,8 @@
1
1
  import { Command } from 'commander';
2
- import { EnvService } from '../service/index.js';
2
+ import { ConfigService, EnvService } from '../service/index.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
- export function destroy(program, envService) {
5
+ export function destroy(program, configService, envService) {
6
6
  program
7
7
  .command('destroy')
8
8
  .description('Destroy environment resources. This is thin wrapper for terraform destroy.'
@@ -13,6 +13,7 @@ export function destroy(program, envService) {
13
13
  .allowUnknownOption(true)
14
14
  .argument('[args...]')
15
15
  .action(wrap(async (tfArgs, options) => {
16
+ configService.checkApiKey();
16
17
  const { force } = options;
17
18
  await envService.destroy(tfArgs, Boolean(force));
18
19
  }));
@@ -1,12 +1,14 @@
1
1
  import { Command } from 'commander';
2
+ import { ConfigService } from '../service/index.js';
2
3
  import { _keys } from './_keys.js';
3
4
  import { wrap } from './_utils.js';
4
- export function logs(program, logService) {
5
+ export function logs(program, configService, logService) {
5
6
  program
6
7
  .command('logs')
7
8
  .description('Get most recent env destroy logs')
8
9
  .option('--key <key>', _keys.KEY)
9
10
  .action(wrap(async (options) => {
11
+ configService.checkApiKey();
10
12
  const { key } = options;
11
13
  await logService.getLogs(key);
12
14
  }));
@@ -1,13 +1,14 @@
1
1
  import { Command } from 'commander';
2
- import { EnvService } from '../service/index.js';
2
+ import { ConfigService, EnvService } from '../service/index.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
- export function status(program, envService) {
5
+ export function status(program, configService, envService) {
6
6
  program
7
7
  .command('status')
8
8
  .description('Get env status')
9
9
  .option('--key <key>', _keys.KEY)
10
10
  .action(wrap(async (options) => {
11
+ configService.checkApiKey();
11
12
  const { key } = options;
12
13
  await envService.status(key);
13
14
  }));
package/dist/container.js CHANGED
@@ -10,7 +10,7 @@ export function buildContainer(appVersion) {
10
10
  const configService = new ConfigService();
11
11
  const backends = [new S3Backend()];
12
12
  const commandId = randomUUID();
13
- const httpClient = new HttpClient(appVersion, commandId);
13
+ const httpClient = new HttpClient(appVersion, commandId, configService);
14
14
  const envApiClient = new EnvApiClient(httpClient);
15
15
  const localStateService = new LocalStateService();
16
16
  const nonInteractive = !process.stdout.isTTY || process.env.CI === 'true';
package/dist/index.js CHANGED
@@ -18,11 +18,11 @@ program
18
18
  .version(pkg.version)
19
19
  .option('--verbose', 'Verbose output (w/ debug logs)');
20
20
  configure(program, configService);
21
- createEphemeral(program, envService);
22
- deleteIt(program, envService);
23
- deploy(program, envService);
24
- destroy(program, envService);
25
- logs(program, logService);
21
+ createEphemeral(program, configService, envService);
22
+ deleteIt(program, configService, envService);
23
+ deploy(program, configService, envService);
24
+ destroy(program, configService, envService);
25
+ logs(program, configService, logService);
26
26
  plan(program, envService);
27
- status(program, envService);
27
+ status(program, configService, envService);
28
28
  program.parse(process.argv);
@@ -1,7 +1,19 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as os from 'node:os';
3
3
  import path from 'path';
4
- const CONFIG_PATH = path.join(os.homedir(), '.envctl', 'config.json');
4
+ import { KnownException } from '../exceptions.js';
5
+ const CONFIG_PATH = path.join(os.homedir(), '.envctl', 'default.json');
6
+ var EnvKey;
7
+ (function (EnvKey) {
8
+ EnvKey["ENV_API_KEY"] = "ENVCTL_API_KEY";
9
+ EnvKey["ENV_MAP_KEY_TO"] = "ENVCTL_MAP_KEY_TO";
10
+ })(EnvKey || (EnvKey = {}));
11
+ function env(key) {
12
+ return process.env[key];
13
+ }
14
+ function isCI() {
15
+ return !!process.env['CI'];
16
+ }
5
17
  export class ConfigService {
6
18
  config;
7
19
  constructor() {
@@ -15,18 +27,27 @@ export class ConfigService {
15
27
  loadConfig() {
16
28
  if (this.config)
17
29
  return this.config;
18
- this.config = {};
19
30
  if (fs.existsSync(CONFIG_PATH)) {
20
31
  const data = fs.readFileSync(CONFIG_PATH, 'utf-8');
21
32
  this.config = JSON.parse(data);
33
+ return this.config;
22
34
  }
23
- return this.config;
35
+ return null;
24
36
  }
25
37
  getApiKey() {
26
- const apiKey = this.loadConfig()?.apiKey;
38
+ let apiKey = env(EnvKey.ENV_API_KEY);
27
39
  if (!apiKey) {
28
- return 'api-key';
40
+ apiKey = this.loadConfig()?.apiKey;
29
41
  }
30
42
  return apiKey;
31
43
  }
44
+ checkApiKey() {
45
+ const apiKey = this.getApiKey();
46
+ if (!apiKey) {
47
+ if (isCI()) {
48
+ throw new KnownException('API key is missing, set env variable ' + EnvKey.ENV_API_KEY);
49
+ }
50
+ throw new KnownException('API key is missing, call \'envctl configure\' or set env variable ' + EnvKey.ENV_API_KEY);
51
+ }
52
+ }
32
53
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agilecustoms/envctl",
3
- "version": "1.11.4",
3
+ "version": "1.13.0",
4
4
  "description": "node.js CLI client for manage environments",
5
5
  "keywords": [
6
6
  "terraform wrapper",
@@ -77,7 +77,7 @@
77
77
  "eslint": "^9.22.0",
78
78
  "eslint-plugin-import": "^2.31.0",
79
79
  "husky": "^9.1.7",
80
- "typescript": "^5.7.2",
80
+ "typescript": "^6.0.0",
81
81
  "typescript-eslint": "^8.8.1",
82
82
  "vitest": "^4.0.0"
83
83
  }