Skip to content

Commit cc557d1

Browse files
authored
Merge pull request #327 from opsmill/stable
Backport Stable
2 parents f7b97cb + 22d0e94 commit cc557d1

11 files changed

Lines changed: 124 additions & 11 deletions

File tree

.github/file-filters.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ markdown_all: &markdown_all
2828
- "**/*.{md,mdx}"
2929

3030
documentation_all:
31+
- *ansible_plugins
3132
- *doc_files
3233
- *markdown_all

.github/workflows/workflow-changelog-and-docs.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@ jobs:
3131
- name: "Generate Docs"
3232
run: "uv run invoke generate-doc"
3333

34+
- name: "Setup Node.js"
35+
uses: actions/setup-node@v6
36+
with:
37+
node-version: 20
38+
cache: "npm"
39+
cache-dependency-path: docs/package-lock.json
40+
41+
- name: "Install Node dependencies"
42+
run: npm install
43+
working-directory: ./docs
44+
45+
- name: "Build documentation"
46+
run: "uv run invoke docusaurus"
47+
3448
- name: commit docs
3549
uses: github-actions-x/commit@v2.9
3650
with:

docs/_templates/plugin.mdx.j2

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ description: {{ documentation.get('short_description', '') }}
55

66
# {{ plugin_type.title() }} {{ plugin_type.lower() }}
77

8-
{% for description in documentation.get('description', '') %}{{ description }} {% endfor %}
8+
{% for description in documentation.get('description', '') %}{{ description | mdx_safe }} {% endfor %}
99

1010
{% if documentation.get('options') -%}
1111
## Parameters
1212

1313
| Parameter | Type | Required | Default | Description |
1414
|-----------|------|----------|---------|-------------|
1515
{%- for param_name, param in documentation.options.items() %}
16-
| `{{ param_name }}` | `{{ param.get('type', '') }}` | {{ "Yes" if param.get('required', False) else "No" }} | {{ param.get('default', '') }} | {{ param.get('description', '') if param.get('description') is string else param.get('description', [])|join(' ') }} |
16+
| `{{ param_name }}` | `{{ param.get('type', '') }}` | {{ "Yes" if param.get('required', False) else "No" }} | {{ param.get('default', '') }} | {{ param.get('description', '') | mdx_safe }} |
1717
{%- endfor %}
1818
{%- endif %}
1919
{% if examples %}
@@ -30,6 +30,6 @@ description: {{ documentation.get('short_description', '') }}
3030
| Key | Type | Description |
3131
|-----|------|-------------|
3232
{%- for key, data in returns.items() %}
33-
| `{{ key }}` | `{{ data.get('type', '') }}` | {{ data.get('description', '') if data.get('description') is string else data.get('description', [])|join(' ') }} |
33+
| `{{ key }}` | `{{ data.get('type', '') }}` | {{ data.get('description', '') | mdx_safe }} |
3434
{%- endfor %}
3535
{% endif %}

docs/docs/readme.mdx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ These are the plugins in the `opsmill.infrahub` collection:
3030

3131
<!-- vale off -->
3232

33-
* [artifact_fetch](./references/plugins/artifact_fetch_module.mdx) Fetch the content of an artifact from Infrahub
34-
* [schema](./references/plugins/schema_module.mdx) Load, check, or export schemas in Infrahub
3533
* [node](./references/plugins/node_module.mdx) Creates, Updates or Deletes a node in Infrahub
36-
* [artifact_generate](./references/plugins/artifact_generate_module.mdx) Trigger artifact regeneration in Infrahub
37-
* [query_graphql](./references/plugins/query_graphql_module.mdx) Queries and returns elements from Infrahub GraphQL API
34+
* [object_file_fetch](./references/plugins/object_file_fetch_module.mdx) Fetch file content from a CoreFileObject node in Infrahub
3835
* [branch](./references/plugins/branch_module.mdx) Creates, Updates or Deletes a branch in Infrahub
36+
* [schema](./references/plugins/schema_module.mdx) Load, check, or export schemas in Infrahub
37+
* [artifact_fetch](./references/plugins/artifact_fetch_module.mdx) Fetch the content of an artifact from Infrahub
38+
* [query_graphql](./references/plugins/query_graphql_module.mdx) Queries and returns elements from Infrahub GraphQL API
39+
* [artifact_generate](./references/plugins/artifact_generate_module.mdx) Trigger artifact regeneration in Infrahub
3940

