tfv 5.0.1 → 6.1.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.
Files changed (48) hide show
  1. package/.github/workflows/publish.yml +43 -3
  2. package/README.md +253 -122
  3. package/demo.gif +0 -0
  4. package/demo.tape +230 -0
  5. package/index.js +0 -4
  6. package/lib/commands/apply.js +8 -3
  7. package/lib/commands/current.js +25 -0
  8. package/lib/commands/destroy.js +8 -3
  9. package/lib/commands/doctor.js +20 -0
  10. package/lib/commands/exec.js +33 -0
  11. package/lib/commands/fmt.js +26 -0
  12. package/lib/commands/init.js +26 -0
  13. package/lib/commands/install.js +22 -13
  14. package/lib/commands/list.js +20 -11
  15. package/lib/commands/pin.js +26 -0
  16. package/lib/commands/plan.js +8 -3
  17. package/lib/commands/prune.js +41 -0
  18. package/lib/commands/remove.js +17 -12
  19. package/lib/commands/shell-init.js +25 -0
  20. package/lib/commands/switch.js +28 -7
  21. package/lib/commands/upgrade.js +26 -0
  22. package/lib/commands/use.js +17 -13
  23. package/lib/commands/validate.js +21 -0
  24. package/lib/modules/current.js +52 -0
  25. package/lib/modules/doctor.js +160 -0
  26. package/lib/modules/exec.js +36 -0
  27. package/lib/modules/install.js +155 -89
  28. package/lib/modules/list.js +66 -105
  29. package/lib/modules/pin.js +35 -0
  30. package/lib/modules/prune.js +100 -0
  31. package/lib/modules/ps1.js +37 -29
  32. package/lib/modules/remote.js +68 -15
  33. package/lib/modules/remove.js +35 -21
  34. package/lib/modules/shell-init.js +226 -0
  35. package/lib/modules/switch.js +125 -41
  36. package/lib/modules/terraform-command.js +49 -67
  37. package/lib/modules/upgrade.js +93 -0
  38. package/lib/modules/use.js +58 -54
  39. package/lib/utils/formatVersions.js +57 -5
  40. package/lib/utils/paths.js +156 -0
  41. package/lib/utils/postInstall.js +37 -13
  42. package/lib/utils/store.js +17 -6
  43. package/package.json +11 -9
  44. package/test/extractTargets.test.js +75 -0
  45. package/test/formatVersions.test.js +126 -0
  46. package/test/moduleImports.test.js +45 -0
  47. package/test/paths.test.js +69 -0
  48. package/test/versionResolution.test.js +92 -0
@@ -2,27 +2,67 @@ name: Publish tfv to npm registry
2
2
 
3
3
  on:
4
4
  push:
5
- branches: [main]
5
+ tags:
6
+ - 'v[0-9]+.[0-9]+.[0-9]+' # only trigger on semver tags: v6.0.0, v6.1.0, etc.
6
7
 
7
8
  permissions:
8
9
  contents: read
9
10
  id-token: write
10
11
 
11
12
  jobs:
12
- publish:
13
+ verify-branch:
13
14
  runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ with:
18
+ fetch-depth: 0
19
+
20
+ - name: Verify tag is on main branch
21
+ run: |
22
+ git fetch origin main
23
+ if ! git merge-base --is-ancestor ${{ github.sha }} origin/main; then
24
+ echo "Error: tags must only be pushed on commits that exist on the main branch."
25
+ exit 1
26
+ fi
14
27
 
28
+ test:
29
+ needs: verify-branch
30
+ runs-on: ubuntu-latest
15
31
  steps:
16
32
  - uses: actions/checkout@v4
17
33
 
18
34
  - uses: actions/setup-node@v4
19
35
  with:
20
- node-version: 18
36
+ node-version: 22
37
+ cache: 'npm'
38
+
39
+ - run: npm ci
40
+
41
+ - run: npm test
42
+
43
+ publish:
44
+ needs: test
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - uses: actions/checkout@v4
48
+
49
+ - uses: actions/setup-node@v4
50
+ with:
51
+ node-version: 22
21
52
  registry-url: https://registry.npmjs.org
