scratchrelaxtv 0.7.0__py3-none-any.whl

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.
scratchrelaxtv/cli.py ADDED
@@ -0,0 +1,209 @@
1
+ """scratchrelaxtv command line interface (CLI).
2
+
3
+ scratchrelaxtv (anagram of extract-hcl-vars) is a Terraform development
4
+ convenience tool that extracts vars from HCL and generates an HCL variables
5
+ file with the extracted vars. The point is to make creating Terraform modules
6
+ easier.
7
+
8
+ Running scratchrelaxtv without any options reads a main.tf, extracts the vars
9
+ from that file, and generates a variables.tf file with those variables.
10
+
11
+ Options include:
12
+ -i, -o changing input/output file names
13
+ -f force overwriting the output file
14
+ -a, -d sort ascending, descending (omit to preserve original order)
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import argparse
20
+ import contextlib
21
+ import copy
22
+ import errno
23
+ import logging
24
+ import os
25
+ import sys
26
+ from collections.abc import Sequence
27
+
28
+ import scratchrelaxtv
29
+
30
+
31
+ def parse_args(args: Sequence[str] | None) -> argparse.Namespace:
32
+ """Parse args list."""
33
+ parser = argparse.ArgumentParser(prog="scratchrelaxtv")
34
+ parser.add_argument(
35
+ "-i",
36
+ "--input",
37
+ help="file to read (default: all *.tf files in the current directory; "
38
+ "template.sh with --template)",
39
+ )
40
+ parser.add_argument("-o", "--output", help="file to write to")
41
+ parser.add_argument(
42
+ "-f",
43
+ "--force",
44
+ default=False,
45
+ action="store_true",
46
+ help="overwrite existing output file (with -c: add missing declarations)",
47
+ )
48
+
49
+ task = parser.add_mutually_exclusive_group()
50
+ task.add_argument(
51
+ "-m",
52
+ "--modstub",
53
+ default=False,
54
+ action="store_true",
55
+ help="generate module usage stub",
56
+ )
57
+ parser.add_argument("-n", "--modname", help="name to use in module stub")
58
+ task.add_argument(
59
+ "-r",
60
+ "--remove",
61
+ default=False,
62
+ action="store_true",
63
+ help="remove all modstub.tf, variables.#.tf files",
64
+ )
65
+ task.add_argument(
66
+ "-c",
67
+ "--check",
68
+ default=False,
69
+ action="store_true",
70
+ help="report undeclared and unused variables and exit 1 if any; "
71
+ "with -f, also add missing declarations",
72
+ )
73
+ task.add_argument(
74
+ "-e",
75
+ "--env",
76
+ default=False,
77
+ action="store_true",
78
+ help="generate .env with Terraform vars",
79
+ )
80
+ task.add_argument(
81
+ "-t",
82
+ "--tfvars",
83
+ default=False,
84
+ action="store_true",
85
+ help="generate .tfvars with Terraform vars",
86
+ )
87
+ task.add_argument(
88
+ "--template",
89
+ default=False,
90
+ action="store_true",
91
+ help="generate .tf from Terraform template vars",
92
+ )
93
+
94
+ sort_order = parser.add_mutually_exclusive_group()
95
+ sort_order.add_argument(
96
+ "-a",
97
+ "--asc",
98
+ action="store_true",
99
+ help="sort output variables in ascending order",
100
+ )
101
+ sort_order.add_argument(
102
+ "-d",
103
+ "--desc",
104
+ action="store_true",
105
+ help="sort output variables in descending order",
106
+ )
107
+
108
+ parser.add_argument(
109
+ "-v",
110
+ "--verbose",
111
+ action="store_true",
112
+ help="log each step, not only warnings",
113
+ )
114
+ parser.add_argument(
115
+ "--version",
116
+ action="version",
117
+ version=f"%(prog)s {scratchrelaxtv.__version__}",
118
+ )
119
+ parser.add_argument(
120
+ "paths",
121
+ nargs="*",
122
+ metavar="PATH",
123
+ help="with -c: module directories or .tf files to check; a file means its "
124
+ "directory (default: the current directory)",
125
+ )
126
+
127
+ parsed = parser.parse_args(args)
128
+ if parsed.paths and not parsed.check:
129
+ parser.error("PATH arguments are only supported with -c")
130
+ if parsed.paths and parsed.input:
131
+ parser.error("-i cannot be combined with PATH arguments")
132
+ return parsed
133
+
134
+
135
+ def _configure_logging(verbose: bool) -> None:
136
+ """Send warnings (or, with -v, every step) to stderr."""
137
+ handler = logging.StreamHandler(sys.stderr)
138
+ if verbose:
139
+ handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
140
+ else:
141
+ handler.setFormatter(logging.Formatter("scratchrelaxtv: %(message)s"))
142
+ logger = scratchrelaxtv.logger
143
+ logger.handlers.clear()
144
+ logger.addHandler(handler)
145
+ logger.setLevel(logging.INFO if verbose else logging.WARNING)
146
+ logger.propagate = False
147
+
148
+
149
+ def _module_dirs(paths: list[str]) -> list[str]:
150
+ """Return the unique module directories for paths, in sorted order.
151
+
152
+ Raises FileNotFoundError for a path that doesn't exist, so a mistyped module
153
+ path fails instead of checking (or, with -f, changing) another module.
154
+ """
155
+ for path in paths:
156
+ if not os.path.exists(path):
157
+ raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), path)
158
+ dirs = {path if os.path.isdir(path) else os.path.dirname(path) or "." for path in paths}
159
+ return sorted(dirs)
160
+
161
+
162
+ def _check(args: argparse.Namespace) -> int:
163
+ """Check each module directory and return 1 if any has a problem."""
164
+ if not args.paths:
165
+ return scratchrelaxtv.Checker(args).extract()
166
+
167
+ status = scratchrelaxtv.EXIT_OKAY
168
+ for directory in _module_dirs(args.paths):
169
+ with contextlib.chdir(directory):
170
+ checker = scratchrelaxtv.Checker(copy.copy(args), label=directory)
171
+ status = max(status, checker.extract())
172
+ return status
173
+
174
+
175
+ def _run(args: argparse.Namespace) -> int:
176
+ """Run the task selected by args and return its exit code."""
177
+ if args.remove:
178
+ scratchrelaxtv.remove_files()
179
+ return scratchrelaxtv.EXIT_OKAY
180
+
181
+ extractor: scratchrelaxtv.BassExtractor
182
+ if args.modstub:
183
+ extractor = scratchrelaxtv.StubMaker(args)
184
+ elif args.check:
185
+ return _check(args)
186
+ elif args.env or args.tfvars:
187
+ extractor = scratchrelaxtv.EnvGenerator(args)
188
+ elif args.template:
189
+ extractor = scratchrelaxtv.TemplateExtractor(args)
190
+ else:
191
+ extractor = scratchrelaxtv.VarExtractor(args)
192
+
193
+ return extractor.extract()
194
+
195
+
196
+ def main(argv: Sequence[str] | None = None) -> int:
197
+ """Entry point for scratchrelaxtv CLI."""
198
+ args = parse_args(sys.argv[1:] if argv is None else argv)
199
+ _configure_logging(args.verbose)
200
+
201
+ try:
202
+ return _run(args)
203
+ except OSError as exc:
204
+ print(f"scratchrelaxtv: {exc}", file=sys.stderr)
205
+ return scratchrelaxtv.EXIT_NOT_OKAY
206
+
207
+
208
+ if __name__ == "__main__":
209
+ sys.exit(main())
File without changes
@@ -0,0 +1,368 @@
1
+ Metadata-Version: 2.5
2
+ Name: scratchrelaxtv
3
+ Version: 0.7.0
4
+ Summary: Terraform module development tool to extract variables and generate variables.tf files
5
+ Project-URL: Homepage, https://github.com/YakDriver/scratchrelaxtv
6
+ Project-URL: Issues, https://github.com/YakDriver/scratchrelaxtv/issues
7
+ Project-URL: Changelog, https://github.com/YakDriver/scratchrelaxtv/blob/main/CHANGELOG.md
8
+ Author-email: YakDriver <projects@plus3it.com>
9
+ License-Expression: Apache-2.0
10
+ License-File: LICENSE
11
+ Keywords: devops,hcl,module,terraform,variables
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: MacOS :: MacOS X
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Programming Language :: Python :: 3.15
24
+ Classifier: Programming Language :: Python :: Implementation :: CPython
25
+ Classifier: Topic :: Utilities
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.11
28
+ Provides-Extra: dev
29
+ Requires-Dist: bump-my-version==1.6.7; extra == 'dev'
30
+ Requires-Dist: mypy==2.4.1; extra == 'dev'
31
+ Requires-Dist: pytest-cov==7.1.0; extra == 'dev'
32
+ Requires-Dist: pytest==9.2.0; extra == 'dev'
33
+ Requires-Dist: ruff==0.17.3; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
36
+ ![scratch relax tv](https://github.com/YakDriver/scratchrelaxtv/raw/main/assets/srt.gif "Extract HCL Vars")
37
+
38
+ <p>
39
+ <a href="./LICENSE" alt="License">
40
+ <img src="https://img.shields.io/github/license/YakDriver/scratchrelaxtv.svg" /></a>
41
+ <a href="https://github.com/YakDriver/scratchrelaxtv/actions/workflows/ci.yml" alt="Build status">
42
+ <img src="https://github.com/YakDriver/scratchrelaxtv/actions/workflows/ci.yml/badge.svg?branch=main" /></a>
43
+ <a href="https://pypi.python.org/pypi/scratchrelaxtv" alt="Python versions">
44
+ <img src="https://img.shields.io/pypi/pyversions/scratchrelaxtv.svg" /></a>
45
+ <a href="https://pypi.python.org/pypi/scratchrelaxtv" alt="Version">
46
+ <img src="https://img.shields.io/pypi/v/scratchrelaxtv.svg" /></a>
47
+ </p>
48
+
49
+ Terraform module development tool.
50
+
51
+ 1. Extract the variables a module uses and generate a `variables.tf` file
52
+ 1. Check that a module declares every variable it uses and uses every variable it declares, from the command line or as a [pre-commit](https://pre-commit.com) hook
53
+ 1. Generate a module use stub from a module's variable declarations
54
+ 1. Generate a .env or `.tfvars` file with the variables a module uses
55
+ 1. Generate HCL with template variables corresponding to variables from a Terraform template
56
+ 1. Delete extra *scratchrelaxtv* files
57
+
58
+ *scratchrelaxtv* reads every `*.tf` file in the current directory, so modules split across `main.tf`, `locals.tf`, and other files are handled as a whole. Use `-i` to read a single file instead. Comments are ignored, so a commented-out `var.x` doesn't count as a use.
59
+
60
+ # install
61
+
62
+ ```
63
+ pipx install scratchrelaxtv
64
+ ```
65
+
66
+ Or run it without installing, using [uv](https://docs.astral.sh/uv/):
67
+
68
+ ```
69
+ uvx scratchrelaxtv --help
70
+ ```
71
+
72
+ `pip install scratchrelaxtv` also works. *scratchrelaxtv* requires Python 3.11 or newer.
73
+
74
+ # pre-commit
75
+
76
+ Add *scratchrelaxtv* to `.pre-commit-config.yaml` to check every module that has changed `.tf` files:
77
+
78
+ ```yaml
79
+ repos:
80
+ - repo: https://github.com/YakDriver/scratchrelaxtv
81
+ rev: 0.7.0
82
+ hooks:
83
+ - id: relaxtv-check # report undeclared and unused variables
84
+ # - id: relaxtv-fix # also add declarations for undeclared variables
85
+ ```
86
+
87
+ Both hooks fail when a module has an undeclared or unused variable. `relaxtv-fix` adds the missing declarations to the module's `variables.tf`, so the next run passes unless unused variables remain. Remove those yourself.
88
+
89
+ # tips
90
+
91
+ Once installed, you can run *scratchrelaxtv* by typing either `relaxtv` or `scratchrelaxtv`. You can also run it as `python -m scratchrelaxtv`.
92
+
93
+ By default, *scratchrelaxtv* only prints warnings. Add `-v` to log each step.
94
+
95
+ Find out more about Terraform workflows and using *scratchrelaxtv* [here](https://medium.com/@dirk.avery/terraform-secure-simple-sweet-development-workflow-d7188d33d9cf).
96
+
97
+ # workflows
98
+
99
+ Here are two example workflows using *scratchrelaxtv*.
100
+
101
+ **Original module development**:
102
+ 1. Write `main.tf` with whatever variables you need
103
+ 1. Run *scratchrelaxtv* to generate `variables.tf`
104
+ 1. Fill in descriptions, defaults, etc. in `variables.tf`
105
+ 1. Run `terraform fmt` to prettify everything
106
+
107
+ **Cleanup module**:
108
+ 1. Run `relaxtv -c` in the module's directory to find undeclared and unused variables
109
+ 1. Run `relaxtv -cf` to automatically add declarations for undeclared variables to `variables.tf`
110
+ 1. Fill in descriptions, defaults, etc. in `variables.tf` for newly added vars
111
+ 1. Run `terraform fmt` to prettify everything
112
+
113
+ # examples
114
+
115
+ ## example: generate `variables.tf`
116
+
117
+ By default, *scratchrelaxtv* reads every `*.tf` file in the current directory and generates a `variables.tf` file. (Use `-i` to read a single file.) Variables are in order of first use, reading files in alphabetical order. You can sort the variables using the `-a` (ascending) and `-d` (descending) options. You can also `--force` to overwrite an existing `variables.tf` file. Otherwise, *scratchrelaxtv* will generate new `variables.tf` files with each run: `variables.1.tf`, `variables.2.tf` and so on.
118
+
119
+ Assume this `main.tf`:
120
+ ```hcl
121
+ resource "aws_s3_bucket" "this" {
122
+ count = var.create_bucket ? 1 : 0
123
+ bucket = var.bucket
124
+ region = var.region
125
+ }
126
+ ```
127
+
128
+ Run *scratchrelaxtv*:
129
+ ```console
130
+ $ relaxtv
131
+ ```
132
+
133
+ The generated `variables.tf`:
134
+ ```hcl
135
+ variable "create_bucket" {
136
+ description = ""
137
+ type = any
138
+ }
139
+
140
+ variable "bucket" {
141
+ description = ""
142
+ type = any
143
+ }
144
+
145
+ variable "region" {
146
+ description = ""
147
+ type = any
148
+ }
149
+ ```
150
+
151
+ ## example: find and fix missing variables
152
+
153
+ Assume you already have a `main.tf` and a `variables.tf`. In this example, the `variables.tf` is missing the `region` variable. `relaxtv -c` reports undeclared and unused variables and exits with status 1 if it finds any. Add `-f` to also add declarations for the undeclared ones.
154
+
155
+ `main.tf`:
156
+ ```hcl
157
+ resource "aws_s3_bucket" "this" {
158
+ bucket = var.bucket
159
+ region = var.region
160
+ }
161
+ ```
162
+
163
+ `variables.tf`:
164
+ ```hcl
165
+ variable "bucket" {
166
+ description = "The bucket where the stuff will be stored"
167
+ type = string
168
+ default = ""
169
+ }
170
+ ```
171
+
172
+ Run *scratchrelaxtv* to automatically add any missing variables:
173
+
174
+ ```console
175
+ $ relaxtv -cf
176
+ scratchrelaxtv: .: undeclared variables: region
177
+ scratchrelaxtv: .: added declarations to variables.tf
178
+ ```
179
+
180
+ Now, the `variables.tf` looks like this:
181
+ ```hcl
182
+ variable "bucket" {
183
+ description = "The bucket where the stuff will be stored"
184
+ type = string
185
+ default = ""
186
+ }
187
+
188
+ variable "region" {
189
+ description = ""
190
+ type = any
191
+ }
192
+ ```
193
+
194
+ ## example: generate a stub for using the module
195
+
196
+ By default, when generating a stub, *scratchrelaxtv* reads the variable declarations in every `*.tf` file in the current directory.
197
+
198
+ Assume this `variables.tf`:
199
+ ```hcl
200
+ variable "id" {
201
+ description = "The ID of the resource"
202
+ type = string
203
+ default = ""
204
+ }
205
+
206
+ variable "bucket" {
207
+ description = "The bucket where the stuff will be stored"
208
+ type = string
209
+ default = ""
210
+ }
211
+
212
+ variable "region" {
213
+ description = "The AWS region where the bucket lives"
214
+ type = string
215
+ default = ""
216
+ }
217
+ ```
218
+
219
+ Run *scratchrelaxtv* with the module stub option:
220
+ ```console
221
+ $ relaxtv -m
222
+ ```
223
+
224
+ The generated `modstub.tf`:
225
+ ```hcl
226
+ module "tests2" {
227
+ source = "../tests2"
228
+
229
+ id = local.id
230
+ bucket = local.bucket
231
+ region = local.region
232
+ }
233
+ ```
234
+
235
+ ## example: generate a `.tfvars` file
236
+
237
+ By default, when generating a `.tfvars` file, *scratchrelaxtv* reads every `*.tf` file in the current directory.
238
+
239
+ Assume this `main.tf`:
240
+ ```hcl
241
+ resource "aws_s3_bucket" "this" {
242
+ bucket = var.bucket
243
+ region = var.region
244
+ }
245
+ ```
246
+
247
+ Run *scratchrelaxtv* with the generate `.tfvars` and sort-ascending options:
248
+ ```console
249
+ $ relaxtv -ta
250
+ ```
251
+
252
+ The generated `terraform.tfvars` file:
253
+ ```
254
+ bucket = "replace"
255
+ region = "replace"
256
+ ```
257
+
258
+ ## example: generate a `.tf` file from Terraform template
259
+
260
+ By default, when using template mode, *scratchrelaxtv* looks for `template.sh`. (Use the `-i` to specify a different input file.)
261
+
262
+ Assume this `template.sh`:
263
+ ```bash
264
+ #!/bin/bash
265
+
266
+ build_os="${build_os}"
267
+ build_type="${build_type}"
268
+ ```
269
+
270
+ Run *scratchrelaxtv* with the `--template` and sort-ascending options:
271
+ ```console
272
+ $ relaxtv -a --template
273
+ ```
274
+
275
+ The generated `template_vars.tf` file:
276
+ ```hcl
277
+ locals {
278
+ templates_vars = {
279
+ build_os = "replace"
280
+ build_type = "replace"
281
+ }
282
+ }
283
+ ```
284
+
285
+ ## example: generate a `.env` (dotenv) file
286
+
287
+ By default, when generating a `.env` file, *scratchrelaxtv* reads every `*.tf` file in the current directory. (Use `-i` to read a single file.)
288
+
289
+ Assume this `main.tf`:
290
+ ```hcl
291
+ resource "aws_s3_bucket" "this" {
292
+ bucket = var.bucket
293
+ region = var.region
294
+ }
295
+ ```
296
+
297
+ Run *scratchrelaxtv* with the generate `.env` and sort-ascending options:
298
+ ```console
299
+ $ relaxtv -ea
300
+ ```
301
+
302
+ The generated `.env`:
303
+ ```bash
304
+ unset "${!TF_VAR_@}"
305
+ export TF_VAR_bucket=replace
306
+ export TF_VAR_region=replace
307
+ ```
308
+
309
+ ## example: remove files
310
+
311
+ ```console
312
+ $ relaxtv -r
313
+ ```
314
+
315
+ *scratchrelaxtv* can also tidy up your directories by removing its own extra generated files. Presumably it will only remove files you no longer need but *be careful*. This chart shows examples of what would be deleted or not.
316
+
317
+ **NOTE**: *scratchrelaxtv* removes files in the current directory _and subdirectories_.
318
+
319
+ | Filename | Deleted? |
320
+ | -------- | ------ |
321
+ | variables.tf | no |
322
+ | modstub.tf | yes |
323
+ | modstub.1.tf | yes |
324
+ | variables.1.tf | yes |
325
+ | xyz.abc | no |
326
+ | variables.a.tf | no |
327
+ | variables.43.tf | yes |
328
+ | modstub | no |
329
+ | modstub..tf | no |
330
+
331
+ # help
332
+
333
+ *scratchrelaxtv* includes help:
334
+
335
+ ```console
336
+ $ relaxtv -h
337
+ usage: scratchrelaxtv [-h] [-i INPUT] [-o OUTPUT] [-f] [-m] [-n MODNAME] [-r]
338
+ [-c] [-e] [-t] [--template] [-a | -d] [-v] [--version]
339
+ [PATH ...]
340
+
341
+ positional arguments:
342
+ PATH with -c: module directories or .tf files to check; a
343
+ file means its directory (default: the current
344
+ directory)
345
+
346
+ options:
347
+ -h, --help show this help message and exit
348
+ -i INPUT, --input INPUT
349
+ file to read (default: all *.tf files in the current
350
+ directory; template.sh with --template)
351
+ -o OUTPUT, --output OUTPUT
352
+ file to write to
353
+ -f, --force overwrite existing output file (with -c: add missing
354
+ declarations)
355
+ -m, --modstub generate module usage stub
356
+ -n MODNAME, --modname MODNAME
357
+ name to use in module stub
358
+ -r, --remove remove all modstub.tf, variables.#.tf files
359
+ -c, --check report undeclared and unused variables and exit 1 if
360
+ any; with -f, also add missing declarations
361
+ -e, --env generate .env with Terraform vars
362
+ -t, --tfvars generate .tfvars with Terraform vars
363
+ --template generate .tf from Terraform template vars
364
+ -a, --asc sort output variables in ascending order
365
+ -d, --desc sort output variables in descending order
366
+ -v, --verbose log each step, not only warnings
367
+ --version show program's version number and exit
368
+ ```
@@ -0,0 +1,9 @@
1
+ scratchrelaxtv/__init__.py,sha256=A0cl05aZ04ee5oGMUvYRLNUdFiKCScRDKYSDWKj8Od0,20674
2
+ scratchrelaxtv/__main__.py,sha256=JzJHz5ExPzXlGNrYKQ8tGdIZ_6iyQT84Lh1bxpAUmYE,109
3
+ scratchrelaxtv/cli.py,sha256=awIscnagdNj4_BSLY4C6SQvsEIBUc3kPXi_Yh31tMF0,6370
4
+ scratchrelaxtv/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ scratchrelaxtv-0.7.0.dist-info/METADATA,sha256=t3FS3U0W3zaDkfiGfIyxZVZe7qS90cvep4lioOYX9xo,11666
6
+ scratchrelaxtv-0.7.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
7
+ scratchrelaxtv-0.7.0.dist-info/entry_points.txt,sha256=UeMIJLjl2QYeLfjwcDsWi511MyqGmjesefRPk6SN9yo,93
8
+ scratchrelaxtv-0.7.0.dist-info/licenses/LICENSE,sha256=njGNT2TazyT3JJSyx6iRrIsbig3O3r-sxIbjG2H0hIg,35142
9
+ scratchrelaxtv-0.7.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ relaxtv = scratchrelaxtv.cli:main
3
+ scratchrelaxtv = scratchrelaxtv.cli:main