feat: first version
This commit is contained in:
parent
101f0c67c6
commit
07e218cc01
54 changed files with 1669 additions and 2 deletions
503
README.md
503
README.md
|
|
@ -1,3 +1,502 @@
|
|||
# rougail-output-hcl
|
||||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
|
||||
Rougail output to HCL2 format
|
||||
# HCL
|
||||
|
||||
The HashiCorp Configuration Language (HCL) is a specialized configuration language developed by HashiCorp, primarily designed for use with its suite of tools, including Terraform, Vault, and Nomad.
|
||||
|
||||
Since Rougail is designed to centralize the variables from your various projects, it may be useful to export the variables and values defined in Rougail to an HCL file.
|
||||
|
||||
Please note that the goal here is not to manage all your HCL files, but to export the variables and values to them.
|
||||
|
||||
## Attributes
|
||||
|
||||
Attributes assigns a value to a particular name.
|
||||
|
||||
An attribute is therefore the name of a variable or a family to which a value is assigned.
|
||||
|
||||
### Primitive Types
|
||||
|
||||
Value has those primitive types: `string`, `integer`, `bool` or `null`
|
||||
|
||||
```hcl
|
||||
a_string = "a value"
|
||||
an_integer = 1
|
||||
a_boolean = true
|
||||
null_value = null
|
||||
```
|
||||
|
||||
Nothing particular in Rougail:
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_string: a value # An attribute with a string
|
||||
|
||||
an_integer: 1 # An attribute with a integer
|
||||
|
||||
a_boolean: true # A boolean attribute
|
||||
|
||||
null_value:
|
||||
description: A nullable value
|
||||
mandatory: false
|
||||
...
|
||||
```
|
||||
|
||||
### Complex Types
|
||||
|
||||
Value could be `list`, `set`, `tuple`, `map` or `object`.
|
||||
|
||||
#### `list`, `set` or `tuple`
|
||||
|
||||
The `set` type is an unordered `list` (the elements are all of the same type) with no duplicates. Rougail lists also do not allow duplicates by default (though this can be enabled), but their values are ordered.
|
||||
|
||||
The type `tuple` (where each element has its own type) is not allowed in Rougail, so hasn't equivalent for now.
|
||||
|
||||
Syntactically, there is no difference between a list, a set, and a tuple.
|
||||
|
||||
In Rougail, you must therefore declare a variable of a specific type and set the “multi” parameter to True.
|
||||
|
||||
```hcl
|
||||
a_list = [
|
||||
"value 1",
|
||||
"value 2",
|
||||
]
|
||||
an_integer_list = [
|
||||
1,
|
||||
2,
|
||||
]
|
||||
```
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_list: ["value 1", "value 2"] # An attribute with a list of strings
|
||||
|
||||
an_integer_list: [1, 2] # An attribute with a list of integer
|
||||
...
|
||||
```
|
||||
|
||||
#### `map` or `object`
|
||||
|
||||
The difference between `map` and `object` in HCL is analogous to that between `list` and `tuple`: the values in a `map` are all of the same type, whereas the values in an `object` can be of different.
|
||||
|
||||
In Rougail, this corresponds to a family.
|
||||
|
||||
```hcl
|
||||
a_map = {
|
||||
key_1 = "value 1"
|
||||
key_2 = "value 2"
|
||||
key_3 = "value 3"
|
||||
}
|
||||
an_object = {
|
||||
a_string = "a value"
|
||||
an_integer = 1
|
||||
a_boolean = true
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_map: # A map is simply a collection of variables of the same type
|
||||
|
||||
key_1: value 1 # A first attribute
|
||||
|
||||
key_2: value 2 # A second attribute
|
||||
|
||||
key_3: value 3 # A third attribute
|
||||
|
||||
an_object: # A object is just a family
|
||||
|
||||
a_string: a value # An attribute with a string
|
||||
|
||||
an_integer: 1 # An attribute with a integer
|
||||
|
||||
a_boolean: true # A boolean attribute
|
||||
...
|
||||
```
|
||||
|
||||
### Variable
|
||||
|
||||
```hcl
|
||||
a_first_string: "a value"
|
||||
a_second_string: a_first_string
|
||||
```
|
||||
|
||||
A string, for now, must be enclosed in quotes. But a variable must not be enclosed in quotes.
|
||||
|
||||
You need to calculate the value directly in Rougail.
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_first_string: "a value" # Take a first value
|
||||
|
||||
a_second_string:
|
||||
description: An expressions is just a string
|
||||
default:
|
||||
variable: _.a_first_string
|
||||
...
|
||||
```
|
||||
|
||||
Or use basic expression (see after).
|
||||
|
||||
### Expressions
|
||||
|
||||
Expressions allow for dynamic configuration.
|
||||
|
||||
#### Basic
|
||||
|
||||
```hcl
|
||||
a_first_string = "a value"
|
||||
a_second_string = a_first_string
|
||||
a_third_string = "combine ${a_first_string}"
|
||||
```
|
||||
|
||||
A string, for now, must be enclosed in quotes. But a variable must not be enclosed in quotes (see below).
|
||||
|
||||
You have tu use basic expression in this case:
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_first_string: "a value" # Take a first value
|
||||
|
||||
a_second_string:
|
||||
description: A variable could also use expression to get value
|
||||
hcl_quoted_string: false
|
||||
default: a_first_string
|
||||
|
||||
a_third_string: combine ${a_first_string} # An expressions is just a string
|
||||
...
|
||||
```
|
||||
|
||||
#### Conditional Expressions
|
||||
|
||||
```hcl
|
||||
a_first_string = "a value"
|
||||
a_second_string = a_first_string == "a value" ? 2 : 1
|
||||
```
|
||||
|
||||
A string, for now, must be enclosed in quotes. But a conditional expression must not be enclosed in quotes.
|
||||
|
||||
You need to calculate the value directly in Rougail.
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_first_string: "a value" # Take a first value
|
||||
|
||||
a_second_string:
|
||||
description: An expressions is just a string
|
||||
hcl_quoted_string: false
|
||||
default: "a_first_string == \"a value\" ? 2 : 1"
|
||||
...
|
||||
```
|
||||
|
||||
#### Function
|
||||
|
||||
HCL includes a variety of built-in functions that you can use to manipulate data and perform calculations.
|
||||
|
||||
```hcl
|
||||
content = file("config.json")
|
||||
```
|
||||
|
||||
A string, for now, must be enclosed in quotes. But a conditional expression must not be enclosed in quotes.
|
||||
|
||||
## Block
|
||||
|
||||
A `block` is a container for configuration.
|
||||
|
||||
### A basic block
|
||||
|
||||
There is no equivalent to a `block` in Rougail. To define a `block`, you must define a family and add an `hcl_type` parameter set to `block`.
|
||||
|
||||
[!NOTE] The `hcl` structural module must be loaded for this type to be recognized.
|
||||
|
||||
```hcl
|
||||
a_block {
|
||||
an_attribute = "a value"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
an_attribute: a value # An attribute in a block
|
||||
...
|
||||
```
|
||||
|
||||
### A block in a block
|
||||
|
||||
Blocks can have a tree-like structure, like family in Rougail.
|
||||
|
||||
```hcl
|
||||
a_block {
|
||||
a_second_block {
|
||||
an_attribute = "a value"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
a_second_block:
|
||||
description: We can add a block inside a block
|
||||
hcl_type: block
|
||||
|
||||
an_attribute: a value # An attribute in a block
|
||||
...
|
||||
```
|
||||
|
||||
### A block with a label
|
||||
|
||||
Blocks have a block type and can have zero or more labels.
|
||||
|
||||
There is no equivalent to a `label` in Rougail. To define a `label`, you must define a family and add an `hcl_type` parameter set to `label`. A `label` must be defined within a `block`.
|
||||
|
||||
[!NOTE] The `hcl` structural module must be loaded for this type to be recognized.
|
||||
|
||||
```hcl
|
||||
a_block "a_label" {
|
||||
an_attribute = "a value"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
a_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
an_attribute: a value # An attribute in a block
|
||||
...
|
||||
```
|
||||
|
||||
### A block with some labels
|
||||
|
||||
```hcl
|
||||
a_block "a_first_label" "a_second_label" {
|
||||
an_attribute = "a value"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
a_first_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
a_second_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
an_attribute: a value # An attribute in a block
|
||||
...
|
||||
```
|
||||
|
||||
### Block inside a label block
|
||||
|
||||
A `block` could be in a `label` block.
|
||||
|
||||
```hcl
|
||||
a_block "a_first_label" "a_second_label" {
|
||||
a_sub_block {
|
||||
a_string = "a value"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
a_first_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
a_second_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
a_sub_block:
|
||||
description: A block insible label block
|
||||
hcl_type: block
|
||||
|
||||
a_string: a value # An attribute in a block
|
||||
...
|
||||
```
|
||||
|
||||
### Block with a label inside a block
|
||||
|
||||
And a `label` block could be in a `block`.
|
||||
|
||||
```hcl
|
||||
a_block {
|
||||
a_sub_block "a_first_label" "a_second_label" {
|
||||
a_string = "a value"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
a_sub_block:
|
||||
description: A label block insible block
|
||||
hcl_type: block
|
||||
|
||||
a_first_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
a_second_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
a_string: a value # An attribute in a block
|
||||
...
|
||||
```
|
||||
|
||||
### Duplicate block name
|
||||
|
||||
In Rougail, it's not possible to have two families with the same name.
|
||||
|
||||
```hcl
|
||||
a_block {
|
||||
a_sub_block "a_label" {
|
||||
an_attribut = [
|
||||
"value 1",
|
||||
"value2",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
a_sub_block "a_label" {
|
||||
an_attribut = [
|
||||
"value 3",
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
a_sub_block_1:
|
||||
description: A label block insible block
|
||||
hcl_type: block
|
||||
hcl_name: a_sub_block
|
||||
|
||||
a_label:
|
||||
description: Add a label
|
||||
hcl_type: label
|
||||
|
||||
an_attribut: # An attribute
|
||||
- value 1
|
||||
- value2
|
||||
|
||||
a_sub_block_2:
|
||||
description: Add a label
|
||||
hcl_type: block
|
||||
hcl_name: a_sub_block
|
||||
|
||||
a_label:
|
||||
hcl_type: label
|
||||
|
||||
an_attribut: # An attribute
|
||||
- value 3
|
||||
...
|
||||
```
|
||||
|
||||
### Variable block
|
||||
|
||||
The variable block define variables within your HCL configuration.
|
||||
|
||||
```hcl
|
||||
variable "environment" {
|
||||
description = "The deployment environment"
|
||||
type = string
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
variable:
|
||||
description: Variable block
|
||||
hcl_type: block
|
||||
|
||||
environment:
|
||||
description: The deployment environment
|
||||
hcl_type: label
|
||||
|
||||
description: The deployment environment # The environment description
|
||||
|
||||
type:
|
||||
description: The type description
|
||||
hcl_quoted_string: false
|
||||
default: string
|
||||
...
|
||||
```
|
||||
|
|
|
|||
31
locale/fr/LC_MESSAGES/rougail_output_hcl.po
Normal file
31
locale/fr/LC_MESSAGES/rougail_output_hcl.po
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2026-07-06 16:47+0200\n"
|
||||
"PO-Revision-Date: 2026-07-06 16:47+0200\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
"X-Generator: Poedit 3.9\n"
|
||||
|
||||
#: src/rougail/output_hcl/export.py:130
|
||||
msgid "The family \"{0}\" has a hcl_type parameter but is not in a root config"
|
||||
msgstr ""
|
||||
"La famille \"{0}\" has le paramètre hcl_type mais n'est pas dans la config "
|
||||
"racine"
|
||||
|
||||
#: src/rougail/output_hcl/export.py:142
|
||||
msgid ""
|
||||
"The family \"{0}\" has a hcl_type parameter to label but is not in a block"
|
||||
msgstr ""
|
||||
"La famille \"{0}\" has le paramètre hcl_type à label mais n'est pas dans un "
|
||||
"block"
|
||||
25
locale/rougail_output_hcl.pot
Normal file
25
locale/rougail_output_hcl.pot
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"POT-Creation-Date: 2026-07-06 16:48+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
||||
#: src/rougail/output_hcl/export.py:130
|
||||
msgid "The family \"{0}\" has a hcl_type parameter but is not in a root config"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/output_hcl/export.py:142
|
||||
msgid "The family \"{0}\" has a hcl_type parameter to label but is not in a block"
|
||||
msgstr ""
|
||||
|
||||
43
pyproject.toml
Normal file
43
pyproject.toml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
[build-system]
|
||||
build-backend = "flit_core.buildapi"
|
||||
requires = ["flit_core >=3.8.0,<4"]
|
||||
|
||||
[project]
|
||||
name = "rougail.output_hcl"
|
||||
version = "0.0.0"
|
||||
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
|
||||
readme = "README.md"
|
||||
description = "Rougail output hcl"
|
||||
requires-python = ">=3.11"
|
||||
license = {file = "LICENSE"}
|
||||
classifiers = [
|
||||
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Operating System :: OS Independent",
|
||||
"Natural Language :: English",
|
||||
"Natural Language :: French",
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
"rougail-base >= 1.2.0,<2",
|
||||
"python-hcl2 >= 8.1.2,<9",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Home = "https://forge.cloud.silique.fr/stove/rougail-output-hcl"
|
||||
|
||||
[tool.commitizen]
|
||||
name = "cz_conventional_commits"
|
||||
tag_format = "$version"
|
||||
version_scheme = "pep440"
|
||||
version_provider = "pep621"
|
||||
version_files = [
|
||||
"src/rougail/output_hcl/__version__.py",
|
||||
"pyproject.toml:version"
|
||||
]
|
||||
update_changelog_on_bump = true
|
||||
changelog_merge_prerelease = true
|
||||
26
src/rougail/output_hcl/__init__.py
Normal file
26
src/rougail/output_hcl/__init__.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
"""
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2026
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from .export import RougailOutputHcl
|
||||
from .__version__ import __version__
|
||||
|
||||
|
||||
RougailOutput = RougailOutputHcl
|
||||
|
||||
|
||||
__all__ = ("RougailOutputHcl", "__version__")
|
||||
1
src/rougail/output_hcl/__version__.py
Normal file
1
src/rougail/output_hcl/__version__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
__version__ = "0.0.0"
|
||||
31
src/rougail/output_hcl/config.py
Normal file
31
src/rougail/output_hcl/config.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
"""
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2026
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
def get_rougail_config(
|
||||
*,
|
||||
backward_compatibility=True,
|
||||
) -> dict:
|
||||
return {
|
||||
"name": "hcl",
|
||||
"process": "output",
|
||||
"level": 70,
|
||||
}
|
||||
|
||||
|
||||
__all__ = ("get_rougail_config",)
|
||||
163
src/rougail/output_hcl/export.py
Normal file
163
src/rougail/output_hcl/export.py
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
"""
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2026
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from typing import Any, Union
|
||||
from hcl2 import dumps
|
||||
from hcl2.deserializer import DeserializerOptions
|
||||
from hcl2.formatter import FormatterOptions
|
||||
from json import dumps as add_quote
|
||||
from rougail.error import DictConsistencyError
|
||||
|
||||
|
||||
from .i18n import _
|
||||
from ..output_json import RougailOutputJson
|
||||
|
||||
|
||||
DEBUG = False
|
||||
# DEBUG = True
|
||||
|
||||
|
||||
class RougailOutputHcl(RougailOutputJson):
|
||||
output_name = "hcl"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: "Config",
|
||||
*,
|
||||
rougailconfig: "RougailConfig" = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
super().__init__(config, rougailconfig=rougailconfig, **kwargs)
|
||||
self.d_opts = DeserializerOptions(object_elements_trailing_comma=False)
|
||||
self.f_opts = FormatterOptions(open_empty_objects=False)
|
||||
|
||||
def run(self) -> None:
|
||||
ret = self.exporter()
|
||||
value = dumps(
|
||||
self.dico, deserializer_options=self.d_opts, formatter_options=self.f_opts
|
||||
)
|
||||
if DEBUG:
|
||||
# You can compare to the result to `hcl2tojson xxxx.hcl`, it should be the same
|
||||
print(add_quote(self.dico, ensure_ascii=False, indent=2))
|
||||
return ret, value
|
||||
|
||||
def parse_sequence(
|
||||
self,
|
||||
child: "Option",
|
||||
child_obj: list[dict],
|
||||
) -> None:
|
||||
super().parse_sequence(child, child_obj)
|
||||
for sequence in child_obj:
|
||||
for key, value in sequence.items():
|
||||
sequence[key] = self.add_quote(child, value)
|
||||
|
||||
def variable_informations(self, parent_obj, child: "Option") -> None:
|
||||
if isinstance(parent_obj, list):
|
||||
# the parent is a block
|
||||
if not parent_obj:
|
||||
parent_obj.append({})
|
||||
parent_obj = parent_obj[-1]
|
||||
parent_obj[child.name()] = self.add_quote(child, child.value.get())
|
||||
|
||||
def add_quote(self, child: "Option", value: Any) -> Any:
|
||||
if child and not child.information.get("hcl_quoted_string", True):
|
||||
return value
|
||||
if isinstance(value, str):
|
||||
return add_quote(value)
|
||||
if isinstance(value, list):
|
||||
return [
|
||||
self.add_quote(None, val) if isinstance(val, str) else val
|
||||
for val in value
|
||||
]
|
||||
return value
|
||||
|
||||
def get_default_family(self, child: "Option") -> Union[list, dict]:
|
||||
hcl_type = child.information.get("hcl_type", None)
|
||||
if hcl_type in ["block", "block_with_label"]:
|
||||
return list()
|
||||
return dict()
|
||||
|
||||
def family_informations(
|
||||
self,
|
||||
parent: "Option",
|
||||
parent_obj: Union[list, dict],
|
||||
child: "Option",
|
||||
child_obj: Union[list, dict],
|
||||
) -> None:
|
||||
quote_name = self.family_add_block_information(parent, child, child_obj)
|
||||
name = self.get_family_name(child, quote_name)
|
||||
parent_is_block = isinstance(parent_obj, list)
|
||||
child_is_block = isinstance(child_obj, list)
|
||||
if child_is_block:
|
||||
if parent_is_block:
|
||||
if not parent_obj:
|
||||
parent_obj.append({name: []})
|
||||
elif name not in parent_obj[-1]:
|
||||
parent_obj[-1][name] = []
|
||||
block_to_insert = parent_obj[-1][name]
|
||||
else:
|
||||
if name not in parent_obj:
|
||||
parent_obj[name] = []
|
||||
block_to_insert = parent_obj[name]
|
||||
block_to_insert.extend(child_obj)
|
||||
elif parent_is_block:
|
||||
parent_obj.append({name: child_obj})
|
||||
else:
|
||||
parent_obj[name] = child_obj
|
||||
|
||||
def family_add_block_information(
|
||||
self, parent: "Option", child: "Option", child_obj: Union[list, dict]
|
||||
) -> bool:
|
||||
hcl_type = child.information.get("hcl_type", None)
|
||||
quote_name = False
|
||||
if hcl_type:
|
||||
if self.config != parent and not parent.information.get("hcl_type", None):
|
||||
msg = _(
|
||||
'The family "{0}" has a hcl_type parameter but is not in a root config'
|
||||
).format(child.path())
|
||||
raise DictConsistencyError(
|
||||
msg, 500, child.information.get("ymlfiles", None)
|
||||
)
|
||||
if hcl_type == "block":
|
||||
child_obj[-1]["__is_block__"] = True
|
||||
elif hcl_type == "label":
|
||||
if self.config == parent or not parent.information.get(
|
||||
"hcl_type", None
|
||||
):
|
||||
msg = _(
|
||||
'The family "{0}" has a hcl_type parameter to label but is not in a block'
|
||||
).format(child.path())
|
||||
raise DictConsistencyError(
|
||||
msg, 501, child.information.get("ymlfiles", None)
|
||||
)
|
||||
child_obj["__is_block__"] = True
|
||||
quote_name = True
|
||||
elif hcl_type == "label_with_label":
|
||||
quote_name = True
|
||||
return quote_name
|
||||
|
||||
def get_family_name(self, child: "Option", quote_name: bool) -> str:
|
||||
name = child.information.get("hcl_name", None)
|
||||
if not name:
|
||||
name = child.name()
|
||||
if quote_name:
|
||||
return add_quote(name)
|
||||
return name
|
||||
|
||||
|
||||
__all__ = ("RougailOutputHcl",)
|
||||
26
src/rougail/output_hcl/i18n.py
Normal file
26
src/rougail/output_hcl/i18n.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
"""Internationalisation utilities
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2026
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from gettext import translation
|
||||
from pathlib import Path
|
||||
|
||||
t = translation(
|
||||
"rougail_output_hcl", str(Path(__file__).parent / "locale"), fallback=True
|
||||
)
|
||||
|
||||
_ = t.gettext
|
||||
Binary file not shown.
22
src/rougail/structural_hcl/__init__.py
Normal file
22
src/rougail/structural_hcl/__init__.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
"""
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2026
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from .object_model import Family, Variable
|
||||
|
||||
|
||||
__all__ = ("Family", "Variable")
|
||||
82
src/rougail/structural_hcl/annotator.py
Normal file
82
src/rougail/structural_hcl/annotator.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
"""
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2026
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
from rougail.annotator.variable import Walk
|
||||
from rougail.error import DictConsistencyError
|
||||
|
||||
|
||||
class Annotator(Walk):
|
||||
"""Annotate for bitwarden"""
|
||||
|
||||
level = 92
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
objectspace,
|
||||
*args, # pylint: disable=unused-argument
|
||||
) -> None:
|
||||
if not objectspace.paths:
|
||||
return
|
||||
self.objectspace = objectspace
|
||||
self.parse_family()
|
||||
self.parse_variable()
|
||||
|
||||
def parse_family(self, current=".", parent_is_block=False):
|
||||
is_label = None
|
||||
for child_path in self.objectspace.parents[current]:
|
||||
if child_path not in self.objectspace.families:
|
||||
continue
|
||||
child = self.objectspace.paths[child_path]
|
||||
sub_is_block = is_block = (
|
||||
hasattr(child, "hcl_type") and child.hcl_type == "block"
|
||||
)
|
||||
information_value = None
|
||||
if child.hcl_type == "label":
|
||||
if is_label == False:
|
||||
raise Exception()
|
||||
information_value = "label"
|
||||
is_label = True
|
||||
sub_is_block = True
|
||||
elif is_label:
|
||||
raise Exception()
|
||||
else:
|
||||
is_label = False
|
||||
if self.parse_family(child_path, sub_is_block):
|
||||
if is_label:
|
||||
information_value = "label_with_label"
|
||||
else:
|
||||
information_value = "block_with_label"
|
||||
elif is_block:
|
||||
information_value = "block"
|
||||
if information_value:
|
||||
self.objectspace.informations.add(
|
||||
child.path, "hcl_type", information_value
|
||||
)
|
||||
hcl_name = getattr(child, "hcl_name")
|
||||
if hcl_name:
|
||||
self.objectspace.informations.add(child.path, "hcl_name", hcl_name)
|
||||
return is_label
|
||||
|
||||
def parse_variable(self):
|
||||
for child in self.get_variables():
|
||||
if getattr(child, "hcl_quoted_string", True):
|
||||
continue
|
||||
self.objectspace.informations.add(child.path, "hcl_quoted_string", False)
|
||||
44
src/rougail/structural_hcl/config.py
Normal file
44
src/rougail/structural_hcl/config.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
"""
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2026
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
def get_rougail_config(
|
||||
*,
|
||||
backward_compatibility: bool = True, # pylint: disable=unused-argument
|
||||
) -> dict:
|
||||
options = """
|
||||
step:
|
||||
|
||||
structural:
|
||||
redefine: true
|
||||
default:
|
||||
jinja: |-
|
||||
{% if _.output is not propertyerror and _.output == 'hcl' %}
|
||||
hcl
|
||||
{% endif %}
|
||||
directory
|
||||
"""
|
||||
return {
|
||||
"name": "hcl",
|
||||
"process": "structural",
|
||||
"options": options,
|
||||
"level": 40,
|
||||
}
|
||||
|
||||
|
||||
__all__ = ("get_rougail_config",)
|
||||
39
src/rougail/structural_hcl/object_model.py
Normal file
39
src/rougail/structural_hcl/object_model.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
"""
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2026
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
from typing import Optional, Literal
|
||||
from pydantic import BaseModel, StrictBool, StrictStr
|
||||
|
||||
|
||||
class Family(BaseModel):
|
||||
hcl_type: Literal[
|
||||
None,
|
||||
"block",
|
||||
"label",
|
||||
] = None
|
||||
hcl_name: Optional[StrictStr] = None
|
||||
|
||||
|
||||
class Variable(BaseModel):
|
||||
hcl_quoted_string: StrictBool = True
|
||||
|
||||
|
||||
__all__ = ("Family", "Variable")
|
||||
9
tests/errors/00-label_outside_block.yaml
Normal file
9
tests/errors/00-label_outside_block.yaml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_label:
|
||||
hcl_type: label
|
||||
|
||||
a_variable: a value
|
||||
...
|
||||
1
tests/errors/00-label_outside_block.yaml.errno
Normal file
1
tests/errors/00-label_outside_block.yaml.errno
Normal file
|
|
@ -0,0 +1 @@
|
|||
501
|
||||
4
tests/results/00-doc_primitive.yaml.hcl
Normal file
4
tests/results/00-doc_primitive.yaml.hcl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
a_string = "a value"
|
||||
an_integer = 1
|
||||
a_boolean = true
|
||||
null_value = null
|
||||
8
tests/results/01-doc_complex_types.yaml.hcl
Normal file
8
tests/results/01-doc_complex_types.yaml.hcl
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
a_list = [
|
||||
"value 1",
|
||||
"value 2",
|
||||
]
|
||||
an_integer_list = [
|
||||
1,
|
||||
2,
|
||||
]
|
||||
10
tests/results/01-doc_complex_types2.yaml.hcl
Normal file
10
tests/results/01-doc_complex_types2.yaml.hcl
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
a_map = {
|
||||
key_1 = "value 1"
|
||||
key_2 = "value 2"
|
||||
key_3 = "value 3"
|
||||
}
|
||||
an_object = {
|
||||
a_string = "a value"
|
||||
an_integer = 1
|
||||
a_boolean = true
|
||||
}
|
||||
3
tests/results/02-doc_expressions_basic.yaml.hcl
Normal file
3
tests/results/02-doc_expressions_basic.yaml.hcl
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
a_first_string = "a value"
|
||||
a_second_string = a_first_string
|
||||
a_third_string = "combine ${a_first_string}"
|
||||
2
tests/results/03-doc_conditional_expressions.yaml.hcl
Normal file
2
tests/results/03-doc_conditional_expressions.yaml.hcl
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
a_first_string = "a value"
|
||||
a_second_string = a_first_string == "a value" ? 2 : 1
|
||||
3
tests/results/04-doc_basic_block.yaml.hcl
Normal file
3
tests/results/04-doc_basic_block.yaml.hcl
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
a_block {
|
||||
an_attribute = "a value"
|
||||
}
|
||||
5
tests/results/05-doc_block_in_block.yaml.hcl
Normal file
5
tests/results/05-doc_block_in_block.yaml.hcl
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
a_block {
|
||||
a_second_block {
|
||||
an_attribute = "a value"
|
||||
}
|
||||
}
|
||||
3
tests/results/06-doc_block_with_a_label.yaml.hcl
Normal file
3
tests/results/06-doc_block_with_a_label.yaml.hcl
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
a_block "a_label" {
|
||||
an_attribute = "a value"
|
||||
}
|
||||
3
tests/results/07-doc_block_with_some_labels.yaml.hcl
Normal file
3
tests/results/07-doc_block_with_some_labels.yaml.hcl
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
a_block "a_first_label" "a_second_label" {
|
||||
an_attribute = "a value"
|
||||
}
|
||||
5
tests/results/08-doc_block_inside_a_label_block.yaml.hcl
Normal file
5
tests/results/08-doc_block_inside_a_label_block.yaml.hcl
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
a_block "a_first_label" "a_second_label" {
|
||||
a_sub_block {
|
||||
a_string = "a value"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
a_block {
|
||||
a_sub_block "a_first_label" "a_second_label" {
|
||||
a_string = "a value"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
a_block {
|
||||
a_sub_block "a_label" {
|
||||
an_attribut = [
|
||||
"value 1",
|
||||
"value2",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
a_sub_block "a_label" {
|
||||
an_attribut = [
|
||||
"value 3",
|
||||
]
|
||||
}
|
||||
}
|
||||
4
tests/results/09-doc_variable_block.yaml.hcl
Normal file
4
tests/results/09-doc_variable_block.yaml.hcl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
variable "environment" {
|
||||
description = "The deployment environment"
|
||||
type = string
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
a_block {
|
||||
a_sub_block_1 "a_first_label" "a_second_label" {
|
||||
a_string = "a value"
|
||||
}
|
||||
|
||||
|
||||
a_sub_block_2 {
|
||||
a_string = "a value"
|
||||
}
|
||||
}
|
||||
24
tests/results/92-examples.yaml.hcl
Normal file
24
tests/results/92-examples.yaml.hcl
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
build {
|
||||
name = "learn-packer"
|
||||
sources = [
|
||||
"source.docker.ubuntu",
|
||||
"source.docker.ubuntu-focal",
|
||||
]
|
||||
|
||||
provisioner "shell" {
|
||||
environment_vars = [
|
||||
"FOO=hello world",
|
||||
]
|
||||
inline = [
|
||||
"echo Adding file to Docker Container",
|
||||
"echo \"FOO is $FOO\" > example.txt",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
provisioner "shell" {
|
||||
inline = [
|
||||
"echo Running ${var.docker_image} Docker image.",
|
||||
]
|
||||
}
|
||||
}
|
||||
16
tests/results/93-examples.yaml.hcl
Normal file
16
tests/results/93-examples.yaml.hcl
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
provisioner "shell" {
|
||||
environment_vars = [
|
||||
"FOO=hello world",
|
||||
]
|
||||
inline = [
|
||||
"echo Adding file to Docker Container",
|
||||
"echo \"FOO is $FOO\" > example.txt",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
provisioner "shell" {
|
||||
inline = [
|
||||
"echo Running ${var.docker_image} Docker image.",
|
||||
]
|
||||
}
|
||||
24
tests/results/94-examples.yaml.hcl
Normal file
24
tests/results/94-examples.yaml.hcl
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
build {
|
||||
name = "learn-packer"
|
||||
sources = [
|
||||
"source.docker.ubuntu",
|
||||
"source.docker.ubuntu-focal",
|
||||
]
|
||||
|
||||
provisioner "shell" {
|
||||
environment_vars = [
|
||||
"FOO=hello world",
|
||||
]
|
||||
inline = [
|
||||
"echo Adding file to Docker Container",
|
||||
"echo \"FOO is $FOO\" > example.txt",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
provisioner "shell" {
|
||||
inline = [
|
||||
"echo Running ${var.docker_image} Docker image.",
|
||||
]
|
||||
}
|
||||
}
|
||||
3
tests/results_subconfig/rougail.hcl
Normal file
3
tests/results_subconfig/rougail.hcl
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
a_bock "a_label" {
|
||||
a_variable = "a value"
|
||||
}
|
||||
14
tests/structures/00-doc_primitive.yaml
Normal file
14
tests/structures/00-doc_primitive.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_string: a value # An attribute with a string
|
||||
|
||||
an_integer: 1 # An attribute with a integer
|
||||
|
||||
a_boolean: true # A boolean attribute
|
||||
|
||||
null_value:
|
||||
description: A nullable value
|
||||
mandatory: false
|
||||
...
|
||||
8
tests/structures/01-doc_complex_types.yaml
Normal file
8
tests/structures/01-doc_complex_types.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_list: ["value 1", "value 2"] # An attribute with a list of strings
|
||||
|
||||
an_integer_list: [1, 2] # An attribute with a list of integer
|
||||
...
|
||||
20
tests/structures/01-doc_complex_types2.yaml
Normal file
20
tests/structures/01-doc_complex_types2.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_map: # A map is simply a collection of variables of the same type
|
||||
|
||||
key_1: value 1 # A first attribute
|
||||
|
||||
key_2: value 2 # A second attribute
|
||||
|
||||
key_3: value 3 # A third attribute
|
||||
|
||||
an_object: # A object is just a family
|
||||
|
||||
a_string: a value # An attribute with a string
|
||||
|
||||
an_integer: 1 # An attribute with a integer
|
||||
|
||||
a_boolean: true # A boolean attribute
|
||||
...
|
||||
12
tests/structures/02-doc_expressions_basic.yaml
Normal file
12
tests/structures/02-doc_expressions_basic.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_first_string: "a value" # Take a first value
|
||||
|
||||
a_second_string:
|
||||
description: A variable could also use expression to get value
|
||||
hcl_quoted_string: false
|
||||
default: a_first_string
|
||||
|
||||
a_third_string: combine ${a_first_string} # An expressions is just a string
|
||||
11
tests/structures/03-doc_conditional_expressions.yaml
Normal file
11
tests/structures/03-doc_conditional_expressions.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_first_string: "a value" # Take a first value
|
||||
|
||||
a_second_string:
|
||||
description: An expressions is just a string
|
||||
hcl_quoted_string: false
|
||||
default: "a_first_string == \"a value\" ? 2 : 1"
|
||||
...
|
||||
10
tests/structures/04-doc_basic_block.yaml
Normal file
10
tests/structures/04-doc_basic_block.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
an_attribute: a value # An attribute in a block
|
||||
...
|
||||
14
tests/structures/05-doc_block_in_block.yaml
Normal file
14
tests/structures/05-doc_block_in_block.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
a_second_block:
|
||||
description: We can add a block inside a block
|
||||
hcl_type: block
|
||||
|
||||
an_attribute: a value # An attribute in a block
|
||||
...
|
||||
14
tests/structures/06-doc_block_with_a_label.yaml
Normal file
14
tests/structures/06-doc_block_with_a_label.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
a_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
an_attribute: a value # An attribute in a block
|
||||
...
|
||||
18
tests/structures/07-doc_block_with_some_labels.yaml
Normal file
18
tests/structures/07-doc_block_with_some_labels.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
a_first_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
a_second_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
an_attribute: a value # An attribute in a block
|
||||
...
|
||||
22
tests/structures/08-doc_block_inside_a_label_block.yaml
Normal file
22
tests/structures/08-doc_block_inside_a_label_block.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
a_first_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
a_second_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
a_sub_block:
|
||||
description: A block insible label block
|
||||
hcl_type: block
|
||||
|
||||
a_string: a value # An attribute in a block
|
||||
...
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
a_sub_block:
|
||||
description: A label block insible block
|
||||
hcl_type: block
|
||||
|
||||
a_first_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
a_second_label:
|
||||
description: A label is also a family with hcl_type attribute
|
||||
hcl_type: label
|
||||
|
||||
a_string: a value # An attribute in a block
|
||||
...
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_block:
|
||||
description: A block is a family with hcl_type attribute
|
||||
hcl_type: block
|
||||
|
||||
a_sub_block_1:
|
||||
description: A label block insible block
|
||||
hcl_type: block
|
||||
hcl_name: a_sub_block
|
||||
|
||||
a_label:
|
||||
description: Add a label
|
||||
hcl_type: label
|
||||
|
||||
an_attribut: # An attribute
|
||||
- value 1
|
||||
- value2
|
||||
|
||||
a_sub_block_2:
|
||||
description: Add a label
|
||||
hcl_type: block
|
||||
hcl_name: a_sub_block
|
||||
|
||||
a_label:
|
||||
hcl_type: label
|
||||
|
||||
an_attribut: # An attribute
|
||||
- value 3
|
||||
...
|
||||
19
tests/structures/09-doc_variable_block.yaml
Normal file
19
tests/structures/09-doc_variable_block.yaml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
variable:
|
||||
description: Variable block
|
||||
hcl_type: block
|
||||
|
||||
environment:
|
||||
_description: The deployment environment
|
||||
hcl_type: label
|
||||
|
||||
description: The deployment environment # The environment description
|
||||
|
||||
type:
|
||||
description: The type description
|
||||
hcl_quoted_string: false
|
||||
default: string
|
||||
...
|
||||
37
tests/structures/92-examples.yaml
Normal file
37
tests/structures/92-examples.yaml
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
build:
|
||||
hcl_type: block
|
||||
|
||||
name: learn-packer
|
||||
|
||||
sources:
|
||||
- source.docker.ubuntu
|
||||
- source.docker.ubuntu-focal
|
||||
|
||||
provisioner_1:
|
||||
hcl_type: block
|
||||
hcl_name: provisioner
|
||||
|
||||
shell:
|
||||
hcl_type: label
|
||||
|
||||
environment_vars:
|
||||
- FOO=hello world
|
||||
|
||||
inline:
|
||||
- echo Adding file to Docker Container
|
||||
- "echo \"FOO is $FOO\" > example.txt"
|
||||
|
||||
provisioner_2:
|
||||
hcl_type: block
|
||||
hcl_name: provisioner
|
||||
|
||||
shell:
|
||||
hcl_type: label
|
||||
|
||||
inline:
|
||||
- echo Running ${var.docker_image} Docker image.
|
||||
...
|
||||
28
tests/structures/93-examples.yaml
Normal file
28
tests/structures/93-examples.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
provisioner_1:
|
||||
hcl_type: block
|
||||
hcl_name: provisioner
|
||||
|
||||
shell:
|
||||
hcl_type: label
|
||||
|
||||
environment_vars:
|
||||
- FOO=hello world
|
||||
|
||||
inline:
|
||||
- echo Adding file to Docker Container
|
||||
- "echo \"FOO is $FOO\" > example.txt"
|
||||
|
||||
provisioner_2:
|
||||
hcl_type: block
|
||||
hcl_name: provisioner
|
||||
|
||||
shell:
|
||||
hcl_type: label
|
||||
|
||||
inline:
|
||||
- echo Running ${var.docker_image} Docker image.
|
||||
...
|
||||
34
tests/structures/94-examples.yaml
Normal file
34
tests/structures/94-examples.yaml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
build:
|
||||
hcl_type: block
|
||||
|
||||
name: learn-packer
|
||||
|
||||
sources:
|
||||
- source.docker.ubuntu
|
||||
- source.docker.ubuntu-focal
|
||||
|
||||
provisioner:
|
||||
hcl_type: block
|
||||
|
||||
shell_1:
|
||||
hcl_type: label
|
||||
hcl_name: shell
|
||||
|
||||
environment_vars:
|
||||
- FOO=hello world
|
||||
|
||||
inline:
|
||||
- echo Adding file to Docker Container
|
||||
- "echo \"FOO is $FOO\" > example.txt"
|
||||
|
||||
shell_2:
|
||||
hcl_type: label
|
||||
hcl_name: shell
|
||||
|
||||
inline:
|
||||
- echo Running ${var.docker_image} Docker image.
|
||||
...
|
||||
21
tests/structures_subconfig/00-rougail.yaml
Normal file
21
tests/structures_subconfig/00-rougail.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
|
||||
a_variable: a value # A first variable
|
||||
|
||||
a_family: # A family
|
||||
|
||||
a_bock:
|
||||
description: A block
|
||||
hcl_type: block
|
||||
|
||||
a_label:
|
||||
description: A label
|
||||
hcl_type: label
|
||||
|
||||
a_variable:
|
||||
description: A variable with a value
|
||||
default:
|
||||
variable: ____.a_variable
|
||||
...
|
||||
43
tests/test_error.py
Normal file
43
tests/test_error.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
from pytest import fixture, raises
|
||||
from pathlib import Path
|
||||
from rougail import Rougail, RougailConfig
|
||||
from rougail.output_hcl import RougailOutputHcl as RougailOutput
|
||||
from rougail.error import DictConsistencyError
|
||||
|
||||
|
||||
EXT = 'hcl'
|
||||
|
||||
|
||||
structures = list((Path(__file__).parent / 'errors').glob("*.yaml"))
|
||||
structures.sort()
|
||||
|
||||
|
||||
def idfn(fixture_value):
|
||||
return fixture_value.name
|
||||
|
||||
|
||||
@fixture(scope="module", params=structures, ids=idfn)
|
||||
def test_file(request):
|
||||
return request.param
|
||||
|
||||
|
||||
def _test_structural_files(test_file, ext):
|
||||
rougailconfig = RougailConfig.copy()
|
||||
##################################
|
||||
rougailconfig['step.output'] = 'hcl'
|
||||
rougailconfig["main_namespace"] = None
|
||||
rougailconfig["main_structural_directories"] = [str(test_file.absolute())]
|
||||
##################################
|
||||
rougail = Rougail(rougailconfig)
|
||||
config = rougail.run()
|
||||
config.information.set("description_type", "path_and_description")
|
||||
config.property.read_only()
|
||||
with Path(str(test_file.absolute()) + ".errno").open() as fh:
|
||||
error_no = int(fh.read().strip())
|
||||
with raises(DictConsistencyError) as err:
|
||||
generated_output = RougailOutput(config, rougailconfig=rougailconfig).run()[1]
|
||||
assert err.value.errno == error_no, f'expected errno: {error_no}, errno: {err.value.errno}, msg: {err}'
|
||||
|
||||
|
||||
def test_structural_files_hcl(test_file):
|
||||
_test_structural_files(test_file, EXT)
|
||||
48
tests/test_load.py
Normal file
48
tests/test_load.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
from pytest import fixture # , raises
|
||||
from pathlib import Path
|
||||
from rougail import Rougail, RougailConfig
|
||||
from rougail.output_hcl import RougailOutputHcl as RougailOutput
|
||||
|
||||
|
||||
|
||||
EXT = 'hcl'
|
||||
|
||||
|
||||
structures = list((Path(__file__).parent / 'structures').glob("*.yaml"))
|
||||
structures.sort()
|
||||
|
||||
|
||||
def idfn(fixture_value):
|
||||
return fixture_value.name
|
||||
|
||||
|
||||
@fixture(scope="module", params=structures, ids=idfn)
|
||||
def test_file(request):
|
||||
return request.param
|
||||
|
||||
|
||||
def _test_structural_files(test_file, ext):
|
||||
rougailconfig = RougailConfig.copy()
|
||||
##################################
|
||||
rougailconfig['step.output'] = 'hcl'
|
||||
rougailconfig["main_namespace"] = None
|
||||
rougailconfig["main_structural_directories"] = [str(test_file.absolute())]
|
||||
##################################
|
||||
rougail = Rougail(rougailconfig)
|
||||
config = rougail.run()
|
||||
config.information.set("description_type", "path_and_description")
|
||||
config.property.read_only()
|
||||
generated_output = RougailOutput(config, rougailconfig=rougailconfig).run()[1]
|
||||
output_file = Path(__file__).parent / 'results' / (test_file.name + "." + ext)
|
||||
if not output_file.is_file():
|
||||
if not output_file.parent.is_dir():
|
||||
output_file.parent.mkdir()
|
||||
with output_file.open('w') as outfh:
|
||||
outfh.write(generated_output)
|
||||
with output_file.open() as outfh:
|
||||
attented_output = outfh.read()
|
||||
assert generated_output == attented_output, f'filename {output_file}'
|
||||
|
||||
|
||||
def test_structural_files_hcl(test_file):
|
||||
_test_structural_files(test_file, EXT)
|
||||
51
tests/test_subconfig.py
Normal file
51
tests/test_subconfig.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
from pytest import raises
|
||||
from pathlib import Path
|
||||
from rougail import Rougail, RougailConfig
|
||||
from rougail.output_hcl import RougailOutputHcl as RougailOutput
|
||||
from rougail.error import DictConsistencyError
|
||||
|
||||
|
||||
|
||||
def _test_subconfig(error):
|
||||
rougailconfig = RougailConfig.copy()
|
||||
##################################
|
||||
rougailconfig['step.output'] = 'hcl'
|
||||
rougailconfig["main_namespace"] = None
|
||||
rougailconfig["main_structural_directories"] = [str((Path(__file__).parent / 'structures_subconfig').absolute())]
|
||||
##################################
|
||||
rougail = Rougail(rougailconfig)
|
||||
config = rougail.run()
|
||||
config.information.set("description_type", "path_and_description")
|
||||
config.property.read_only()
|
||||
if not error:
|
||||
subconfig = config
|
||||
elif error == "block":
|
||||
subconfig = config.option("a_family")
|
||||
else:
|
||||
subconfig = config.option("a_family.a_bock")
|
||||
generated_output = RougailOutput(subconfig, rougailconfig=rougailconfig, root_config=config, true_config=config).run()[1]
|
||||
output_file = Path(__file__).parent / 'results_subconfig' / 'rougail.hcl'
|
||||
if not output_file.is_file():
|
||||
if not output_file.parent.is_dir():
|
||||
output_file.parent.mkdir()
|
||||
with output_file.open('w') as outfh:
|
||||
outfh.write(generated_output)
|
||||
with output_file.open() as outfh:
|
||||
attented_output = outfh.read()
|
||||
assert generated_output == attented_output, f'filename {output_file}'
|
||||
|
||||
|
||||
def test_subconfig_error():
|
||||
with raises(DictConsistencyError) as err:
|
||||
_test_subconfig(None)
|
||||
assert err.value.errno == 500
|
||||
|
||||
|
||||
def test_subconfig():
|
||||
_test_subconfig("block")
|
||||
|
||||
|
||||
def test_subconfig_error_label():
|
||||
with raises(DictConsistencyError) as err:
|
||||
_test_subconfig("label")
|
||||
assert err.value.errno == 501
|
||||
Loading…
Reference in a new issue