22
53
  cache: 'npm'
23
54
 
24
55
  - run: npm ci
25
56
 
57
+ - name: Verify package version matches git tag
58
+ run: |
59
+ PKG_VERSION="v$(node -p "require('./package.json').version")"
60
+ GIT_TAG="${{ github.ref_name }}"
61
+ if [ "$PKG_VERSION" != "$GIT_TAG" ]; then
62
+ echo "Version mismatch: package.json=$PKG_VERSION, tag=$GIT_TAG"
63
+ exit 1
64
+ fi
65
+
26
66
  - run: npm publish --access public
27
67
  env:
28
68
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/README.md CHANGED
@@ -1,5 +1,6 @@
1
- # Use `tfv` to manage multiple versions of terraform with ease
1
+ # tfv Terraform & OpenTofu Version Manager for macOS, Linux & Windows
2
2
 
3
+ ```text
3
4
  _ ________ __
4
5
  _| |__ / _____|\ \ / /
5
6
  |_ ___\ | |___ \ \ / /
@@ -7,229 +8,359 @@
7
8
  | |___ | | \ \/ /
8
9
  \______\_| \__/
9
10
 
10
- Happy terraforming 😍🥂!
11
- ---------------------------------------
11
+ Happy terraforming! 😍🥂
12
+ ---------------------------------
13
+ ```
12
14
 
13
- ## Installation
15
+ `tfv` lets you install, switch, and manage multiple versions of **Terraform** and **OpenTofu** without `sudo`. All binaries live in `~/.tfv/` — upgrading `tfv` itself never wipes your installed versions.
14
16
 
15
- > **_NOTE:_** `tfv` should be installed `globally` so that it can be run from anywhere on your computer.
17
+ ![tfv demo](demo.gif)
18
+
19
+ ---
20
+
21
+ ## Installation
16
22
 
17
23
  ```sh
18
24
  npm install -g tfv
19
25
  ```
20
26
 
21
- Run with alias
27
+ `tfv` automatically adds `~/.tfv/bin` to your PATH on install (shell configs + Windows User PATH). Restart your terminal once after install.
22
28
 
23
- ```sh
24
- npm i -g tfv
25
- ```
29
+ ---
26
30
 
27
- ## Help
31
+ ## How it works
28
32
 
29
- ```sh
30
- tfv --help
33
+ ```text
34
+ npm install -g tfv
35
+
36
+
37
+ postInstall.js
38
+ ├── creates ~/.tfv/bin/, ~/.tfv/store/, ~/.tfv/cache/
39
+ └── adds ~/.tfv/bin to PATH (shell configs / Windows registry)
40
+
41
+
42
+ tfv install 1.9.0
43
+ ├── fetches version list → ~/.tfv/cache/terraform-versions.json (1hr TTL)
44
+ ├── downloads zip → system temp dir (with progress bar)
45
+ ├── verifies SHA256 → HashiCorp / OpenTofu checksums
46
+ ├── extracts binary only → ~/.tfv/store/terraform/1.9.0
47
+ └── records arch → ~/.tfv/store/terraform/arch.json
48
+
49
+
50
+ tfv use 1.9.0
51
+ ├── copies ~/.tfv/store/terraform/1.9.0 → ~/.tfv/bin/terraform
52
+ └── writes active version → ~/.tfv/active.json
53
+
54
+
55
+
56
+ terraform plan ← resolves to ~/.tfv/bin/terraform (same binary as tfv)
57
+ tfv plan ← spawns ~/.tfv/bin/terraform directly (no PATH lookup)
31
58
  ```
32
59
 
33
- Run with alias
34
-
35
- ```sh
36
- tfv -h
60
+ Both `terraform` and `tfv` commands always use the **exact same binary**.
61
+
62
+ ---
63
+
64
+ ## Store layout
65
+
66
+ ```text
67
+ ~/.tfv/
68
+ bin/
69
+ terraform ← active terraform binary (no sudo, no symlinks)
70
+ tofu ← active opentofu binary
71
+ store/
72
+ terraform/
73
+ 1.9.0 ← installed versions (binary renamed to version number)
74
+ 1.7.3
75
+ arch.json ← { "1.9.0": "arm64", "1.7.3": "amd64" }
76
+ opentofu/
77
+ 1.8.0
78
+ arch.json
79
+ cache/
80
+ terraform-versions.json ← remote list cached for 1 hour
81
+ opentofu-versions.json
82
+ active.json ← { "terraform": "1.9.0", "opentofu": null }
37
83
  ```