4041
<!-- vale on -->
4142

docs/docs/references/plugins/node_module.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Creates, Updates or Deletes a node of a given Kind in Infrahub through Infrahub
1919
| `branch` | `str` | No | main | Branch in which the request is made |
2020
| `validate_certs` | `bool` | No | True | Whether or not to validate SSL of the Infrahub instance |
2121
| `state` | `str` | No | present | Use C(present) or C(absent) for adding or removing. |
22+
| `file_path` | `str` | No | None | Local filesystem path to the file to upload when creating or updating a CoreFileObject node. Required when the kind inherits from CoreFileObject (unless C(fetch_file) is used instead). Mutually exclusive with C(fetch_file). When provided, the module computes a SHA-1 checksum and skips the upload if it matches the server-side checksum (idempotent). Fails if the kind does not inherit from CoreFileObject. Ignored in check mode (no upload performed). |
23+
| `fetch_file` | `bool` | No | False | When C(true), download the file content from the CoreFileObject node and include it in the result as C(binary) (base64-encoded) and C(text) (UTF-8 decoded for text MIME types, null otherwise). Required when the kind inherits from CoreFileObject and C(file_path) is not provided. Mutually exclusive with C(file_path). Ignored in check mode (no download performed). Fails if the kind does not inherit from CoreFileObject. |
2224

2325
## Examples
2426

@@ -50,3 +52,5 @@ Creates, Updates or Deletes a node of a given Kind in Infrahub through Infrahub
5052
|-----|------|-------------|
5153
| `object` | `dict` | Serialized object as created or already existent within Infrahub |
5254
| `msg` | `str` | Message indicating failure or info about what has been achieved |
55+
| `binary` | `str` | Base64-encoded file content downloaded from the CoreFileObject node. Present only when I(fetch_file=true) and not in check mode. |
56+
| `text` | `str` | UTF-8 decoded file content for text MIME types (text/plain, application/json, etc.). null for binary MIME types. Present only when I(fetch_file=true) and not in check mode. |
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
---
2+
title: object_file_fetch
3+
description: Fetch file content from a CoreFileObject node in Infrahub
4+
---
5+
6+
# Modules modules
7+
8+
Downloads the binary file content stored on a CoreFileObject schema node. Identifies the node by UUID (node_id) or HFID (hfid). Optionally saves the file to a local destination path. Returns base64-encoded binary content and metadata regardless of I(dest).
9+
10+
## Parameters
11+
12+
| Parameter | Type | Required | Default | Description |
13+
|-----------|------|----------|---------|-------------|
14+
| `api_endpoint` | `str` | No | | Endpoint of the Infrahub API, optional env=INFRAHUB_ADDRESS |
15+
| `token` | `str` | No | | The API token created through Infrahub, optional env=INFRAHUB_API_TOKEN |
16+
| `timeout` | `int` | No | 10 | Timeout for Infrahub requests in seconds |
17+
| `validate_certs` | `bool` | No | True | Whether or not to validate SSL of the Infrahub instance |
18+
| `branch` | `str` | No | main | Branch in which the request is made |
19+
| `kind` | `str` | Yes | | Schema kind that inherits from CoreFileObject (e.g. C(NetworkCircuitContract)) |
20+
| `node_id` | `str` | No | | UUID of the CoreFileObject node to fetch. One of I(node_id) or I(hfid) is required. |
21+
| `hfid` | `list` | No | | Human-friendly ID component values for the CoreFileObject node. One of I(node_id) or I(hfid) is required. |
22+
| `dest` | `str` | No | None | Local path to save the file content. When a directory path is given (trailing slash or existing directory), the file is saved as `{dest}/{node.file_name}`. When a file path is given, the file is saved exactly at that path. When omitted, file content is returned as variables only. |
23+
24+
## Examples
25+
26+
```yaml
27+
---
28+
- name: Fetch contract PDF by node UUID
29+
opsmill.infrahub.object_file_fetch:
30+
kind: NetworkCircuitContract
31+
node_id: "abc123-uuid"
32+
register: fetch_result
33+
34+
- name: Write fetched file to disk
35+
ansible.builtin.copy:
36+
content: "{{ fetch_result.binary | b64decode }}"
37+
dest: /tmp/contract.pdf
38+
39+
- name: Fetch and save to directory by HFID
40+
opsmill.infrahub.object_file_fetch:
41+
kind: NetworkCircuitContract
42+
hfid:
43+
- "contract.pdf"
44+
dest: /tmp/contracts/
45+
register: fetch_result
46+
# File saved to /tmp/contracts/contract.pdf
47+
# fetch_result.dest == "/tmp/contracts/contract.pdf"
48+
49+
- name: Fetch and save to explicit file path
50+
opsmill.infrahub.object_file_fetch:
51+
kind: NetworkCircuitContract
52+
node_id: "abc123-uuid"
53+
dest: /tmp/my-contract.pdf
54+
register: fetch_result
55+
# File saved to exactly /tmp/my-contract.pdf
56+
```
57+
58+
## Return values
59+
60+
| Key | Type | Description |
61+
|-----|------|-------------|
62+
| `binary` | `str` | Base64-encoded file content downloaded from the CoreFileObject node. |
63+
| `text` | `str` | UTF-8 decoded file content for text MIME types (text/plain, application/json, etc.). null for binary MIME types. |
64+
| `file_name` | `str` | Original filename as stored in Infrahub. |
65+
| `file_type` | `str` | MIME type of the file as detected by Infrahub. |
66+
| `file_size` | `int` | Size of the file in bytes. |
67+
| `checksum` | `str` | SHA-1 hex digest of the file content as stored in Infrahub. |
68+
| `node_id` | `str` | UUID of the fetched CoreFileObject node. |
69+
| `dest` | `str` | Resolved local path where the file was saved. null if I(dest) was not provided. |
70+
| `msg` | `str` | Status message. |

