tfv 3.2.2 → 3.2.3

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.
package/README.md CHANGED
@@ -1,4 +1,5 @@
1
- ### Use `tfv` to manage multiple versions of terraform with ease.
1
+ # Use `tfv` to manage multiple versions of terraform with ease
2
+
2
3
  _ ________ __
3
4
  _| |__ / _____|\ \ / /
4
5
  |_ ___\ | |___ \ \ / /
@@ -10,14 +11,19 @@
10
11
  ---------------------------------------
11
12
 
12
13
  ## Installation
14
+
13
15
  > **_NOTE:_** `tfv` should be installed `globally` so that it can be run from anywhere on your computer.
16
+
14
17
  ```sh
15
18
  npm install -g tfv
16
19
  ```
17
- or
20
+
21
+ Run with alias
22
+
18
23
  ```sh
19
24
  npm i -g tfv
20
25
  ```
26
+
21
27
  # Table of Contents
22
28
 
23
29
  <!--ts-->
@@ -27,22 +33,28 @@ npm i -g tfv
27
33
  * [install](#install)
28
34
  * [use](#use)
29
35
  * [list](#list)
36
+ * [remove](#remove)
37
+ * [auto-switch](#auto-switch)
30
38
  <!--te-->
31
39
 
32
40
  ## Usage
33
41
 
34
- https://user-images.githubusercontent.com/25563661/142188036-4f2a8b65-1a3e-4298-95e0-9ed533c66a18.mp4
42
+ <https://user-images.githubusercontent.com/25563661/142188036-4f2a8b65-1a3e-4298-95e0-9ed533c66a18.mp4>
35
43
 
36
44
  ### Modules
37
45
 
38
- #### *install*
46
+ - #### _INSTALL_
47
+
39
48
  ```sh
40
49
  tfv install <version>
41
50
  ```
42
- or
51
+
52
+ Run with alias
53
+
43
54
  ```sh
44
55
  tfv i <version>
45
56
  ```
57
+
46
58
  | Version | Description |
47
59
  | ---------------- | ------------------------------------------ |
48
60
  | x.x.x | Installs terraform version x.x.x |
@@ -50,7 +62,8 @@ tfv i <version>
50
62
  | x.x.^ | Installs latest version of release x.x |
51
63
  | latest | Installs latest version of terraform |
52
64
 
53
- #### *use*
65
+ - #### _USE_
66
+
54
67
  ```sh
55
68
  tfv use <version>
56
69
  ```
@@ -62,16 +75,52 @@ tfv use <version>
62
75
 
63
76
  > **_NOTE:_** If you're using windows OS, you would be prompted for admin privilege. Accept it. This is a one-time request to set terraform location in you system path. Unix machines would also get password prompt, as this requires permission to copy terraform to your bin directory.
64
77
 
65
- #### *list*
78
+ - #### _LIST_
79
+
66
80
  ```sh
67
81
  tfv list [option]
68
82
  ```
69
- or
83
+
84
+ Run with alias
85
+
70
86
  ```sh
71
87
  tfv ls [option]
72
88
  ```
73
- | Option | Option Alias | Description |
74
- | ---------------|---------------|----------------------------------------------------------------------- |
75
- | `--local` | `-l` | Lists all terraform versions installed locally |
76
- | `--remote` | `-r` | Lists all terraform versions available remotely, on terraform server |
77
- | | | Defaults to listing terraform versions installed locally |
89
+
90
+ | Option | Option Alias | Description |
91
+ | ---------------|---------------|--------------------------------------------------------------------------------|
92
+ | | | Defaults to listing terraform versions installed locally (in tfv store) |
93
+ | `--local` | `-l` | Lists all terraform versions installed locally |
94
+ | `--remote` | `-r` | Lists all terraform versions available remotely, on terraform server |
95
+
96
+ - #### _REMOVE_
97
+
98
+ ```sh
99
+ tfv remove <version>
100
+ ```
101
+
102
+ Run with alias
103
+
104
+ ```sh
105
+ tfv rm <version>
106
+ ```
107
+
108
+ | Version | Description |
109
+ | ---------------- | ----------------------------------------- |
110
+ | x.x.x | remove terraform version x.x.x |
111
+
112
+ - #### _AUTO-SWITCH_
113
+
114
+ ```sh
115
+ tfv auto-switch
116
+ ```
117
+
118
+ Run with alias
119
+
120
+ ```sh
121
+ tfv as
122
+ ```
123
+
124
+ | Description |
125
+ | ---------------------------------------------------------------------------------------------------------------- |
126
+ | auto-detects your project terraform version, downloads it if it's not in tfv store, and switch to the version |
package/index.js CHANGED
@@ -6,6 +6,7 @@ const {join} = require('path')
6
6
  yargs
7
7
  .commandDir(join(__dirname, 'lib', 'commands'))
8
8
  .alias('help', 'h')
9
+ .alias('version', 'v')
9
10
  .scriptName('tfv')
10
11
  .demandCommand(1, 'You need at least one command before moving on')
11
12
  .strictOptions()
@@ -2,8 +2,6 @@
2
2
 
3
3
  const yargs = require('yargs');
4
4
  const {install} = require('../modules/install');
5
- const {fetchAllVersions} = require('../modules/remote');
6
- const {P_END, P_ERROR, P_OK} = require('../utils/colors');
7
5
 
8
6
  exports.command = 'install <version>'
9
7
  exports.aliases = ['i']
@@ -11,33 +9,13 @@ exports.desc = 'Example: tfv install 1.0.11'
11
9
  exports.builder = {
12
10
  'verbose': {
13
11
  describe: 'Produce detailed output',
14
- alias: 'v'
12
+ alias: 'v',
13
+ type: 'boolean',
15
14
  }
16
15
  }
17
16
 
18
17
  exports.handler = async () => {
19
- let [,version] = yargs.argv._;
20
- const result = await fetchAllVersions();
21
-
22
- if (version === 'latest') {
23
- [version] = result.filter(v => {
24
- const [, inTest] = v.split('-');
25
- if (!inTest) return v;
26
- });
27
- }
28
-
29
- if (`${version}`.endsWith('^')) {
30
- [version] = result.filter(v => {
31
- const [, inTest] = v.split('-');
32
- if (v.startsWith(version.replace('^', '')) && !inTest) return v
33
- });
34
- }
35
-
36
- if (!result.find(v => v === version)) {
37
- console.log(`${P_ERROR}Terraform ${version} not found.${P_END}`);
38
- console.log(`To view a list of available version, run ${P_OK}tfv list --remote${P_END}`);
39
- process.exit(1);
40
- }
18
+ const [,version] = yargs.argv._;
41
19
 
42
20
  await install(version);
43
21
  }
@@ -5,16 +5,17 @@ const {remove} = require('../modules/remove');
5
5
 
6
6
  exports.command = 'remove <version>'
7
7
  exports.aliases = ['rm']
8
- exports.desc = 'Example: tfv rm <version>'
8
+ exports.desc = 'Example: tfv rm 1.0.11'
9
9
  exports.builder = {
10
10
  'verbose': {
11
11
  describe: 'Produce detailed output',
12
- alias: 'v'
12
+ alias: 'v',
13
+ type: 'boolean',
13
14
  }
14
15
  }
15
16
 
16
17
  exports.handler = async () => {
17
- let [,version] = yargs.argv._;
18
+ const [,version] = yargs.argv._;
18
19
 
19
20
  await remove(version);
20
21
  }
@@ -0,0 +1,18 @@
1
+ 'use strict'
2
+
3
+ const {autoSwitch} = require('../modules/switch');
4
+
5
+ exports.command = 'auto-switch'
6
+ exports.aliases = ['as']
7
+ exports.desc = 'Example: tfv as'
8
+ exports.builder = {
9
+ 'verbose': {
10
+ describe: 'Produce detailed output',
11
+ alias: 'v',
12
+ type: 'boolean'
13
+ }
14
+ }
15
+
16
+ exports.handler = async () => {
17
+ await autoSwitch();
18
+ }
@@ -1,27 +1,20 @@
1
1
  'use strict'
2
2
 
3
3
  const yargs = require('yargs');
4
- const {existsSync} = require('fs');
5
- const {join} = require('path');
6
4
  const {use} = require('../modules/use');
7
- const {P_ERROR, P_END, P_OK} = require('../utils/colors');
8
5
 
9
6
  exports.command = 'use <version>'
10
7
  exports.desc = 'Example: tfv use 1.0.11'
11
8
  exports.builder = {
12
9
  verbose: {
13
10
  alias: 'v',
14
- describe: 'Produce detailed output'
11
+ describe: 'Produce detailed output',
12
+ type: 'boolean',
15
13
  }
16
14
  }
17
15
 
18
16
  exports.handler = async () => {
19
17
  const [,version] = yargs.argv._;
20
18
 
21
- if (!existsSync(join(__dirname, '../../', 'store'))) {
22
- console.log(`${P_ERROR}You're yet to install terraform with tfv${P_END}`);
23
- return console.log(`For guidance, run ${P_OK}tfv -h${P_END}`);
24
- }
25
-
26
19
  await use(version);
27
20
  }
@@ -4,11 +4,38 @@ const fs = require('fs');
4
4
  const {join} = require('path');
5
5
  const {arch} = process;
6
6
  const unzip = require('decompress');
7
+ const {fetchAllVersions} = require('./remote');
8
+ const {formatVersions} = require('../utils/formatVersions');
7
9
  const {P_END, P_ERROR, P_INFO, P_OK, P_WARN} = require('../utils/colors');
8
10
 
9
- exports.install = async (version) => {
11
+ exports.install = async (installVersion) => {
10
12
  try {
13
+ let version = installVersion;
11
14
  const store = join(__dirname, '../..', 'store');
15
+ const data = await fetchAllVersions();
16
+ const result = formatVersions(data);
17
+
18
+ if (version === 'latest') {
19
+ [version] = result.filter(v => {
20
+ const [, inTest] = v.split('-');
21
+ if (!inTest) return v;
22
+ });
23
+ }
24
+
25
+ if (`${version}`.endsWith('^')) {
26
+ [version] = result.filter(v => {
27
+ const [, inTest] = v.split('-');
28
+ if (v.startsWith(version.replace('^', '')) && !inTest) return v
29
+ });
30
+ version = version ? version : installVersion.replace('^', '');
31
+ }
32
+
33
+ if (!result.find(v => v === version)) {
34
+ console.log(`${P_ERROR}Terraform ${version} not found.${P_END}`);
35
+ console.log(`To view a list of available version, run ${P_OK}tfv list --remote${P_END}`);
36
+ process.exit(1);
37
+ }
38
+
12
39
  const fileName = join(__dirname, '../..', `${version}.zip`);
13
40
  const getVersion = os.platform() === 'win32' ? `${version}.exe` : version;
14
41
 
@@ -20,38 +47,12 @@ exports.install = async (version) => {
20
47
  const sysOs = os.platform() === 'win32' ? 'windows' : os.platform();
21
48
  let sysArch = arch === 'x64' ? 'amd64' : arch;
22
49
 
23
- // if (os.platform() === 'darwin') {
24
- // sysArch = 'amd64'
25
- // }
50
+ if (os.platform() === 'darwin' && version.startsWith('0')) {
51
+ sysArch = 'amd64'
52
+ }
26
53
 
27
54
  const url = `https://releases.hashicorp.com/terraform/${version}/terraform_${version}_${sysOs}_${sysArch}.zip`;
28
55
 
29
- const req = https.get(url, (res) => {
30
- console.log(`${P_INFO}Installing terraform ${version}${P_END}`);
31
-
32
- const fileStream = fs.createWriteStream(fileName);
33
- res.pipe(fileStream);
34
-
35
- fileStream.on('error', (err) => {
36
- console.log(`${P_ERROR}Error writing stream ${P_END}\n`, err);
37
- });
38
-
39
- fileStream.on('close', async () => {
40
- await clean(version);
41
- makeExecutable(version);
42
- });
43
-
44
- fileStream.on('finish', () => {
45
- fileStream.close();
46
- console.log(`${P_OK}Successful!${P_END}`);
47
- console.log(`To use this version Run: ${P_OK}tfv use ${version}${P_END}`);
48
- })
49
- });
50
-
51
- req.on('error', (err) => {
52
- console.log(`${P_ERROR}Error downloading the terraform ${version}${P_END}\n`, err)
53
- });
54
-
55
56
  const clean = async (version) => {
56
57
  if (fs.existsSync(fileName)) {
57
58
  await unzip(fileName, store, {
@@ -71,6 +72,36 @@ exports.install = async (version) => {
71
72
  fs.chmodSync(`${store}/${version}`, '755');
72
73
  }
73
74
  }
75
+
76
+ return new Promise((resolve, reject) => {
77
+ const req = https.get(url, (res) => {
78
+ console.log(`${P_INFO}Installing terraform ${version}${P_END}`);
79
+
80
+ const fileStream = fs.createWriteStream(fileName);
81
+ res.pipe(fileStream);
82
+
83
+ fileStream.on('error', (err) => {
84
+ console.log(`${P_ERROR}Error writing stream ${P_END}\n`, err);
85
+ });
86
+
87
+ fileStream.on('close', async () => {
88
+ await clean(version);
89
+ await makeExecutable(version);
90
+ resolve(version);
91
+ });
92
+
93
+ fileStream.on('finish', () => {
94
+ fileStream.close();
95
+ console.log(`${P_OK}Successful!${P_END}`);
96
+ console.log(`To use this version Run: ${P_OK}tfv use ${version}${P_END}`);
97
+ })
98
+ });
99
+
100
+ req.on('error', (err) => {
101
+ reject(console.log(`${P_ERROR}Error downloading the terraform ${version}${P_END}\n`, err))
102
+ });
103
+ });
104
+
74
105
  } catch ({message}) {
75
106
  console.log('ERROR:', message)
76
107
  }
@@ -2,7 +2,9 @@ const fs = require('fs');
2
2
  const {join} = require('path');
3
3
  const {spawnSync} = require('child_process')
4
4
  const {fetchAllVersions} = require('./remote');
5
- const {P_END, P_OK, P_ERROR} = require('../utils/colors');
5
+ const {formatVersions} = require('../utils/formatVersions');
6
+ const {P_END, P_OK} = require('../utils/colors');
7
+ const {checkStore} = require('../utils/store');
6
8
 
7
9
  exports.list = async (local, remote) => {
8
10
  try {
@@ -14,7 +16,8 @@ exports.list = async (local, remote) => {
14
16
  }
15
17
 
16
18
  if (remote) {
17
- versions = await fetchAllVersions();
19
+ const data = await fetchAllVersions();
20
+ versions = formatVersions(data);
18
21
 
19
22
  return result('List of all available terraform versions', versions)
20
23
  }
@@ -22,19 +25,16 @@ exports.list = async (local, remote) => {
22
25
  if (local) {
23
26
  const store = join(__dirname, '../..', 'store');
24
27
 
25
- if (!fs.existsSync(store)) {
26
- console.log(`${P_ERROR}You're yet to install terraform with tfv${P_END}`);
27
- return console.log(`For guidance, run ${P_OK}tfv -h${P_END}`);
28
- }
28
+ checkStore(store);
29
29
 
30
30
  const tfVersion = spawnSync('terraform', ['version'], {stdio: 'pipe', encoding: 'utf-8'});
31
- const pattern = /v\d\..*\d/;
32
- const [activeVersion] = tfVersion.stdout.match(pattern);
31
+ const pattern = /v\d+\.\d+\.\d+/;
32
+ const versionOutput = tfVersion.stdout.match(pattern);
33
33
 
34
34
  versions = fs.readdirSync(store).map(f => {
35
35
  const versionNumber = f.replace('.exe', '');
36
36
 
37
- if (versionNumber === activeVersion.replace('v', '')) return `${P_OK}*${P_END}${versionNumber}`;
37
+ if (versionOutput && versionNumber === versionOutput[0].replace('v', '')) return `${versionNumber} 🚀`;
38
38
  else return versionNumber;
39
39
  });
40
40
 
@@ -1,29 +1,18 @@
1
- const client = require('https');
1
+ const https = require('https');
2
2
 
3
- const getAvailableVersions = (url) => {
4
- return new Promise((resolve, reject) => {
5
- client.get(url, (resp) => {
6
- let data = '';
7
-
8
- // A chunk of data has been received.
9
- resp.on('data', (chunk) => {
10
- data += chunk;
11
- });
3
+ exports.fetchAllVersions = async () => {
4
+ try {
5
+ return new Promise((resolve, reject) => {
6
+ https.get('https://releases.hashicorp.com/terraform/', (resp) => {
7
+ let data = '';
12
8
 
13
- // The whole response has been received. Print out the result.
14
- resp.on('end', () => {
15
- resolve(data);
9
+ resp.on('data', (chunk) => data += chunk);
10
+ resp.on('end', () => resolve(data));
11
+ }).on("error", (err) => {
12
+ reject(err);
16
13
  });
17
- }).on("error", (err) => {
18
- reject(err);
19
14
  });
20
- });
21
- };
22
-
23
- exports.fetchAllVersions = async () => {
24
- const content = await getAvailableVersions('https://releases.hashicorp.com/terraform/');
25
- const pattern = /(>terraform_).*(?=<\/a>)/g;
26
- const versions = content.match(pattern).map(v => v.replace('>terraform_', ''));
27
-
28
- return versions;
15
+ } catch ({message}) {
16
+ console.log('ERROR:', message);
17
+ }
29
18
  };
@@ -0,0 +1,60 @@
1
+ const fs = require('fs');
2
+ const {join} = require('path');
3
+ const {use} = require('./use');
4
+ const {install} = require('./install');
5
+ const {P_END, P_ERROR, P_INFO} = require('../utils/colors');
6
+ const {checkStore} = require('../utils/store');
7
+
8
+ exports.autoSwitch = async () => {
9
+ try {
10
+ let versionFile;
11
+ let tfVersion;
12
+ const tfState = `terraform.tfstate`;
13
+
14
+ if (fs.existsSync(tfState)) {
15
+ versionFile = tfState;
16
+ tfVersion = JSON.parse(fs.readFileSync(tfState, 'utf-8'))['terraform_version'];
17
+ } else {
18
+ const getAllFiles = fs.readdirSync(process.cwd());
19
+ const likelyVersionFiles = ['main.tf', 'provider.tf', 'providers.tf', 'versions.tf', 'version.tf', 'backend.tf', 'terraform.tf'];
20
+ const existingFiles = getAllFiles.filter(f => likelyVersionFiles.indexOf(f) !== -1);
21
+ const checkVersionIn = existingFiles.concat(getAllFiles.filter(f => existingFiles.indexOf(f) === -1)).filter(f => f.endsWith('.tf'));
22
+
23
+ for (let i = 0; i < checkVersionIn.length; i++) {
24
+ versionFile = checkVersionIn[i];
25
+ const requiredVersion = fs.readFileSync(versionFile, 'utf-8');
26
+ const pattern = /^([\s]{0,}required_version)[\s]{0,}=[\s]{0,}"(>=|=|>|~>)[\s]{0,}\d+.*"/m;
27
+ const specified = requiredVersion.match(pattern);
28
+
29
+ if (specified) {
30
+ const vPattern = /\d+(\.\d+)?(\.\d+)?/;
31
+ [tfVersion] = specified[0].trim().match(vPattern);
32
+ break;
33
+ }
34
+ }
35
+ }
36
+
37
+ if (tfVersion) {
38
+ console.log('required_version was found in:', versionFile);
39
+ const store = join(__dirname, '../..', 'store');
40
+
41
+ checkStore(store);
42
+
43
+ const inStore = fs.readdirSync(store).find(v => v.startsWith(tfVersion));
44
+
45
+ if (inStore) {
46
+ await use(inStore);
47
+ } else {
48
+ const version = await install(`${tfVersion}^`);
49
+ await use(version);
50
+ }
51
+ } else {
52
+ console.log(`${P_ERROR}NOT FOUND:${P_END}${P_INFO}required_version${P_END}`);
53
+ console.log(`${P_INFO}check in your remote state file${P_END}`);
54
+ }
55
+
56
+ } catch ({message}) {
57
+ console.log('ERROR:', message)
58
+ }
59
+ }
60
+
@@ -4,13 +4,18 @@ const {spawnSync} = require('child_process');
4
4
  const {readdirSync, mkdirSync, copyFileSync, existsSync} = require('fs');
5
5
  const {setWindowsTerraform} = require('./ps1');
6
6
  const {P_END, P_OK, P_INFO, P_ERROR} = require('../utils/colors');
7
+ const { checkStore } = require('../utils/store');
7
8
 
8
9
  exports.use = async (tfVer) => {
9
10
  try {
11
+ const store = join(__dirname, '../..', 'store');
12
+
13
+ checkStore(store);
14
+
10
15
  let version = os.platform() === 'win32' ? `${tfVer}.exe` : tfVer;
11
16
 
12
17
  if (tfVer === 'latest') {
13
- [version] = readdirSync(join(__dirname, '../../store')).sort((a, b) => {
18
+ [version] = readdirSync(store).sort((a, b) => {
14
19
  let aVer = a.replace(/(.exe)/g, '');
15
20
  let bVer = b.replace(/(.exe)/g, '');
16
21
 
@@ -32,9 +37,9 @@ exports.use = async (tfVer) => {
32
37
  console.log(`${P_INFO}Your latest installed version is ${version}${P_END}`);
33
38
  }
34
39
 
35
- console.log(`${P_INFO}Switching to terraform ${version}${P_END}`);
40
+ console.log(`${P_INFO}Switching to terraform ${version.replace('.exe', '')}${P_END}`);
36
41
 
37
- const source = join(__dirname, '../../store', version);
42
+ const source = `${store}/${version}`;
38
43
 
39
44
  if (!existsSync(source)) {
40
45
  console.log(`${P_ERROR}terraform ${version} is not installed${P_END}`);
@@ -0,0 +1,6 @@
1
+ exports.formatVersions = (content) => {
2
+ const pattern = /(>terraform_).*(?=<\/a>)/g;
3
+ const versions = content.match(pattern).map(v => v.replace('>terraform_', ''));
4
+
5
+ return versions;
6
+ }
@@ -1,4 +1,4 @@
1
- const {P_END, P_OK, P_INFO, P_ERROR} = require('./colors');
1
+ const {P_END, P_OK} = require('./colors');
2
2
 
3
3
  const message = `\n\
4
4
  _ ________ __\n\
@@ -0,0 +1,10 @@
1
+ const fs = require('fs');
2
+ const {P_END, P_OK, P_ERROR} = require('./colors');
3
+
4
+ exports.checkStore = (store) => {
5
+ if (!fs.existsSync(store)) {
6
+ console.log(`${P_ERROR}You're yet to install terraform with tfv${P_END}`);
7
+ console.log(`For guidance, run ${P_OK}tfv -h${P_END}`);
8
+ process.exit(1);
9
+ }
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tfv",
3
- "version": "3.2.2",
3
+ "version": "3.2.3",
4
4
  "description": "Terraform version manager",
5
5
  "main": "index.js",
6
6
  "directories": {