@agilecustoms/envctl 1.7.0 → 1.8.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.
@@ -10,8 +10,11 @@ import path from 'path';
10
10
  import * as readline from 'readline';
11
11
  import inquirer from 'inquirer';
12
12
  import { ProcessException, TimeoutException } from '../exceptions.js';
13
+ import { logger } from '../logger.js';
13
14
  export const NO_TIMEOUT = 0;
14
15
  export class Cli {
16
+ constructor() {
17
+ }
15
18
  async run(command, args, timeoutMs = NO_TIMEOUT, out_scanner, in_scanner) {
16
19
  const needsInteractive = !!in_scanner;
17
20
  const spawnOptions = {
@@ -93,7 +96,7 @@ export class Cli {
93
96
  }
94
97
  if (code === 0) {
95
98
  if (errorBuffer) {
96
- console.warn('Process completed successfully, but there were errors:\n' + errorBuffer);
99
+ logger.warn('Process completed successfully, but there were errors:\n' + errorBuffer);
97
100
  }
98
101
  resolve();
99
102
  }
@@ -1,4 +1,5 @@
1
1
  import { BusinessException, KnownException, NotFoundException } from '../exceptions.js';
2
+ import { logger } from '../logger.js';
2
3
  import { EnvStatus } from '../model/index.js';
3
4
  import { HttpClient, toUrl } from './HttpClient.js';
4
5
  export class EnvApiClient {
@@ -76,7 +77,7 @@ export class EnvApiClient {
76
77
  env.status = result.status;
77
78
  }
78
79
  async delete(key, force = false) {
79
- console.log('Sending delete command');
80
+ logger.info('Sending delete command');
80
81
  let result;
81
82
  try {
82
83
  result = await this.httpClient.fetch(`/ci/env/${key}?force=${force}`, {
@@ -89,7 +90,7 @@ export class EnvApiClient {
89
90
  }
90
91
  throw error;
91
92
  }
92
- console.log(result.message);
93
+ logger.info(result.message);
93
94
  }
94
95
  async getLogs(key) {
95
96
  let result;
@@ -1,4 +1,5 @@
1
1
  import { BusinessException, HttpException, NotFoundException } from '../exceptions.js';
2
+ import { logger } from '../logger.js';
2
3
  const HOST = 'cli.maintenance.agilecustoms.com';
3
4
  export function toUrl(path) {
4
5
  return `https://${HOST}/env-api${path}`;
@@ -19,6 +20,10 @@ export class HttpClient {
19
20
  }
20
21
  async fetch(path, options = {}) {
21
22
  const url = toUrl(path);
23
+ if (!options.method) {
24
+ options.method = 'GET';
25
+ }
26
+ logger.debug(`--> ${options.method} ${url} ${JSON.stringify(options)}`);
22
27
  let response;
23
28
  try {
24
29
  response = await fetch(url, options);
@@ -3,5 +3,6 @@ export const _keys = {
3
3
  FORCE: 'Force deletion without confirmation',
4
4
  KEY: 'Environment name/identifier - taken from remote stake key, must be unique for a given customer',
5
5
  KIND: 'Environment kind: complete project (default) or some slice such as tt-core + tt-web',
6
- OWNER: 'Environment owner (GH username)'
6
+ OWNER: 'Environment owner (GH username)',
7
+ VERBOSE: 'Verbose output (w/ debug logs)'
7
8
  };
@@ -1,24 +1,21 @@
1
1
  import { ExitPromptError } from '@inquirer/core';
2
2
  import { AbortedException, KnownException } from '../exceptions.js';
3
+ import { logger } from '../logger.js';
3
4
  export function wrap(callable) {
4
5
  return async (...args) => {
5
- let result;
6
6
  try {
7
- result = await callable(...args);
7
+ await callable(...args);
8
8
  }
9
9
  catch (error) {
10
10
  if (error instanceof KnownException) {
11
- console.error(error.message);
11
+ logger.error(error.message);
12
12
  }
13
13
  else if (error instanceof AbortedException || error instanceof ExitPromptError) {
14
14
  }
15
15
  else {
16
- console.error('Unknown error:', error);
16
+ logger.error('Unknown error: ' + error);
17
17
  }
18
18
  process.exit(1);
19
19
  }
20
- if (result !== undefined) {
21
- console.log(result);
22
- }
23
20
  };
24
21
  }
@@ -1,11 +1,13 @@
1
1
  import { Command } from 'commander';
2
2
  import inquirer from 'inquirer';
3
3
  import { configService } from '../container.js';
4
+ import { _keys } from './_keys.js';
4
5
  import { wrap } from './_utils.js';
5
6
  export function configure(program) {
6
7
  program
7
8
  .command('configure')
8
9
  .description('Configure user settings on your local machine')
10
+ .option('-v, --verbose', _keys.VERBOSE)
9
11
  .action(wrap(handler));
10
12
  }
11
13
  async function handler() {
@@ -7,6 +7,7 @@ export function createEphemeral(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
+ .option('-v, --verbose', _keys.VERBOSE)
10
11
  .action(wrap(handler));
11
12
  }
12
13
  async function handler(options) {
@@ -9,6 +9,7 @@ export function deleteIt(program) {
9
9
  .option('--key <key>', _keys.KEY)
10
10
  .option('--force', _keys.FORCE)
11
11
  .option('--cwd <cwd>', _keys.CWD)
12
+ .option('-v, --verbose', _keys.VERBOSE)
12
13
  .action(wrap(handler));
13
14
  }
14
15
  async function handler(options) {
@@ -7,6 +7,7 @@ export function deploy(program) {
7
7
  .command('deploy')
8
8
  .description('Create new or update existing environment')
9
9
  .option('--cwd <cwd>', _keys.CWD)
10
+ .option('-v, --verbose', _keys.VERBOSE)
10
11
  .allowUnknownOption(true)
11
12
  .argument('[args...]')
12
13
  .action(wrap(handler));
@@ -11,6 +11,7 @@ export function destroy(program) {
11
11
  + ' Main use case - test deletion process, basically that you have enough permissions to delete resources')
12
12
  .option('--force', _keys.FORCE)
13
13
  .option('--cwd <cwd>', _keys.CWD)
14
+ .option('-v, --verbose', _keys.VERBOSE)
14
15
  .allowUnknownOption(true)
15
16
  .argument('[args...]')
16
17
  .action(wrap(handler));
@@ -8,6 +8,7 @@ export function logs(program) {
8
8
  .description('Get most recent env destroy logs')
9
9
  .option('--key <key>', _keys.KEY)
10
10
  .option('--cwd <cwd>', _keys.CWD)
11
+ .option('-v, --verbose', _keys.VERBOSE)
11
12
  .action(wrap(handler));
12
13
  }
13
14
  async function handler(options) {
@@ -8,6 +8,7 @@ export function plan(program) {
8
8
  .description('High level wrapper for terraform plan. Compliments deploy command: if you plan to deploy env with envctl deploy,'
9
9
  + ' then it is recommended to do plan with envctl plan to guarantee consistent behavior')
10
10
  .option('--cwd <cwd>', _keys.CWD)
11
+ .option('-v, --verbose', _keys.VERBOSE)
11
12
  .allowUnknownOption(true)
12
13
  .argument('[args...]')
13
14
  .action(wrap(handler));
@@ -8,6 +8,7 @@ export function status(program) {
8
8
  .description('Get env status')
9
9
  .option('--key <key>', _keys.KEY)
10
10
  .option('--cwd <cwd>', _keys.CWD)
11
+ .option('-v, --verbose', _keys.VERBOSE)
11
12
  .action(wrap(handler));
12
13
  }
13
14
  async function handler(options) {
package/dist/logger.js ADDED
@@ -0,0 +1,36 @@
1
+ export class Logger {
2
+ now;
3
+ verbose;
4
+ buffer = [];
5
+ constructor(now, verbose) {
6
+ this.now = now;
7
+ this.verbose = verbose;
8
+ }
9
+ debug(msg) {
10
+ this.log('debug', msg);
11
+ }
12
+ info(msg) {
13
+ this.log('info', msg);
14
+ }
15
+ warn(msg) {
16
+ this.log('warn', msg);
17
+ }
18
+ error(msg) {
19
+ this.log('error', msg);
20
+ }
21
+ log(level, msg) {
22
+ const date = new Date(this.now()).toISOString();
23
+ const detailedMessage = `${date} [${level}] ${msg}`;
24
+ this.buffer.push(detailedMessage);
25
+ if (!this.verbose && level === 'debug') {
26
+ return;
27
+ }
28
+ const method = console[level];
29
+ method(this.verbose ? detailedMessage : `${msg}`);
30
+ }
31
+ getBuffer() {
32
+ return this.buffer;
33
+ }
34
+ }
35
+ const verbose = process.argv.includes('--verbose') || process.argv.includes('-v');
36
+ export const logger = new Logger(Date.now, verbose);
@@ -1,3 +1,4 @@
1
+ import { logger } from '../logger.js';
1
2
  export class BaseService {
2
3
  terraformAdapter;
3
4
  constructor(terraformAdapter) {
@@ -5,7 +6,7 @@ export class BaseService {
5
6
  }
6
7
  getKey(key) {
7
8
  if (!key) {
8
- console.log('Key is not provided, inferring from state file');
9
+ logger.info('Key is not provided, inferring from state file');
9
10
  key = this.terraformAdapter.getTerraformBackend().getKey();
10
11
  }
11
12
  return key;
@@ -1,6 +1,7 @@
1
1
  import { Cli } from '../client/Cli.js';
2
2
  import { EnvApiClient } from '../client/index.js';
3
3
  import { KnownException } from '../exceptions.js';
4
+ import { logger } from '../logger.js';
4
5
  import { EnvStatus } from '../model/index.js';
5
6
  import { BaseService } from './BaseService.js';
6
7
  export const PLAN_FILE = '.terraform/envctl.plan';
@@ -14,13 +15,13 @@ export class EnvService extends BaseService {
14
15
  }
15
16
  async status(key) {
16
17
  key = this.getKey(key);
17
- console.log(`Retrieve env ${key}`);
18
+ logger.info(`Retrieve env ${key}`);
18
19
  const env = await this.envApi.get(key);
19
20
  if (env === null) {
20
- console.log(`Env ${key} does not exist`);
21
+ logger.info(`Env ${key} does not exist`);
21
22
  return;
22
23
  }
23
- console.log(`Env ${key} status: ${env.status}`);
24
+ logger.info(`Env ${key} status: ${env.status}`);
24
25
  if (env.status !== EnvStatus.Deleting && env.status !== EnvStatus.DeleteError) {
25
26
  const date = new Date(env.ttl * 1000);
26
27
  const formattedDate = date.toLocaleString(undefined, {
@@ -31,23 +32,23 @@ export class EnvService extends BaseService {
31
32
  second: '2-digit',
32
33
  hour12: false,
33
34
  });
34
- console.log(`Expires at ${formattedDate}`);
35
+ logger.info(`Expires at ${formattedDate}`);
35
36
  }
36
37
  if (env.kind) {
37
38
  const kind = this.cli.getKind();
38
39
  if (env.kind !== kind) {
39
- console.warn(`Env ${key} kind (dir-name): ${env.kind} - looks like this env was deployed from a different directory`);
40
+ logger.warn(`Env ${key} kind (dir-name): ${env.kind} - looks like this env was deployed from a different directory`);
40
41
  }
41
42
  }
42
43
  }
43
44
  async tryGetEnv(key) {
44
- console.log(`Check if env ${key} already exists`);
45
+ logger.info(`Check if env ${key} already exists`);
45
46
  const env = await this.envApi.get(key);
46
47
  if (env) {
47
- console.log(`Found ${key} env`);
48
+ logger.info(`Found ${key} env`);
48
49
  }
49
50
  else {
50
- console.log(`Env ${key} does not exist`);
51
+ logger.info(`Env ${key} does not exist`);
51
52
  }
52
53
  return env;
53
54
  }
@@ -77,7 +78,7 @@ export class EnvService extends BaseService {
77
78
  return await this.envApi.createEphemeral(createEnv);
78
79
  }
79
80
  async lockTerraform(env, newEnv = false) {
80
- console.log('Lock providers');
81
+ logger.info('Lock providers');
81
82
  const res = await this.terraformAdapter.lock(newEnv);
82
83
  if (env !== null) {
83
84
  if (res.lockFile) {
@@ -123,11 +124,11 @@ export class EnvService extends BaseService {
123
124
  let env = await this.tryGetEnv(key);
124
125
  if (env === null) {
125
126
  const kind = this.cli.getKind();
126
- console.log(`Inferred kind from directory name: ${kind}`);
127
+ logger.info(`Inferred kind from directory name: ${kind}`);
127
128
  const createEnv = { key, kind };
128
129
  await this.lockTerraform(createEnv, true);
129
130
  tfArgs = await this.ensurePlan(env, tfArgs);
130
- console.log('Creating env metadata');
131
+ logger.info('Creating env metadata');
131
132
  env = await this.envApi.create(createEnv);
132
133
  await this.runDeploy(env, tfArgs);
133
134
  return;
@@ -139,16 +140,16 @@ export class EnvService extends BaseService {
139
140
  const answerYes = await this.cli.promptYesNo(`Env status is ${status}, likely due to an error from a previous run\n
140
141
  Do you want to proceed with deployment?`);
141
142
  if (!answerYes) {
142
- console.log('Aborting deployment');
143
+ logger.info('Aborting deployment');
143
144
  return;
144
145
  }
145
146
  }
146
147
  if (status === EnvStatus.Init || status === EnvStatus.Active) {
147
- console.log(`Env status is ${status}`);
148
+ logger.info(`Env status is ${status}`);
148
149
  await this.lockTerraform(env);
149
150
  tfArgs = await this.ensurePlan(env, tfArgs);
150
151
  if (env.status == EnvStatus.Active) {
151
- console.log('Will lock for update and run terraform apply (to update resources)');
152
+ logger.info('Will lock for update and run terraform apply (to update resources)');
152
153
  }
153
154
  await this.envApi.lockForUpdate(env);
154
155
  await this.runDeploy(env, tfArgs);
@@ -158,9 +159,9 @@ export class EnvService extends BaseService {
158
159
  await this.runDeploy(env, tfArgs);
159
160
  }
160
161
  async runDeploy(env, tfArgs) {
161
- console.log('Deploying resources');
162
+ logger.info('Deploying resources');
162
163
  await this.terraformAdapter.apply(tfArgs, env.ttl);
163
- console.log('Activating env (to finish creation)');
164
+ logger.info('Activating env (to finish creation)');
164
165
  await this.envApi.activate(env);
165
166
  }
166
167
  async delete(force, key) {
@@ -171,7 +172,7 @@ export class EnvService extends BaseService {
171
172
  const key = this.terraformAdapter.getTerraformBackend().getKey();
172
173
  const env = await this.get(key);
173
174
  if (env.status === EnvStatus.Init) {
174
- console.log(`Env ${env.key} status is INIT - no resources, nothing to destroy, just deleting metadata`);
175
+ logger.info(`Env ${env.key} status is INIT - no resources, nothing to destroy, just deleting metadata`);
175
176
  await this.envApi.delete(key, force);
176
177
  return;
177
178
  }
@@ -179,20 +180,20 @@ export class EnvService extends BaseService {
179
180
  throw new KnownException(`Env ${env.key} status is DELETING, please wait or re-run with --force`);
180
181
  }
181
182
  if (env.status === EnvStatus.Active) {
182
- console.log('Lock env to run destroy');
183
+ logger.info('Lock env to run destroy');
183
184
  await this.envApi.lockForUpdate(env);
184
185
  }
185
186
  await this.terraformAdapter.destroy(env, tfArgs, force);
186
187
  await this.envApi.delete(key, force);
187
- console.log('Please wait for ~30 sec before you can create env with same name');
188
+ logger.info('Please wait for ~30 sec before you can create env with same name');
188
189
  }
189
190
  async get(key) {
190
- console.log(`Retrieve env ${key}`);
191
+ logger.info(`Retrieve env ${key}`);
191
192
  const env = await this.envApi.get(key);
192
193
  if (!env) {
193
194
  throw new KnownException(`Environment ${key} is not found`);
194
195
  }
195
- console.log(`Env status: ${env.status}`);
196
+ logger.info(`Env status: ${env.status}`);
196
197
  return env;
197
198
  }
198
199
  }
@@ -1,5 +1,6 @@
1
1
  import * as fs from 'node:fs';
2
2
  import path from 'path';
3
+ import { logger } from '../logger.js';
3
4
  const CURRENT_VERSION = 1;
4
5
  export class LocalStateService {
5
6
  config;
@@ -16,16 +17,16 @@ export class LocalStateService {
16
17
  if (this.config) {
17
18
  return this.config;
18
19
  }
19
- console.log('Load local state config');
20
+ logger.info('Load local state config');
20
21
  const filePath = this.filePath();
21
22
  if (!fs.existsSync(filePath)) {
22
- console.warn('Local state file does not exist, must have been accidentally deleted, re-initializing');
23
+ logger.warn('Local state file does not exist, must have been accidentally deleted, re-initializing');
23
24
  return this.createEmptyConfig(filePath);
24
25
  }
25
26
  const data = fs.readFileSync(filePath, 'utf-8');
26
27
  let config = JSON.parse(data);
27
28
  if (config.version != CURRENT_VERSION) {
28
- console.log('Local state file version mismatch, re-initializing');
29
+ logger.info('Local state file version mismatch, re-initializing');
29
30
  config = this.createEmptyConfig(filePath);
30
31
  }
31
32
  this.config = config;
@@ -1,6 +1,7 @@
1
1
  import { basename } from 'node:path';
2
2
  import {} from '../client/index.js';
3
3
  import { KnownException } from '../exceptions.js';
4
+ import { logger } from '../logger.js';
4
5
  import { BaseService } from './BaseService.js';
5
6
  export class LogService extends BaseService {
6
7
  cli;
@@ -33,7 +34,7 @@ export class LogService extends BaseService {
33
34
  throw new KnownException(`Failed to download logs: ${res.status} ${res.statusText}${msg ? ` - ${msg}` : ''}`);
34
35
  }
35
36
  const outPath = await this.cli.writeInTmpFile(res.body, outName);
36
- console.log(`Logs saved to: ${outPath}`);
37
+ logger.info(`Logs saved to: ${outPath}`);
37
38
  await this.cli.run('open', ['-R', outPath]);
38
39
  }
39
40
  }
@@ -3,6 +3,7 @@ import { createHash } from 'node:crypto';
3
3
  import path from 'path';
4
4
  import { NO_TIMEOUT } from '../client/Cli.js';
5
5
  import { AbortedException, KnownException, ProcessException, TimeoutException } from '../exceptions.js';
6
+ import { logger } from '../logger.js';
6
7
  import { LocalStateService } from './LocalStateService.js';
7
8
  export const MAX_ATTEMPTS = 2;
8
9
  export const RETRYABLE_ERRORS = [
@@ -103,7 +104,7 @@ export class TerraformAdapter {
103
104
  return res;
104
105
  }
105
106
  async lockProviders() {
106
- console.log('Update lock file');
107
+ logger.info('Update lock file');
107
108
  await this.cli.run('terraform', ['providers', 'lock', '-platform=linux_arm64']);
108
109
  }
109
110
  printTime() {
@@ -112,7 +113,7 @@ export class TerraformAdapter {
112
113
  const edt = now.toLocaleString('en-US', {
113
114
  timeZone: 'America/New_York'
114
115
  });
115
- console.log(`\nTime EDT: ${edt}, UTC: ${utc}\n`);
116
+ logger.info(`\nTime EDT: ${edt}, UTC: ${utc}\n`);
116
117
  }
117
118
  tfArgs(env, args, vars = []) {
118
119
  const varDefs = this.getVarDefs();
@@ -163,7 +164,7 @@ export class TerraformAdapter {
163
164
  const keyValue = arg.slice(5);
164
165
  const eqIndex = keyValue.indexOf('=');
165
166
  if (eqIndex === -1) {
166
- console.error('Terraform var argument is not in key=value format:', arg);
167
+ logger.error('Terraform var argument is not in key=value format: ' + arg);
167
168
  return;
168
169
  }
169
170
  const key = keyValue.slice(0, eqIndex);
@@ -179,7 +180,7 @@ export class TerraformAdapter {
179
180
  Object.entries(vars).forEach(([key, value]) => {
180
181
  const varDef = varDefs[key];
181
182
  if (!varDef) {
182
- console.warn(`Terraform variable ${key} passed, but not found in .tf files`);
183
+ logger.warn(`Terraform variable ${key} passed, but not found in .tf files`);
183
184
  return;
184
185
  }
185
186
  if (varDef.sensitive)
@@ -215,7 +216,7 @@ export class TerraformAdapter {
215
216
  tfVarName = '';
216
217
  }
217
218
  }
218
- console.log('Running: terraform plan', ...args, '\n');
219
+ logger.info('Running: terraform plan ' + args.join(' ') + '\n');
219
220
  try {
220
221
  await this.cli.run('terraform', ['plan', ...args], NO_TIMEOUT, out_scanner, in_scanner);
221
222
  }
@@ -224,20 +225,20 @@ export class TerraformAdapter {
224
225
  throw error;
225
226
  }
226
227
  if (attemptNo < MAX_ATTEMPTS && RETRYABLE_ERRORS.some(err => error.message.includes(err))) {
227
- console.warn(`Retrying terraform plan due to error: ${error.message}`);
228
+ logger.warn(`Retrying terraform plan due to error: ${error.message}`);
228
229
  return this._plan(args, onDemandVars, attemptNo + 1);
229
230
  }
230
231
  const lockId = this.lockId(error, attemptNo);
231
232
  if (lockId) {
232
233
  await this.promptUnlock(lockId);
233
- console.info('State unlocked, retrying terraform plan');
234
+ logger.info('State unlocked, retrying terraform plan');
234
235
  return this._plan(args, onDemandVars, attemptNo + 1);
235
236
  }
236
237
  throw new KnownException(`terraform plan failed with code ${error.code}:\n${error.message}`, { cause: error });
237
238
  }
238
239
  }
239
240
  async apply(args, ttl) {
240
- console.log('Running: terraform apply', ...args, '\n');
241
+ logger.info('Running: terraform apply ' + args.join(' ') + '\n');
241
242
  try {
242
243
  await this._apply(args, ttl, 1);
243
244
  }
@@ -258,7 +259,7 @@ export class TerraformAdapter {
258
259
  if (timeout <= 0) {
259
260
  throw new KnownException('TTL expired before terraform apply could start');
260
261
  }
261
- console.debug('timeout: ', timeout);
262
+ logger.debug('timeout(ms): ' + timeout);
262
263
  try {
263
264
  await this.cli.run('terraform', ['apply', ...args], timeout);
264
265
  this.printTime();
@@ -268,13 +269,13 @@ export class TerraformAdapter {
268
269
  throw error;
269
270
  }
270
271
  if (attemptNo < MAX_ATTEMPTS && RETRYABLE_ERRORS.some(err => error.message.includes(err))) {
271
- console.warn(`Retrying terraform apply due to error: ${error.message}`);
272
+ logger.warn(`Retrying terraform apply due to error: ${error.message}`);
272
273
  return this._apply(args, ttl, attemptNo + 1);
273
274
  }
274
275
  const lockId = this.lockId(error, attemptNo);
275
276
  if (lockId) {
276
277
  await this.promptUnlock(lockId);
277
- console.info('State unlocked, retrying terraform apply');
278
+ logger.info('State unlocked, retrying terraform apply');
278
279
  return this._apply(args, ttl, attemptNo + 1);
279
280
  }
280
281
  throw new KnownException(`terraform apply failed with code ${error.code}:\n${error.message}`, { cause: error });
@@ -291,7 +292,7 @@ export class TerraformAdapter {
291
292
  wrongDir = true;
292
293
  }
293
294
  };
294
- console.log('Running: terraform destroy -auto-approve', ...args, '\n');
295
+ logger.info('Running: terraform destroy -auto-approve' + args.join(' ') + '\n');
295
296
  try {
296
297
  await this.cli.run('terraform', ['destroy', '-auto-approve', ...args], NO_TIMEOUT, scanner);
297
298
  }
@@ -307,7 +308,7 @@ export class TerraformAdapter {
307
308
  else {
308
309
  await this.promptUnlock(lockId);
309
310
  }
310
- console.info('State unlocked, retrying terraform destroy');
311
+ logger.info('State unlocked, retrying terraform destroy');
311
312
  return this._destroy(args, force, attemptNo + 1);
312
313
  }
313
314
  throw new KnownException(`terraform destroy failed with code ${error.code}:\n${error.message}`, { cause: error });
@@ -320,7 +321,7 @@ export class TerraformAdapter {
320
321
  if (attemptNo < MAX_ATTEMPTS && error.message.includes('Error acquiring the state lock')) {
321
322
  const match = error.message.match(/ID:\s+([a-f0-9-]{36})/i);
322
323
  if (match) {
323
- console.error(error.message);
324
+ logger.error(error.message);
324
325
  return match[1];
325
326
  }
326
327
  }
@@ -335,7 +336,7 @@ export class TerraformAdapter {
335
336
  await this.forceUnlock(id);
336
337
  }
337
338
  async forceUnlock(id) {
338
- console.log('Force unlocking state');
339
+ logger.info('Force unlocking state');
339
340
  await this.cli.run('terraform', ['force-unlock', '-force', id]);
340
341
  }
341
342
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agilecustoms/envctl",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "node.js CLI client for manage environments",
5
5
  "keywords": [
6
6
  "terraform wrapper",
@@ -37,11 +37,12 @@
37
37
  "test": "vitest run --coverage",
38
38
  "build": "tsc -p tsconfig.build.json",
39
39
  "run": "tsc -p tsconfig.build.json --sourceMap true && node dist/index.js",
40
+ "run-help": " npm run run -- deploy --help",
40
41
  "run-version": " npm run run -- --version",
41
42
  "run-configure": "npm run run -- configure",
42
43
  "~": "",
43
44
  "run-core-init": " cd ../tt-core && terraform init -upgrade -backend-config=key=laxa1986 -reconfigure",
44
- "run-core-status": " npm run run -- status --cwd ../tt-core",
45
+ "run-core-status": " npm run run -- status --cwd ../tt-core -v",
45
46
  "run-core-plan": " npm run run -- plan --cwd ../tt-core -out=.terraform/plan",
46
47
  "run-core-deployp": "npm run run -- deploy --cwd ../tt-core .terraform/plan",
47
48
  "run-core-deploy": " npm run run -- deploy --cwd ../tt-core -var=\"env_size=min\"",