galaxy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace: opsmill
1010
name: infrahub
1111

1212
# The version of the collection. Must be compatible with semantic versioning
13-
version: 1.8.0
13+
version: 1.8.1
1414

1515
# The path to the Markdown (.md) readme file. This path is relative to the root of the collection
1616
readme: README.md

plugins/modules/object_file_fetch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
description:
7373
- Local path to save the file content.
7474
- When a directory path is given (trailing slash or existing directory),
75-
the file is saved as C({dest}/{node.file_name}).
75+
the file is saved as `{dest}/{node.file_name}`.
7676
- When a file path is given, the file is saved exactly at that path.
7777
- When omitted, file content is returned as variables only.
7878
type: str

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "infrahub_ansible_modules"
3-
version = "1.8.0"
3+
version = "1.8.1"
44
description = "Ansible collection to interact with Infrahub's API"
55
authors = [{name = "OpsMill", email = "info@opsmill.com"}]
66
license = {text = "GPLv3"}

tasks/docs.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,34 @@
11
from __future__ import annotations
22

3+
import re
34
import sys
45
from pathlib import Path
56
from typing import Any, NamedTuple
67

78
import yaml
89
from invoke import Context, task
910

11+
# Splits text on Markdown inline code spans (single backticks) so MDX-escaping
12+
# only runs on prose, not on code spans where `{var}` is already safe.
13+
_CODE_SPAN_RE = re.compile(r"(`[^`]+`)")
14+
15+
16+
def mdx_safe(value: object) -> str:
17+
"""Render a docstring description as MDX-safe text.
18+
19+
Joins list-style descriptions into one line and escapes JSX-reserved braces
20+
that appear outside Markdown code spans, preventing MDX from interpreting
21+
placeholders like ``{var}`` as JSX expressions referencing undefined names.
22+
"""
23+
if value is None:
24+
return ""
25+
text = " ".join(str(item) for item in value) if isinstance(value, list) else str(value)
26+
parts = _CODE_SPAN_RE.split(text)
27+
return "".join(
28+
part if index % 2 == 1 else part.replace("{", r"\{").replace("}", r"\}") for index, part in enumerate(parts)
29+
)
30+
31+
1032
MAIN_DIRECTORY = "."
1133
NAMESPACE = "INFRAHUB-ANSIBLE-DOCS"
1234

@@ -228,6 +250,7 @@ def generate_docs(context: Context, debug: bool = False, plugin_type: str | None
228250
trim_blocks=False,
229251
lstrip_blocks=True,
230252
)
253+
environment.filters["mdx_safe"] = mdx_safe
231254

232255
plugin_template = environment.from_string((template_dir / "plugin.mdx.j2").read_text())
233256
readme_template = environment.from_string((template_dir / "readme.mdx.j2").read_text())

0 commit comments

Comments
 (0)