@agilecustoms/envctl 0.28.2 → 0.30.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.
@@ -34,7 +34,11 @@ export class EnvApiClient {
34
34
  env.status = EnvStatus.Active;
35
35
  }
36
36
  async lockForUpdate(env) {
37
- const { status } = await this.httpClient.post(`/ci/env/${env.key}/lock-for-update`);
37
+ const body = {
38
+ stateFile: env.stateFile,
39
+ lockFile: env.lockFile
40
+ };
41
+ const { status } = await this.httpClient.post(`/ci/env/${env.key}/lock-for-update`, body);
38
42
  env.status = status;
39
43
  }
40
44
  async delete(env) {
@@ -19,7 +19,7 @@ export class TerraformAdapter {
19
19
  return acc;
20
20
  }, new Map());
21
21
  }
22
- getKey(cwd) {
22
+ getStateFile(cwd) {
23
23
  const dir = cwd ?? process.cwd();
24
24
  const statePath = path.join(dir, '.terraform', 'terraform.tfstate');
25
25
  if (!fs.existsSync(statePath)) {
@@ -32,12 +32,29 @@ export class TerraformAdapter {
32
32
  catch (err) {
33
33
  throw new KnownException(`Failed to read terraform state file: ${statePath}`, { cause: err });
34
34
  }
35
+ return content;
36
+ }
37
+ getLockFile(cwd) {
38
+ const dir = cwd ?? process.cwd();
39
+ const lockPath = path.join(dir, '.terraform.lock.hcl');
40
+ if (!fs.existsSync(lockPath)) {
41
+ throw new KnownException(`Terraform lock file not found at: ${lockPath}`);
42
+ }
43
+ try {
44
+ return fs.readFileSync(lockPath, 'utf8');
45
+ }
46
+ catch (err) {
47
+ throw new KnownException(`Failed to read terraform lock file: ${lockPath}`, { cause: err });
48
+ }
49
+ }
50
+ getKey(cwd) {
51
+ const stateFile = this.getStateFile(cwd);
35
52
  let tfstate;
36
53
  try {
37
- tfstate = JSON.parse(content);
54
+ tfstate = JSON.parse(stateFile);
38
55
  }
39
56
  catch (err) {
40
- throw new KnownException(`Failed to parse terraform state file: ${statePath}`, { cause: err });
57
+ throw new KnownException(`Failed to parse terraform state file: .terraform/terraform.tfstate`, { cause: err });
41
58
  }
42
59
  const backendJson = tfstate.backend;
43
60
  const type = backendJson?.type;
@@ -53,6 +70,10 @@ export class TerraformAdapter {
53
70
  }
54
71
  return key;
55
72
  }
73
+ async lockProviders(cwd) {
74
+ console.log('Update lock file');
75
+ await this.processRunner.run('terraform', ['providers', 'lock', '-platform=linux_amd64'], cwd);
76
+ }
56
77
  printTime() {
57
78
  const now = new Date();
58
79
  const utc = now.toISOString();
@@ -12,12 +12,11 @@ async function handler() {
12
12
  const answers = await inquirer.prompt([
13
13
  {
14
14
  type: 'input',
15
- name: 'owner',
16
- message: 'owner is required to deploy any env\n'
17
- + 'this value will be used as default - if not provided via --owner key in "deploy" command\n'
15
+ name: 'userName',
16
+ message: 'userName is required to deploy any env\n'
18
17
  + '(prefer GitHub username)\n'
19
- + 'owner:',
18
+ + 'userName:',
20
19
  },
21
20
  ]);
22
- configService.saveConfig({ owner: answers.owner });
21
+ configService.saveConfig({ userName: answers.userName });
23
22
  }
@@ -23,11 +23,11 @@ export class ConfigService {
23
23
  }
24
24
  return this.config;
25
25
  }
26
- getOwner() {
27
- const owner = this.loadConfig()?.owner;
28
- if (!owner) {
29
- throw new KnownException('please first set owner via calling \'envctl configure\'');
26
+ getUserName() {
27
+ const userName = this.loadConfig()?.userName;
28
+ if (!userName) {
29
+ throw new KnownException('please first set userName via calling \'envctl configure\'');
30
30
  }
31
- return owner;
31
+ return userName;
32
32
  }
33
33
  }
@@ -59,9 +59,9 @@ export class EnvCtl {
59
59
  checkInput(env, cwd) {
60
60
  if (env.ephemeral)
61
61
  return;
62
- const owner = this.configService.getOwner();
63
- if (env.owner !== owner) {
64
- throw new KnownException(`Can not change env owner ${env.owner} -> ${owner}`);
62
+ const userName = this.configService.getUserName();
63
+ if (env.owner !== userName) {
64
+ throw new KnownException(`Can not change env owner ${env.owner} -> ${userName}`);
65
65
  }
66
66
  if (env.kind) {
67
67
  const kind = this.cliHelper.getKind(cwd);
@@ -81,7 +81,7 @@ export class EnvCtl {
81
81
  }
82
82
  async plan(tfArgs, cwd) {
83
83
  const key = this.terraformAdapter.getKey(cwd);
84
- const owner = this.configService.getOwner();
84
+ const userName = this.configService.getUserName();
85
85
  const env = await this.tryGetEnv(key);
86
86
  let vars = undefined;
87
87
  if (env) {
@@ -93,7 +93,7 @@ export class EnvCtl {
93
93
  }
94
94
  vars = env.vars;
95
95
  }
96
- const envTerraform = { key, ephemeral: false, owner, args: tfArgs, vars };
96
+ const envTerraform = { key, ephemeral: false, owner: userName, args: tfArgs, vars };
97
97
  await this.terraformAdapter.plan(envTerraform, cwd);
98
98
  }
99
99
  async createEphemeral(key) {
@@ -104,25 +104,32 @@ export class EnvCtl {
104
104
  const createEnv = { key };
105
105
  return await this.envApi.createEphemeral(createEnv);
106
106
  }
107
+ async lockTerraform(env, cwd) {
108
+ await this.terraformAdapter.lockProviders(cwd);
109
+ env.stateFile = this.terraformAdapter.getStateFile(cwd);
110
+ env.lockFile = this.terraformAdapter.getLockFile(cwd);
111
+ }
107
112
  async deploy(tfArgs, cwd) {
108
113
  const key = this.terraformAdapter.getKey(cwd);
109
114
  let env = await this.tryGetEnv(key);
110
115
  if (env === null) {
111
116
  const kind = this.cliHelper.getKind(cwd);
112
117
  console.log('Creating env metadata');
113
- const owner = this.configService.getOwner();
114
- const createEnv = { key, owner, kind };
118
+ const userName = this.configService.getUserName();
119
+ const createEnv = { key, owner: userName, kind };
120
+ await this.lockTerraform(createEnv, cwd);
115
121
  env = await this.envApi.create(createEnv);
116
122
  }
117
123
  else {
118
124
  this.checkInput(env, cwd);
119
125
  this.checkStatus(env);
126
+ await this.lockTerraform(env, cwd);
120
127
  if (env.status === EnvStatus.Init) {
121
128
  await this.envApi.lockForUpdate(env);
122
129
  }
123
130
  else if (env.status == EnvStatus.Updating) {
124
- const answerYes = await this.cliHelper.promptYesNo(`Env status is ${env.status},
125
- likely to be an error from a previous run\nDo you want to proceed with deployment?`);
131
+ const answerYes = await this.cliHelper.promptYesNo(`Env status is ${env.status}, likely due to an error from a previous run\n
132
+ Do you want to proceed with deployment?`);
126
133
  if (!answerYes) {
127
134
  console.log('Aborting deployment');
128
135
  return;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agilecustoms/envctl",
3
3
  "description": "node.js CLI client for manage environments",
4
- "version": "0.28.2",
4
+ "version": "0.30.0",
5
5
  "author": "Alex Chekulaev",
6
6
  "type": "module",
7
7
  "bin": {