38
84
 
39
- > **_OUTPUT:_**
85
+ ---
40
86
 
41
- ```
42
- tfv <command>
87
+ ## Commands
43
88
 
44
- Commands:
89
+ ### Install
45
90
 
46
- tfv install <version> [option] Install a terraform version [aliases: i]
91
+ ```sh
92
+ tfv install latest # latest stable
93
+ tfv install 1.9.0 # exact version
94
+ tfv install 1.9.^ # latest 1.9.x patch
95
+ tfv install 1.8.0-beta1 # explicit pre-release (warns you)
96
+ tfv install latest --provider tofu # OpenTofu
97
+ tfv install 1.9.0 --arch amd64 # override architecture
98
+ ```
47
99
 
48
- tfv list [option] List installed or available terraform versions [aliases: ls]
100
+ Aliases: `tfv i`
49
101
 
50
- tfv remove <version> Remove terraform versions from tfv store [aliases: rm]
102
+ ---
51
103
 
52
- tfv auto-switch Auto-detect and switch to your project terraform version [aliases: as]
104
+ ### Switch version
53
105
 
54
- tfv use <version> Switch to a specified terraform version
106
+ ```sh
107
+ tfv use 1.9.0
108
+ tfv use latest # latest installed version
109
+ tfv use 1.8.0 --provider tofu
110
+ ```
55
111
 
56
- tfv apply Run terraform apply with optional file-based targets.
57
- Accepts all terraform flags after --
58
- Example:
59
- tfv apply --file main.tf --file network.tf -- -auto-approve -target=<TARGET> -var="env=prod"
112
+ No `sudo`. Copies binary to `~/.tfv/bin/terraform`.
60
113
 
61
- tfv destroy Run terraform destroy with optional file-based targets.
62
- Accepts all terraform flags after --
63
- Example:
64
- tfv destroy --file main.tf --file network.tf -- -auto-approve -target=<TARGET> -var="env=prod"
114
+ ---
65
115
 
66
- tfv plan Run terraform plan with optional file-based targets.
67
- Accepts all terraform flags after --
68
- Example:
69
- tfv plan --file main.tf --file network.tf -- -auto-approve -target=<TARGET> -var="env=prod"
116
+ ### Auto-switch
70
117
 
71
- Options:
72
- -h, --help Show help [boolean]
73
- -v, --version Show version number [boolean]
118
+ Detects and switches to the version your project requires. Installs it if not already in store.
119
+
120
+ ```sh
121
+ tfv auto-switch
122
+ tfv as # alias
123
+ tfv as --provider tofu
74
124
  ```
75
125
 
76
- # Usage
126
+ Reads version from (in priority order):
77
127
 
78
- https://github.com/marcdomain/tfv/assets/25563661/fa44f0f2-2dca-4f22-9fea-c74e4b8f767c
128
+ 1. `.terraform-version` file in current directory
129
+ 2. `terraform.tfstate` → `terraform_version` field
130
+ 3. `required_version` in any `.tf` file — supports all constraint operators:
131
+ `=`, `>=`, `>`, `<=`, `<`, `!=`, `~>`, and compound `">= 1.3, < 2.0"`
79
132
 
80
- # Table of Contents
133
+ ---
81
134
 
82
- <!--ts-->
83
- * [Table of Contents](#table-of-contents)
84
- * [Modules](#modules)
85
- * [install](#install)
86
- * [use](#use)
87
- * [list](#list)
88
- * [remove](#remove)
89
- * [auto-switch](#auto-switch)
90
- * [plan](#plan)
91
- * [apply](#apply)
92
- * [destroy](#destroy)
93
- <!--te-->
135
+ ### Shell hook — auto-switch on `cd`
94
136
 
95
- ### Modules
137
+ Wraps `cd` so that entering a directory automatically switches the terraform version — but **only if the directory looks like a terraform project**. Non-terraform folders have zero overhead (one filesystem check, no subprocess).
96
138
 
97
- - #### _INSTALL_
139
+ A directory is considered a terraform project if it contains:
98
140
 
99
- | Version | Description |
100
- | ---------------- | ------------------------------------------ |
101
- | x.x.x | Installs terraform version x.x.x |
102
- | x^ | Installs latest version of release x |
103
- | x.x.^ | Installs latest version of release x.x |
104
- | latest | Installs latest version of terraform |
141
+ - a `.terraform-version` file, **or**
142
+ - any `*.tf` files
105
143
 
106
144
  ```sh
