Skip to content

Commit 6ffc68f

Browse files
authored
Enhance Template and File Handling Features (#11)
**Overview:** This PR introduces several improvements to the template system and file handling functionality of the project. It adds new features for working with template variables, custom Jinja2 filters, and interactive input. **Changes:** - Updated both `README.md` and `README.es.md` to reflect the new interactive and subcommand-based usage for the `struct` script. - Enhanced support for template variables: - Introduced the ability to prompt users interactively if template variables are not provided. - Added descriptions, types, and default values for template variables. - New Jinja2 custom filter (`slugify`) introduced to convert strings into slugs for better identifier handling. - Replaced the `stringify` filter with the new `slugify` filter across YAML configuration files, scripts, and tests. - Updated example structure YAML files with enhanced variable definitions. - Added improvements to Kubernetes manifests and Terraform module templates to use the `slugify` filter. **Justification:** These updates improve usability by allowing for interactive input of template variables and enhancing string handling via the `slugify` filter. The improvements make the system more flexible and adaptable to various use cases, while the documentation updates ensure clarity for users. **Impact:** - Increased ease of use with interactive prompts for variables. - Improved readability and maintainability of file templates through `slugify`. - Updated documentation for users to follow the latest features and options.
1 parent 3a66405 commit 6ffc68f

8 files changed

Lines changed: 134 additions & 49 deletions

File tree

README.es.md

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Está dirigido a desarrolladores, ingenieros DevOps y cualquier persona que quie
3535
## ✨ Características
3636

3737
- **Configuración YAML**: Define la estructura de tu proyecto en un simple archivo YAML.
38-
- **Variables de Plantilla**: Usa marcadores de posición en tu configuración y reemplázalos con valores reales en tiempo de ejecución.
38+
- **Variables de Plantilla**: Usa marcadores de posición en tu configuración y reemplázalos con valores reales en tiempo de ejecución. También admite filtros personalizados de Jinja2 y modo interactivo para completar las variables.
3939
- **Permisos de Archivos Personalizados**: Establece permisos personalizados para tus archivos directamente desde la configuración YAML.
4040
- **Obtención de Contenido Remoto**: Incluye contenido de archivos remotos especificando sus URLs.
4141
- **Estrategias de Manejo de Archivos**: Elige entre múltiples estrategias (sobrescribir, omitir, añadir, renombrar, respaldar) para gestionar archivos existentes.
@@ -99,34 +99,38 @@ struct structure.yaml .
9999

100100
## 📝 Uso
101101

102-
Ejecuta el script con el siguiente comando:
102+
Ejecuta el script con el siguiente comando usando uno de los siguientes subcomandos:
103+
104+
- `generate`: Genera la estructura del proyecto basada en la configuración YAML.
105+
- `validate`: Valida la configuración YAML para asegurarte de que sea válida.
106+
- `info`: Muestra información sobre el script y sus dependencias.
107+
- `list`: Lista las estructuras disponibles.
108+
109+
Para más información, ejecuta el script con la opción `-h` o `--help` (esto también está disponible para cada subcomando):
103110

104111
```sh
105-
usage: struct [-h] [--log LOG] [--dry-run] [--vars VARS] [--backup BACKUP] [--file-strategy {overwrite,skip,append,rename,backup}] [--log-file LOG_FILE] yaml_file base_path
112+
struct -h
106113
```
107114

108-
### Opciones
115+
### Ejemplo Simple
109116

110-
- `-h` o `--help`: Muestra la ayuda y sale.
111-
- `--log`: Establece el nivel de registro (DEBUG, INFO, WARNING, ERROR, CRITICAL). El valor predeterminado es INFO.
112-
- `--dry-run`: Realiza una ejecución en seco sin crear archivos o directorios.
113-
- `--vars`: Variables de plantilla en el formato `CLAVE1=valor1,CLAVE2=valor2`.
114-
- `--backup`: Ruta a la carpeta de respaldo.
115-
- `--file-strategy`: Estrategia para manejar archivos existentes (sobrescribir, omitir, añadir, renombrar, respaldar). El valor predeterminado es sobrescribir.
116-
- `--log-file`: Ruta a un archivo de registro.
117+
```sh
118+
struct generate terraform-module ./mi-modulo-terraform
119+
```
117120

118-
### Ejemplo
121+
### Ejemplo Más Completo
119122

120123
```sh
121-
struct \
124+
struct generate \
122125
--log=DEBUG \
123126
--dry-run \
124127
--vars="project_name=MiProyecto,author_name=JuanPerez" \
125128
--backup=/ruta/al/respaldo \
126129
--file-strategy=rename \
127130
--log-file=/ruta/al/archivo_de_registro.log \
128-
./example/structure.yaml \
129-
/ruta/a/tu/directorio/de/salida
131+
terraform-module \
132+
./mi-modulo-terraform
133+
130134
```
131135

132136
## 📄 Configuración YAML
@@ -149,11 +153,20 @@ structure:
149153
- src/main.py:
150154
content: |
151155
print("Hello, World!")
156+
variables:
157+
- project_name:
158+
description: "The name of the project"
159+
default: "MyProject"
160+
type: string
161+
- author_name:
162+
description: "The name of the author"
163+
type: string
164+
default: "John Doe"
152165
```
153166
154167
### Variables de plantilla
155168
156-
Puedes usar variables de plantilla en tu archivo de configuración encerrándolas entre `{{@` y `@}}`. Por ejemplo, `{{@ project_name @}}` será reemplazado con el valor de la variable `project_name` en tiempo de ejecución.
169+
Puedes usar variables de plantilla en tu archivo de configuración encerrándolas entre `{{@` y `@}}`. Por ejemplo, `{{@ project_name @}}` será reemplazado con el valor de la variable `project_name` en tiempo de ejecución. Si las variables no se proporcionan en la línea de comandos, se solicitarán interactivamente.
157170

158171
Si necesitas definir bloques, puedes usar la notación de inicio de bloque `{%@` y la notación de final de bloque `%@}`.
159172

@@ -164,6 +177,22 @@ Para definir comentarios, puedes usar la notación de inicio de comentario `{#@`
164177
- `file_name`: El nombre del archivo que se está procesando.
165178
- `file_directory`: El nombre del directorio del archivo que se está procesando.
166179

180+
#### Variables de plantilla interactivo
181+
182+
Si no proporcionas todas las variables en la línea de comandos, se solicitarán interactivamente.
183+
184+
La struct definida debe incluir las variables en una seccion de `variables` con la siguiente estructura:
185+
186+
```yaml
187+
variables:
188+
- variable_name:
189+
description: "Descripción de la variable"
190+
type: string
191+
default: "Valor predeterminado"
192+
```
193+
194+
como puedes ver, cada variable debe tener una descripción, un tipo y un valor predeterminado (opcional). Este valor predeterminado se usará si no se proporciona la variable en la línea de comandos.
195+
167196
#### Filtros personalizados de Jinja2
168197

169198
##### `latest_release`
@@ -184,6 +213,19 @@ Si ocurre un error en el proceso, el filtro devolverá `LATEST_RELEASE_ERROR`.
184213

185214
NOTA: puedes usar este filtro para obtener la última versión de un proveedor de Terraform. Por ejemplo, para obtener la última versión del proveedor `aws`, puedes usar `{{@ "hashicorp/terraform-provider-aws" | latest_release @}}` o el proveedor de datadog `{{@ "DataDog/terraform-provider-datadog" | latest_release @}}`.
186215

216+
##### `slugify`
217+
218+
Este filtro convierte una cadena en un slug. Toma un argumento opcional para especificar el carácter separador (el valor predeterminado es `-`).
219+
220+
```yaml
221+
structure:
222+
- README.md:
223+
content: |
224+
# {{@ project_name @}}
225+
This is a template repository.
226+
slugify project_name: {{@ project_name | slugify @}}
227+
```
228+
187229
## 👩‍💻 Desarrollo
188230

189231
Para comenzar con el desarrollo, sigue estos pasos:

README.md

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ This is targeted towards developers, DevOps engineers, and anyone who wants to a
3434
## ✨ Features
3535

3636
- **YAML Configuration**: Define your project structure in a simple YAML file.
37-
- **Template Variables**: Use placeholders in your configuration and replace them with actual values at runtime.
37+
- **Template Variables**: Use placeholders in your configuration and replace them with actual values at runtime. Also supports custom Jinja2 filters and interactive mode to fill in the variables.
3838
- **Custom File Permissions**: Set custom permissions for your files directly from the YAML configuration.
3939
- **Remote Content Fetching**: Include content from remote files by specifying their URLs.
4040
- **File Handling Strategies**: Choose from multiple strategies (overwrite, skip, append, rename, backup) to manage existing files.
@@ -98,26 +98,23 @@ struct generate structure.yaml .
9898

9999
## 📝 Usage
100100

101-
Run the script with the following command:
101+
Run the script with the following command using one of the following subcommands:
102102

103-
```sh
104-
usage: struct [-h] [--log LOG] [--dry-run] [--vars VARS] [--backup BACKUP] [--file-strategy {overwrite,skip,append,rename,backup}] [--log-file LOG_FILE] yaml_file base_path
105-
```
103+
- `generate`: Generate the project structure based on the YAML configuration.
104+
- `validate`: Validate the YAML configuration file.
105+
- `info`: Display information about the script and its dependencies.
106+
- `list`: List the available structs
106107

107-
### Options
108+
For more information, run the script with the `-h` or `--help` option (this is also available for each subcommand):
108109

109-
- `-h` or `--help`: Show help and exit
110-
- `--log`: Set the logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL). Default is INFO.
111-
- `--dry-run`: Perform a dry run without creating any files or directories.
112-
- `--vars`: Template variables in the format `KEY1=value1,KEY2=value2`.
113-
- `--backup`: Path to the backup folder.
114-
- `--file-strategy`: Strategy for handling existing files (overwrite, skip, append, rename, backup). Default is overwrite.
115-
- `--log-file`: Path to a log file.
110+
```sh
111+
struct -h
112+
```
116113

117114
### Simple Example
118115

119116
```sh
120-
struct generate /path/to/your/structure.yaml /path/to/your/output/directory
117+
struct generate terraform-module ./my-terraform-module
121118
```
122119

123120
### More Complete Example
@@ -126,12 +123,11 @@ struct generate /path/to/your/structure.yaml /path/to/your/output/directory
126123
struct generate \
127124
--log=DEBUG \
128125
--dry-run \
129-
--vars="project_name=MyProject,author_name=JohnDoe" \
130126
--backup=/path/to/backup \
131127
--file-strategy=rename \
132128
--log-file=/path/to/logfile.log \
133-
/path/to/your/structure.yaml \
134-
/path/to/your/output/directory
129+
terraform-module \
130+
./my-terraform-module
135131
```
136132

137133
## 📄 YAML Configuration
@@ -154,11 +150,20 @@ structure:
154150
- src/main.py:
155151
content: |
156152
print("Hello, World!")
153+
variables:
154+
- project_name:
155+
description: "The name of the project"
156+
default: "MyProject"
157+
type: string
158+
- author_name:
159+
description: "The name of the author"
160+
type: string
161+
default: "John Doe"
157162
```
158163
159164
### Template variables
160165
161-
You can use template variables in your configuration file by enclosing them in `{{@` and `@}}`. For example, `{{@ project_name @}}` will be replaced with the value of the `project_name` variable at runtime.
166+
You can use template variables in your configuration file by enclosing them in `{{@` and `@}}`. For example, `{{@ project_name @}}` will be replaced with the value of the `project_name` variable at runtime. If this are not set when running the script, it will prompt you to enter the value interactively.
162167

163168
If you need to define blocks you can use starting block notation `{%@` and end block notation `%@}`.
164169

@@ -169,6 +174,22 @@ To define comments you can use the comment start notation `{#@` and end comment
169174
- `file_name`: The name of the file being processed.
170175
- `file_directory`: The name of the directory of file that is being processed.
171176

177+
#### Interactive template variables
178+
179+
If you don't provide a default value for a variable, the script will prompt you to enter the value interactively.
180+
181+
The struct defined should define the variable on a specific section of the YAML file. For example:
182+
183+
```yaml
184+
variables:
185+
- author_name:
186+
description: "The name of the author"
187+
type: string
188+
default: "John Doe"
189+
```
190+
191+
as you can see, the `author_name` variable is defined on the `variables` section of the YAML file. it includes a `description`, `type` and `default` value which is used if the user doesn't provide a value interactively.
192+
172193
#### Custom Jinja2 filters
173194

174195
##### `latest_release`
@@ -189,6 +210,19 @@ If there is an error in the process, the filter will return `LATEST_RELEASE_ERRO
189210

190211
NOTE: you can use this filter to get the latest release for a terraform provider. For example, to get the latest release of the `aws` provider, you can use `{{@ "hashicorp/terraform-provider-aws" | latest_release @}}` or datadog provider `{{@ "DataDog/terraform-provider-datadog" | latest_release @}}`.
191212

213+
##### `slugify`
214+
215+
This filter converts a string into a slug. It takes an optional argument to specify the separator character (default is `-`).
216+
217+
```yaml
218+
structure:
219+
- README.md:
220+
content: |
221+
# {{@ project_name @}}
222+
This is a template repository.
223+
slugify project_name: {{@ project_name | slugify @}}
224+
```
225+
192226
## 👩‍💻 Development
193227

194228
To get started with development, follow these steps:

contribs/kubernetes-manifests.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ structure:
44
apiVersion: apps/v1
55
kind: Deployment
66
metadata:
7-
name: {{@ deployment_name | stringify @}}
7+
name: {{@ deployment_name | slugify @}}
88
labels:
9-
app: {{@ app_name | stringify @}}
9+
app: {{@ app_name | slugify @}}
1010
spec:
1111
replicas: 2
1212
selector:
1313
matchLabels:
14-
app: {{@ app_name | stringify @}}
14+
app: {{@ app_name | slugify @}}
1515
template:
1616
metadata:
1717
labels:
18-
app: {{@ app_name | stringify @}}
18+
app: {{@ app_name | slugify @}}
1919
spec:
2020
containers:
21-
- name: {{@ app_name | stringify @}}
21+
- name: {{@ app_name | slugify @}}
2222
image: {{@ image_name @}}:{{@ image_tag @}}
2323
ports:
2424
- containerPort: 80
@@ -30,7 +30,7 @@ structure:
3030
name: example-service
3131
spec:
3232
selector:
33-
app: {{@ app_name | stringify @}}
33+
app: {{@ app_name | slugify @}}
3434
ports:
3535
- protocol: TCP
3636
port: 80

contribs/terraform-module.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ structure:
2424
## Usage
2525
```hcl
2626
module "example" {
27-
source = "./path/to/module/{{@ module_name | stringify @}}"
27+
source = "./path/to/module/{{@ module_name | slugify @}}"
2828
instance_type = "t2.micro"
2929
}
3030
```

example/structure.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,12 @@ structure:
2121
- uses: actions/checkout@{{@ "actions/checkout" | latest_release @}}
2222
- name: Run a one-line script
2323
run: echo Hello, world!
24+
variables:
25+
- project_name:
26+
description: 'The name of the project.'
27+
type: string
28+
default: 'My Project'
29+
- author_name:
30+
description: 'The name of the author.'
31+
type: string
32+
default: 'Alice'

struct_module/filters.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def get_latest_release(repo_name):
2525
except Exception as e:
2626
return "LATEST_RELEASE_ERROR"
2727

28-
def stringify(value):
28+
def slugify(value):
2929
# Convert to lowercase
3030
value = value.lower()
3131
# Replace spaces with hyphens

struct_module/template_renderer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# FILE: template_renderer.py
22
import logging
33
from jinja2 import Environment, meta
4-
from struct_module.filters import get_latest_release, stringify
4+
from struct_module.filters import get_latest_release, slugify
55

66
class TemplateRenderer:
77
def __init__(self, config_variables):
@@ -18,7 +18,7 @@ def __init__(self, config_variables):
1818

1919
custom_filters = {
2020
'latest_release': get_latest_release,
21-
'stringify': stringify,
21+
'slugify': slugify,
2222
}
2323
self.env.filters.update(custom_filters)
2424
self.logger = logging.getLogger(__name__)

tests/test_filters.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from unittest.mock import patch, MagicMock
2-
from struct_module.filters import get_latest_release, stringify
2+
from struct_module.filters import get_latest_release, slugify
33

44
@patch('struct_module.filters.Github')
55
@patch('struct_module.filters.os.getenv')
@@ -27,7 +27,7 @@ def test_get_latest_release(mock_getenv, mock_github):
2727
# mock_repo.default_branch.side_effect = Exception()
2828
assert get_latest_release('fake/repo') == 'LATEST_RELEASE_ERROR'
2929

30-
def test_stringify():
31-
assert stringify('Hello World') == 'hello-world'
32-
assert stringify('Python 3.8') == 'python-38'
33-
assert stringify('Special_Characters!@#') == 'specialcharacters'
30+
def test_slugify():
31+
assert slugify('Hello World') == 'hello-world'
32+
assert slugify('Python 3.8') == 'python-38'
33+
assert slugify('Special_Characters!@#') == 'specialcharacters'

0 commit comments

Comments
 (0)