From 4aca8f7675c9593248b4caaa76e5706260ab591a Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Tue, 10 Mar 2026 17:24:20 +0100 Subject: [PATCH 1/4] feat: add noop building block reference implementation with pre-run script --- .github/copilot-instructions.md | 26 ++- .pre-commit-config.yaml | 4 +- .../meshstack/noop/buildingblock/README.md | 98 ++++++++++ modules/meshstack/noop/buildingblock/logo.png | Bin 0 -> 7634 bytes modules/meshstack/noop/buildingblock/logo.svg | 1 + modules/meshstack/noop/buildingblock/main.tf | 3 + .../meshstack/noop/buildingblock/outputs.tf | 57 ++++++ .../meshstack/noop/buildingblock/prerun.sh | 43 +++++ .../meshstack/noop/buildingblock/variables.tf | 57 ++++++ .../meshstack/noop/buildingblock/versions.tf | 5 + .../meshstack/noop/meshstack_integration.tf | 173 ++++++++++++++++++ 11 files changed, 463 insertions(+), 4 deletions(-) create mode 100644 modules/meshstack/noop/buildingblock/README.md create mode 100644 modules/meshstack/noop/buildingblock/logo.png create mode 100644 modules/meshstack/noop/buildingblock/logo.svg create mode 100644 modules/meshstack/noop/buildingblock/main.tf create mode 100644 modules/meshstack/noop/buildingblock/outputs.tf create mode 100644 modules/meshstack/noop/buildingblock/prerun.sh create mode 100644 modules/meshstack/noop/buildingblock/variables.tf create mode 100644 modules/meshstack/noop/buildingblock/versions.tf create mode 100644 modules/meshstack/noop/meshstack_integration.tf diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 24f996c8..8955ed12 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -45,10 +45,26 @@ They are starting points that should cover the simplest use case. A secondary purpose of these files is to serve as a ready-to-use Terraform module root that IaC runtimes can source directly. - Must use variables for required user inputs. -- Must include `required_providers` (place the `terraform { required_providers { ... } }` block at the **bottom** of the file — resources and variables should come first so readers see the important configuration before technical boilerplate). +- Must include `required_providers` block at the **bottom** of the file — resources and variables should come first so readers see the important configuration before technical boilerplate). - Never include `provider` configuration. - Reference modules using Git URLs and a ref pointing to the feature branch when developing. Once merged into main, the `update-module-refs` tooling in CI pins the ref to an appropriate commit. +### Required providers + +Every `meshstack_integration.tf` must declare the `meshcloud/meshstack` provider in a +`required_providers` block. + +```hcl +terraform { + required_providers { + meshstack = { + source = "meshcloud/meshstack" + version = ">= 0.19.3" + } + } +} +``` + ### Shared Variable Conventions The following variables must appear in every `meshstack_integration.tf`. @@ -67,7 +83,7 @@ variable "hub" { default = {} description = <<-EOT `git_ref`: Hub release reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of the meshstack-hub repo. - `bbd_draft`: If true, the building block definition version is kept in draft mode, which allows changing it (useful during development in LCF/ICF). + `bbd_draft`: If true, the building block definition version is kept in draft mode. EOT } ``` @@ -92,7 +108,9 @@ Integrating with meshStack requires context, like a workspace where the resource variable "meshstack" { type = object({ owning_workspace_identifier = string + tags = optional(map(list(string)), {}) }) + description = "Shared meshStack context. Tags are optional and propagated to building block definition metadata." } ``` @@ -102,6 +120,7 @@ Use these variables in the implementation block of building block definitions. resource "meshstack_building_block_definition" "this" { metadata = { owned_by_workspace = var.meshstack.owning_workspace_identifier + tags = var.meshstack.tags } # ... other required fields ... implementation = { @@ -173,6 +192,9 @@ Do **not** commit these relative paths; switch back to the Hub GitHub URL before - [ ] Variables in `snake_case` - [ ] `buildingblock/README.md` with YAML front-matter - [ ] `buildingblock/APP_TEAM_README.md` with shared responsibility matrix +- [ ] `meshstack_integration.tf` declares `meshcloud/meshstack` in `required_providers` +- [ ] `meshstack_integration.tf` uses `variable "hub" { type = object({git_ref = string}) }` and `variable "meshstack" { type = object({owning_workspace_identifier = string}) }` +- [ ] `meshstack_integration.tf` uses relative `./backplane` source (no absolute GitHub URL) - [ ] `ref_name` uses `var.hub.git_ref` — no hardcoded `"main"` - [ ] `version_spec.draft` uses `var.hub.bbd_draft` - [ ] `building_block_definition_version_uuid` output uses `bbd_draft ? version_latest.uuid : version_latest_release.uuid` diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c080880d..6def737a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.81.0 + rev: v1.105.0 hooks: - id: terraform_docs args: @@ -17,7 +17,7 @@ repos: hooks: - id: validate-modules name: Validate modules - entry: sh -c 'bash ci/validate_modules.sh' + entry: sh -c 'bash ci/validate_modules.sh --fix' language: system pass_filenames: true always_run: true \ No newline at end of file diff --git a/modules/meshstack/noop/buildingblock/README.md b/modules/meshstack/noop/buildingblock/README.md new file mode 100644 index 00000000..b1a79b27 --- /dev/null +++ b/modules/meshstack/noop/buildingblock/README.md @@ -0,0 +1,98 @@ +--- +name: meshStack NoOp Building Block +supportedPlatforms: + - meshstack +description: | + Reference building block demonstrating meshStack's complete Terraform interface: + all input types, file inputs, user permissions injection, and pre-run scripts. +--- +# meshStack NoOp Building Block + +This building block is a reference implementation demonstrating how meshStack interfaces with OpenTofu building blocks. It exercises every input type, file input, pre-run script capability, and output type — without provisioning any cloud resources. + +Use it to: +- Understand how meshStack passes inputs to Terraform +- Learn how FILE-type inputs are written to the working directory +- See how `USER_PERMISSIONS` injects project team members into your building block +- Understand the pre-run script execution model + +## Input Types + +| Input | Type | Assignment | Description | +|-------|------|-----------|-------------| +| `user_permissions` | `CODE` | `USER_PERMISSIONS` | Project team members and their roles as a structured list | +| `user_permissions_json` | `CODE` | `USER_PERMISSIONS` | Same as above, as a raw JSON string | +| `sensitive_yaml` | `CODE` | `STATIC` (sensitive) | Encrypted YAML/JSON value, decrypted at runtime | +| `static` | `STRING` | `STATIC` | A platform-engineer-defined string constant | +| `static_code` | `CODE` | `STATIC` | A platform-engineer-defined map | +| `flag` | `BOOLEAN` | `USER_INPUT` | Boolean flag chosen by the user | +| `num` | `INTEGER` | `USER_INPUT` | Integer chosen by the user | +| `text` | `STRING` | `USER_INPUT` | Free-text string from the user | +| `sensitive_text` | `STRING` (sensitive) | `USER_INPUT` | Sensitive string, masked in UI and logs | +| `single_select` | `SINGLE_SELECT` | `USER_INPUT` | One value from a predefined list | +| `multi_select` | `MULTI_SELECT` | `USER_INPUT` | One or more values from a predefined list | +| `multi_select_json` | `MULTI_SELECT` | `USER_INPUT` | Same as above, as a raw JSON string | +| `some-file.yaml` | `FILE` | `STATIC` | Written to working directory; read via `file("some-file.yaml")` | +| `sensitive-file.yaml` | `FILE` | `STATIC` (sensitive) | Like above, encrypted at rest | + +### How FILE Inputs Work + +meshStack writes FILE inputs as files in the Terraform working directory before `tofu init` runs. Access them in Terraform with: + +```hcl +output "some_file_yaml" { + value = yamldecode(file("some-file.yaml")) +} + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [terraform_data.noop](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [flag](#input\_flag) | n/a | `bool` | n/a | yes | +| [multi\_select](#input\_multi\_select) | n/a | `list(string)` | n/a | yes | +| [multi\_select\_json](#input\_multi\_select\_json) | n/a | `string` | n/a | yes | +| [num](#input\_num) | n/a | `number` | n/a | yes | +| [sensitive\_text](#input\_sensitive\_text) | n/a | `string` | n/a | yes | +| [sensitive\_yaml](#input\_sensitive\_yaml) | n/a | `any` | n/a | yes | +| [single\_select](#input\_single\_select) | n/a | `string` | n/a | yes | +| [static](#input\_static) | n/a | `string` | n/a | yes | +| [static\_code](#input\_static\_code) | n/a | `map(string)` | n/a | yes | +| [text](#input\_text) | n/a | `string` | n/a | yes | +| [user\_permissions](#input\_user\_permissions) | n/a |
list(object({
meshIdentifier = string
username = string
firstName = string
lastName = string
email = string
euid = string
roles = list(string)
}))
| n/a | yes | +| [user\_permissions\_json](#input\_user\_permissions\_json) | n/a | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [flag](#output\_flag) | n/a | +| [multi\_select](#output\_multi\_select) | n/a | +| [multi\_select\_json](#output\_multi\_select\_json) | n/a | +| [num](#output\_num) | n/a | +| [sensitive\_file\_yaml](#output\_sensitive\_file\_yaml) | n/a | +| [sensitive\_text](#output\_sensitive\_text) | n/a | +| [sensitive\_yaml](#output\_sensitive\_yaml) | n/a | +| [single\_select](#output\_single\_select) | n/a | +| [some\_file\_yaml](#output\_some\_file\_yaml) | n/a | +| [static](#output\_static) | n/a | +| [static\_code](#output\_static\_code) | n/a | +| [text](#output\_text) | n/a | +| [user\_permissions](#output\_user\_permissions) | n/a | +| [user\_permissions\_json](#output\_user\_permissions\_json) | n/a | + \ No newline at end of file diff --git a/modules/meshstack/noop/buildingblock/logo.png b/modules/meshstack/noop/buildingblock/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c3bafea3feb3b1e7f8dc1c59b7a11c2d2ae34842 GIT binary patch literal 7634 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 \ No newline at end of file diff --git a/modules/meshstack/noop/buildingblock/main.tf b/modules/meshstack/noop/buildingblock/main.tf new file mode 100644 index 00000000..e6c514fe --- /dev/null +++ b/modules/meshstack/noop/buildingblock/main.tf @@ -0,0 +1,3 @@ +resource "terraform_data" "noop" { + # This resource does nothing and is always up-to-date. +} \ No newline at end of file diff --git a/modules/meshstack/noop/buildingblock/outputs.tf b/modules/meshstack/noop/buildingblock/outputs.tf new file mode 100644 index 00000000..f1288a47 --- /dev/null +++ b/modules/meshstack/noop/buildingblock/outputs.tf @@ -0,0 +1,57 @@ +output "some_file_yaml" { + value = yamldecode(file("some-file.yaml")) +} + +output "sensitive_file_yaml" { + value = yamldecode(file("sensitive-file.yaml")) +} + +output "user_permissions" { + value = var.user_permissions +} + +output "user_permissions_json" { + value = jsondecode(var.user_permissions_json) +} + +output "sensitive_yaml" { + value = var.sensitive_yaml + sensitive = true +} + +output "static" { + value = var.static +} + +output "static_code" { + value = var.static_code +} + +output "flag" { + value = var.flag +} + +output "num" { + value = var.num +} + +output "text" { + value = var.text +} + +output "sensitive_text" { + value = var.sensitive_text + sensitive = true +} + +output "single_select" { + value = var.single_select +} + +output "multi_select" { + value = var.multi_select +} + +output "multi_select_json" { + value = jsondecode(var.multi_select_json) +} diff --git a/modules/meshstack/noop/buildingblock/prerun.sh b/modules/meshstack/noop/buildingblock/prerun.sh new file mode 100644 index 00000000..69cbf402 --- /dev/null +++ b/modules/meshstack/noop/buildingblock/prerun.sh @@ -0,0 +1,43 @@ +echo "=== meshStack Building Block Pre-Run Script ===" +echo "Running after 'tofu init', before 'tofu apply'" +echo "" + +echo "--- Run Modes ---" +echo "Run mode APPLY/DESTROY is passed as a positional argument" +echo "Selected run mode: $1" +echo "" + +echo "--- meshBuildingBlockRun JSON input ---" +echo "# Read stdin once and extract multiple fields:" +input=$(cat) +workspace_id=$(echo "$input" | jq -r '.spec.buildingBlock.spec.workspaceIdentifier') +buildingblock_uuid=$(echo "$input" | jq -r '.spec.buildingBlock.uuid') +echo "Workspace identifier: $workspace_id" +echo "Building Block UUID: $buildingblock_uuid" +echo "" + +echo "--- Working Directory ---" +echo "Working directory: $(pwd)" +ls -lah +echo "" + +echo "--- Tool Installation ---" +echo "Currently not supported via apk add, but coming soon, see https://feedback.meshcloud.io/feature-requests/p/building-block-should-support-aws-cli-and-other" +# sudo apk add aws-cli +echo "" + +echo "--- Terraform State Manipulation ---" +echo "The tofu backend is already initialized and a workspace selected" +tofu show -no-color +echo "" + +echo "--- Capturing System Logs ---" +echo "Stdout log message from pre-run script" +echo "Stderr log message from pre-run script" >&2 +echo "" + +echo "--- Capturing User Messages ---" +echo "User message from pre-run script" >> "$MESHSTACK_USER_MESSAGE" + +echo "=== Pre-run script completed successfully ===" +echo "'tofu apply' will now execute." \ No newline at end of file diff --git a/modules/meshstack/noop/buildingblock/variables.tf b/modules/meshstack/noop/buildingblock/variables.tf new file mode 100644 index 00000000..ebd5d408 --- /dev/null +++ b/modules/meshstack/noop/buildingblock/variables.tf @@ -0,0 +1,57 @@ +variable "user_permissions" { + type = list(object({ + meshIdentifier = string + username = string + firstName = string + lastName = string + email = string + euid = string + roles = list(string) + })) +} + +variable "user_permissions_json" { + type = string +} + +variable "sensitive_yaml" { + type = any + sensitive = true +} + +variable "static" { + type = string +} + +variable "static_code" { + type = map(string) +} + +variable "flag" { + type = bool +} + +variable "num" { + type = number +} + +variable "text" { + type = string +} + +variable "sensitive_text" { + type = string + sensitive = true +} + +variable "single_select" { + type = string +} + +variable "multi_select" { + type = list(string) +} + +variable "multi_select_json" { + type = string +} diff --git a/modules/meshstack/noop/buildingblock/versions.tf b/modules/meshstack/noop/buildingblock/versions.tf new file mode 100644 index 00000000..85004373 --- /dev/null +++ b/modules/meshstack/noop/buildingblock/versions.tf @@ -0,0 +1,5 @@ +terraform { + required_version = ">= 1.0" + # No provider required - this building block manages no cloud resources. + # It only processes and echoes back the inputs provided by meshStack. +} diff --git a/modules/meshstack/noop/meshstack_integration.tf b/modules/meshstack/noop/meshstack_integration.tf new file mode 100644 index 00000000..6dfa681f --- /dev/null +++ b/modules/meshstack/noop/meshstack_integration.tf @@ -0,0 +1,173 @@ +variable "hub" { + type = object({ + git_ref = optional(string, "main") + bbd_draft = optional(bool, false) + }) + default = {} + description = <<-EOT + `git_ref`: Hub release reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of the meshstack-hub repo. + `bbd_draft`: If true, the building block definition version is kept in draft mode. + EOT +} + +variable "meshstack" { + type = object({ + owning_workspace_identifier = string + tags = optional(map(list(string)), {}) + }) + description = "Shared meshStack context. Tags are optional and propagated to building block definition metadata." +} + +resource "meshstack_building_block_definition" "this" { + metadata = { + owned_by_workspace = var.meshstack.owning_workspace_identifier + tags = var.meshstack.tags + } + + spec = { + display_name = "meshStack NoOp Building Block" + description = "Reference building block demonstrating meshStack's complete Terraform interface: all input types, file inputs, user permissions injection, and pre-run scripts." + target_type = "WORKSPACE_LEVEL" + } + + version_spec = { + draft = var.hub.bbd_draft + deletion_mode = "PURGE" + implementation = { + terraform = { + ref_name = var.hub.git_ref + repository_path = "modules/meshstack/noop/buildingblock" + repository_url = "https://github.com/meshcloud/meshstack-hub.git" + terraform_version = "1.11.0" + pre_run_script = file("${path.module}/buildingblock/prerun.sh") + } + } + inputs = { + flag = { + assignment_type = "USER_INPUT" + display_name = "Flag" + type = "BOOLEAN" + } + multi_select = { + assignment_type = "USER_INPUT" + display_name = "Multi Select" + selectable_values = ["multi1", "multi2"] + type = "MULTI_SELECT" + } + multi_select_json = { + assignment_type = "USER_INPUT" + display_name = "Multi Select Json" + selectable_values = ["multi1", "multi2"] + type = "MULTI_SELECT" + } + num = { + assignment_type = "USER_INPUT" + display_name = "Num" + type = "INTEGER" + } + + "sensitive-file.yaml" = { + assignment_type = "STATIC" + display_name = "Sensitive File.yaml" + type = "FILE" + sensitive = { + argument = { + secret_value = "data:application/yaml;base64,c29tZTogaW5wdXQKb3RoZXI6IHZhbHVlCg==" + secret_version = null + } + } + } + sensitive_text = { + assignment_type = "USER_INPUT" + display_name = "Sensitive Text" + type = "STRING" + sensitive = {} + } + sensitive_yaml = { + assignment_type = "STATIC" + display_name = "Sensitive Yaml" + type = "CODE" + sensitive = { + argument = { + secret_value = "some: yaml\nother: value\n" + } + } + } + single_select = { + assignment_type = "USER_INPUT" + display_name = "Single Select" + selectable_values = ["single1", "single2"] + type = "SINGLE_SELECT" + } + "some-file.yaml" = { + assignment_type = "STATIC" + display_name = "Yaml" + type = "FILE" + argument = jsonencode("data:application/yaml;base64,c29tZTogaW5wdXQKb3RoZXI6IHZhbHVlCg==") + } + static = { + argument = jsonencode("A static value") + assignment_type = "STATIC" + display_name = "Static" + type = "STRING" + } + static_code = { + argument = jsonencode(jsonencode({ some : "code" })) + assignment_type = "STATIC" + display_name = "Static Code" + type = "CODE" + } + text = { + assignment_type = "USER_INPUT" + default_value = jsonencode("") + display_name = "Text" + type = "STRING" + } + user_permissions = { + assignment_type = "USER_PERMISSIONS" + display_name = "User Permissions" + type = "CODE" + } + user_permissions_json = { + assignment_type = "USER_PERMISSIONS" + display_name = "User Permissions" + type = "CODE" + } + } + outputs = { + flag = { + assignment_type = "NONE" + display_name = "Flag" + type = "BOOLEAN" + } + num = { + assignment_type = "NONE" + display_name = "Num" + type = "INTEGER" + } + text = { + assignment_type = "NONE" + display_name = "Text" + type = "STRING" + } + } + } +} + +output "building_block_definition_uuid" { + value = meshstack_building_block_definition.this.metadata.uuid +} + +output "building_block_definition_version_uuid" { + description = "UUID of the latest version. In draft mode returns the latest draft; otherwise returns the latest release." + value = var.hub.bbd_draft ? meshstack_building_block_definition.this.version_latest.uuid : meshstack_building_block_definition.this.version_latest_release.uuid +} + +terraform { + required_providers { + meshstack = { + source = "meshcloud/meshstack" + version = ">= 0.19.3" + } + } +} From a88df5d5f3fc3814a67b6f8a30da94bbf1a3b243 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Tue, 17 Mar 2026 16:49:09 +0100 Subject: [PATCH 2/4] feat: advanced input/output debugging using the noop BB --- .../meshstack/noop/buildingblock/README.md | 4 ++ .../meshstack/noop/buildingblock/outputs.tf | 67 +++++++++++++++++++ .../meshstack/noop/meshstack_integration.tf | 25 +++++++ 3 files changed, 96 insertions(+) diff --git a/modules/meshstack/noop/buildingblock/README.md b/modules/meshstack/noop/buildingblock/README.md index b1a79b27..4c8df377 100644 --- a/modules/meshstack/noop/buildingblock/README.md +++ b/modules/meshstack/noop/buildingblock/README.md @@ -81,10 +81,13 @@ No modules. | Name | Description | |------|-------------| +| [debug\_input\_files\_json](#output\_debug\_input\_files\_json) | JSON-encoded map of all input files received, including sensitive values in plaintext. Useful for test assertions. Do not do this in production code. | +| [debug\_input\_variables\_json](#output\_debug\_input\_variables\_json) | JSON-encoded map of all input variables received, including sensitive values in plaintext. Useful for test assertions. Do not do this in production code. | | [flag](#output\_flag) | n/a | | [multi\_select](#output\_multi\_select) | n/a | | [multi\_select\_json](#output\_multi\_select\_json) | n/a | | [num](#output\_num) | n/a | +| [resource\_url](#output\_resource\_url) | n/a | | [sensitive\_file\_yaml](#output\_sensitive\_file\_yaml) | n/a | | [sensitive\_text](#output\_sensitive\_text) | n/a | | [sensitive\_yaml](#output\_sensitive\_yaml) | n/a | @@ -92,6 +95,7 @@ No modules. | [some\_file\_yaml](#output\_some\_file\_yaml) | n/a | | [static](#output\_static) | n/a | | [static\_code](#output\_static\_code) | n/a | +| [summary](#output\_summary) | n/a | | [text](#output\_text) | n/a | | [user\_permissions](#output\_user\_permissions) | n/a | | [user\_permissions\_json](#output\_user\_permissions\_json) | n/a | diff --git a/modules/meshstack/noop/buildingblock/outputs.tf b/modules/meshstack/noop/buildingblock/outputs.tf index f1288a47..a71770fd 100644 --- a/modules/meshstack/noop/buildingblock/outputs.tf +++ b/modules/meshstack/noop/buildingblock/outputs.tf @@ -55,3 +55,70 @@ output "multi_select" { output "multi_select_json" { value = jsondecode(var.multi_select_json) } + +output "resource_url" { + value = "https://hub.meshcloud.io/modules/meshstack/noop" +} + +output "summary" { + value = <<-MARKDOWN +# NoOp Building Block — Deployment Summary + +This building block was successfully deployed. It is a **reference implementation** +demonstrating meshStack's complete Terraform interface — it provisions _no real +infrastructure_. + +The `SUMMARY` output assignment type allows you to provide a rich markdown summary for application teams. +This summary is rendered like a README for this building block in meshPanel. + +## Example: Tables + +| Input | Value | +|----------------|------------------------------| +| Text | `${var.text}` | +| Number | `${var.num}` | +| Flag | `${var.flag}` | +| Single Select | `${var.single_select}` | +| Multi Select | `${join(", ", var.multi_select)}` | + +## Example: Code blocks + +You can use fenced code blocks and `inline code` for formatting. +We support syntax highlighting for common languages. + +```yaml +some: input +other: value +``` + +## Example: Callout blocks + +> **Note**: Use quote blocks to create callouts for important information. +MARKDOWN +} + +output "debug_input_variables_json" { + description = "JSON-encoded map of all input variables received, including sensitive values in plaintext. Useful for test assertions. Do not do this in production code." + value = jsonencode({ + flag = var.flag + num = var.num + text = var.text + single_select = var.single_select + sensitive_text = var.sensitive_text + sensitive_yaml = var.sensitive_yaml + multi_select = var.multi_select + multi_select_json = var.multi_select_json + static = var.static + static_code = var.static_code + user_permissions = var.user_permissions + user_permissions_json = var.user_permissions_json + }) +} + +output "debug_input_files_json" { + description = "JSON-encoded map of all input files received, including sensitive values in plaintext. Useful for test assertions. Do not do this in production code." + value = jsonencode({ + "some-file.yaml" = file("some-file.yaml") + "sensitive-file.yaml" = file("sensitive-file.yaml") + }) +} diff --git a/modules/meshstack/noop/meshstack_integration.tf b/modules/meshstack/noop/meshstack_integration.tf index 6dfa681f..43b4f8d7 100644 --- a/modules/meshstack/noop/meshstack_integration.tf +++ b/modules/meshstack/noop/meshstack_integration.tf @@ -150,6 +150,31 @@ resource "meshstack_building_block_definition" "this" { display_name = "Text" type = "STRING" } + static_code = { + assignment_type = "NONE" + display_name = "Static Code" + type = "CODE" + } + resource_url = { + assignment_type = "RESOURCE_URL" + display_name = "Resource URL" + type = "STRING" + } + summary = { + assignment_type = "SUMMARY" + display_name = "Summary" + type = "STRING" + } + debug_input_variables_json = { + assignment_type = "NONE" + display_name = "Input Variables as JSON for debugging" + type = "CODE" + } + debug_input_files_json = { + assignment_type = "NONE" + display_name = "Input Files as JSON for debugging" + type = "CODE" + } } } } From c190046ea85f08b81673c10eb28bed0d6245fa84 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Tue, 17 Mar 2026 16:55:59 +0100 Subject: [PATCH 3/4] fix: allow sensitive outputs --- modules/meshstack/noop/buildingblock/README.md | 4 ++-- modules/meshstack/noop/buildingblock/outputs.tf | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/meshstack/noop/buildingblock/README.md b/modules/meshstack/noop/buildingblock/README.md index 4c8df377..bbe1df28 100644 --- a/modules/meshstack/noop/buildingblock/README.md +++ b/modules/meshstack/noop/buildingblock/README.md @@ -81,8 +81,8 @@ No modules. | Name | Description | |------|-------------| -| [debug\_input\_files\_json](#output\_debug\_input\_files\_json) | JSON-encoded map of all input files received, including sensitive values in plaintext. Useful for test assertions. Do not do this in production code. | -| [debug\_input\_variables\_json](#output\_debug\_input\_variables\_json) | JSON-encoded map of all input variables received, including sensitive values in plaintext. Useful for test assertions. Do not do this in production code. | +| [debug\_input\_files\_json](#output\_debug\_input\_files\_json) | JSON-encoded map of all input files received, including sensitive values in plaintext. | +| [debug\_input\_variables\_json](#output\_debug\_input\_variables\_json) | JSON-encoded map of all input variables received, including sensitive values in plaintext. | | [flag](#output\_flag) | n/a | | [multi\_select](#output\_multi\_select) | n/a | | [multi\_select\_json](#output\_multi\_select\_json) | n/a | diff --git a/modules/meshstack/noop/buildingblock/outputs.tf b/modules/meshstack/noop/buildingblock/outputs.tf index a71770fd..7453ee3f 100644 --- a/modules/meshstack/noop/buildingblock/outputs.tf +++ b/modules/meshstack/noop/buildingblock/outputs.tf @@ -98,7 +98,8 @@ MARKDOWN } output "debug_input_variables_json" { - description = "JSON-encoded map of all input variables received, including sensitive values in plaintext. Useful for test assertions. Do not do this in production code." + description = "JSON-encoded map of all input variables received, including sensitive values in plaintext." + sensitive = true # For test only. Do not do this in production code. value = jsonencode({ flag = var.flag num = var.num @@ -116,7 +117,8 @@ output "debug_input_variables_json" { } output "debug_input_files_json" { - description = "JSON-encoded map of all input files received, including sensitive values in plaintext. Useful for test assertions. Do not do this in production code." + description = "JSON-encoded map of all input files received, including sensitive values in plaintext." + sensitive = true # For test only. Do not do this in production code. value = jsonencode({ "some-file.yaml" = file("some-file.yaml") "sensitive-file.yaml" = file("sensitive-file.yaml") From 51f4202a3d6158d9b3a682bcdd717adf7a58fac3 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Tue, 17 Mar 2026 17:08:55 +0100 Subject: [PATCH 4/4] feat: simplify summary md without interpolation --- modules/meshstack/noop/buildingblock/outputs.tf | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/meshstack/noop/buildingblock/outputs.tf b/modules/meshstack/noop/buildingblock/outputs.tf index 7453ee3f..aa736bf3 100644 --- a/modules/meshstack/noop/buildingblock/outputs.tf +++ b/modules/meshstack/noop/buildingblock/outputs.tf @@ -75,11 +75,8 @@ This summary is rendered like a README for this building block in meshPanel. | Input | Value | |----------------|------------------------------| -| Text | `${var.text}` | -| Number | `${var.num}` | -| Flag | `${var.flag}` | -| Single Select | `${var.single_select}` | -| Multi Select | `${join(", ", var.multi_select)}` | +| Text | `Hello` | +| Number | `123` | ## Example: Code blocks