107
- tfv install <version>
145
+ cd ~/Downloads # no .tf files → nothing happens
146
+ cd ~/projects/react-app # no .tf files → nothing happens
147
+ cd ~/projects/infra # has main.tf → auto-switch runs silently
148
+ cd ~/projects/platform # has .terraform-version → auto-switch runs silently
108
149
  ```
109
150
 
110
- Run with option
151
+ Setup (one-time, add to your shell config):
111
152
 
112
153
  ```sh
113
- tfv install <version> --arch <system-architecture>
114
- ```
154
+ # Bash
155
+ printf '\neval "$(tfv shell-init bash)"\n' >> ~/.bashrc
115
156
 
116
- EXAMPLE:
157
+ # Zsh
158
+ printf '\neval "$(tfv shell-init zsh)"\n' >> ~/.zshrc
117
159
 
118
- ```sh
119
- tfv install 1.5.7 -arch amd64
160
+ # Fish
161
+ printf '\ntfv shell-init fish | source\n' >> ~/.config/fish/config.fish
162
+
163
+ # PowerShell
164
+ Add-Content $PROFILE "`nInvoke-Expression (tfv shell-init powershell | Out-String)"
120
165
  ```
121
166
 
122
- > NOTE: The default *system-architecture* is the architecture of your computer (arm64, amd64, x64, etc...)
167
+ After setup, entering a terraform project switches the version automatically no manual `tfv as` needed.
123
168
 
124
- - #### _USE_
169
+ `shell-init` also installs **tab completions** for all `tfv` commands and installed versions. After running the setup command above, `tfv <tab>` will complete commands, `tfv use <tab>` will complete installed versions, and `tfv --provider <tab>` will complete provider names.
125
170
 
126
- | Version | Description |
127
- | ---------------- | ----------------------------------------- |
128
- | x.x.x | use terraform version x.x.x |
129
- | latest | use latest version of terraform |
171
+ ---
172
+
173
+ ### List versions
130
174
 
131
175
  ```sh
132
- tfv use <version>
176
+ tfv list # installed versions (default)
177
+ tfv ls --remote # all available versions from HashiCorp
178
+ tfv ls --remote --provider tofu # all available OpenTofu versions
133
179
  ```
134
180
 
135
- > **_NOTE:_** You would get a password prompt. Accept it. This is a one-time request to set the terraform executable in your system path.
181
+ Active version is marked with 🚀.
136
182
 
137
- - #### _LIST_
183
+ ---
138
184
 
139
- | Option | Option Alias | Description |
140
- | ---------------|---------------|--------------------------------------------------------------------------------|
141
- | | | Defaults to listing terraform versions installed locally (in tfv store) |
142
- | `--local` | `-l` | Lists all terraform versions installed locally |
143
- | `--remote` | `-r` | Lists all terraform versions available remotely, on terraform server |
185
+ ### Current version
144
186
 
145
187
  ```sh
146
- tfv list [option]
188
+ tfv current # shows active version + PATH status
189
+ tfv which # alias
190
+ tfv current --provider tofu
147
191
  ```
148
192
 
149
- Run with alias
193
+ Example output:
150
194
 
151
- ```sh
152
- tfv ls [option]
195
+ ```text
196
+ Active terraform version: 1.9.0
197
+ Binary: /Users/you/.tfv/bin/terraform
198
+ Reported: Terraform v1.9.0
199
+ PATH OK — 'terraform' resolves to tfv-managed binary
153
200
  ```
154
201
 
155
- - #### _REMOVE_
202
+ ---
156
203
 
157
- Remove terraform versions managed by tfv
204
+ ### Pin version
205
+
206
+ Writes a `.terraform-version` file in the current directory so teammates get the same version via `tfv auto-switch`:
158
207
 
159
208
  ```sh
160
- tfv remove <versions>
209
+ tfv pin # pin currently active version
210
+ tfv pin 1.9.0 # pin a specific version
211
+ tfv pin --provider tofu
161
212
  ```
162
213
 
163
- Run with alias
214
+ ---
215
+
216
+ ### Upgrade
164
217
 
165
218
  ```sh
166
- tfv rm <versions>
219
+ tfv upgrade # upgrade active 1.6.3 → latest 1.6.x patch
220
+ tfv upgrade 1.9 # install + use latest 1.9.x
221
+ tfv upgrade latest # install + use absolute latest
222
+ tfv upgrade --provider tofu
167
223
  ```
168
224
 
169
- Example
225
+ Also re-anchors `~/.tfv/bin` in PATH to ensure it takes precedence over any system-installed terraform.
226
+
227
+ ---
228
+
229
+ ### Remove
170
230
 
171
231
  ```sh
172
- tfv rm x.y.z z.x.y
232
+ tfv remove 1.7.3
233
+ tfv rm 1.7.3 1.6.6 # remove multiple
234
+ tfv rm 1.8.0 --provider tofu
173
235
  ```
174
236
 
175
- - #### _AUTO-SWITCH_
237
+ Warns if you're removing the currently active version.
176
238
 
177
- Auto-detects your project terraform version, downloads it if it's not in tfv store, and switch to the version
239
+ ---
178
240
 
179
- ```sh
180
- tfv auto-switch
181
- ```
241
+ ### Prune
182
242
 
183
- Run with alias
243
+ Remove all non-active versions at once to free disk space.
184
244
 
185
245
  ```sh
186
- tfv as
246
+ tfv prune # remove everything except the active version
247
+ tfv prune --keep 2 # keep the 2 most recent + the active version
248
+ tfv prune --provider tofu
249
+ tfv prune --yes # skip confirmation prompt
187
250
  ```
188
251
 
189
- - #### _PLAN_
252
+ The active version is always kept regardless of `--keep`.
190
253
 
191
- Run terraform plan with optional file-based targets. Parses terraform files to extract resources, data sources, and modules as targets.
254
+ ---
192
255
 
193
- ```sh
194
- tfv plan --file main.tf
195
- ```
256
+ ### Exec
196
257
 
197
- With multiple files
258
+ Run a single command with a specific installed version **without** changing the active version. Useful for cross-version testing and CI.
198
259
 
199
260
  ```sh
200
- tfv plan --file main.tf --file network.tf
261
+ tfv exec 1.9.0 -- version
262
+ tfv exec 1.8.0 -- plan -var="env=prod"
263
+ tfv exec 1.7.3 --provider tofu -- validate
201
264
  ```
202
265
 
203
- With extra terraform flags
266
+ Pass all terraform/tofu flags after `--`.
267
+
268
+ ---
269
+
270
+ ### Doctor
271
+
272
+ Run a full health check: store directories, active binaries, PATH order, PATH conflicts, and shell config.
204
273
 
205
274
  ```sh
206
- tfv plan --file main.tf -- -var="env=prod" -out=plan.out
275
+ tfv doctor
207
276
  ```
208
277
 
209
- - #### _APPLY_
278
+ Example output:
210
279
 
211
- Run terraform apply with optional file-based targets.
280
+ ```text
281
+ tfv doctor
212
282
 
213
- ```sh
214
- tfv apply --file main.tf
283
+ Store
284
+ ✔ ~/.tfv/store/terraform/ exists
285
+ ✔ ~/.tfv/store/opentofu/ exists
286
+
287
+ Active versions
288
+ ✔ terraform: active version is 1.9.0
289
+ ✔ terraform: binary exists
290
+ ✔ terraform: executes (Terraform v1.9.0)
291
+ – tofu: not set up (no versions installed)
292
+
293
+ PATH
294
+ ✔ ~/.tfv/bin is in PATH
295
+ ✔ ~/.tfv/bin precedes system dirs in PATH
296
+ ✔ 'terraform' resolves to tfv-managed binary
297
+ ✔ PATH block found in shell config (~/.zshrc)
298
+
299
+ Cache
300
+ ✔ terraform version cache exists
301
+ – opentofu version cache not yet created
302
+
303
+ All checks passed. tfv is healthy.
215
304
  ```
216
305
 
217
- With auto-approve
306
+ Exits with code 1 if any issue is found.
307
+
308
+ ---
309
+
310
+ ### Terraform commands (via tfv)
311
+
312
+ All commands use the tfv-managed binary and accept extra terraform flags after `--`:
218
313
 
219
314
  ```sh
220
- tfv apply --file main.tf -- -auto-approve
315
+ tfv init
316
+ tfv validate
317
+ tfv fmt
318
+ tfv fmt -- -recursive
319
+
320
+ tfv plan
321
+ tfv plan --file main.tf # extract targets from file
322
+ tfv plan --file main.tf --file network.tf # multiple files
323
+ tfv plan --file main.tf -- -var="env=prod" # with extra terraform flags
324
+
325
+ tfv apply --file main.tf -- -auto-approve
326
+ tfv destroy --file main.tf -- -auto-approve
221
327
  ```
222
328
 
223
- - #### _DESTROY_
329
+ `--file` parses `.tf` files and auto-generates `-target` flags for every `resource`, `data`, and `module` block found. Comments are stripped before parsing.
330
+
331
+ All commands support `--provider tofu` to use OpenTofu instead.
332
+
333
+ ---
224
334
 
225
- Run terraform destroy with optional file-based targets.
335
+ ## OpenTofu support
336
+
337
+ Every command works with OpenTofu via `--provider tofu` (or `--provider opentofu`):
226
338
 
227
339
  ```sh
228
- tfv destroy --file main.tf
340
+ tfv install latest --provider tofu
341
+ tfv use 1.8.0 --provider tofu
342
+ tfv list --remote --provider tofu
343
+ tfv current --provider tofu
344
+ tfv plan --provider tofu
229
345
  ```
230
346
 
231
- With auto-approve
347
+ OpenTofu binaries are stored separately from Terraform in `~/.tfv/store/opentofu/` and activated as `~/.tfv/bin/tofu`.
348
+
349
+ ---
350
+
351
+ ## Windows support
352
+
353
+ Everything works on Windows without administrator privileges:
354
+
355
+ - Store: `%USERPROFILE%\.tfv\`
356
+ - PATH: updated via `[Environment]::SetEnvironmentVariable("PATH", ..., "User")` (User scope, no admin)
357
+ - Shell hooks: PowerShell profile integration via `tfv shell-init powershell`
358
+
359
+ ---
360
+
361
+ ## Help
232
362
 
233
363
  ```sh
234
- tfv destroy --file main.tf -- -auto-approve
364
+ tfv --help
365
+ tfv <command> --help
235
366
  ```
package/demo.gif ADDED
Binary file