From ebc4669b4def5adb83f46d9c7de7345871ee67cc Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 10 Apr 2026 22:28:40 +0200 Subject: [PATCH] streamline GEMINI file, drop agents rules (#3857) --- .agents/rules/context-interpolation.md | 48 ------------------- .agents/rules/dev-workflow.md | 13 ----- .agents/rules/fast-factory-driven.md | 18 ------- .agents/rules/modules-as-resource-managers.md | 9 ---- .agents/rules/style-guide.md | 27 ----------- .agents/rules/testing-guidelines.md | 31 ------------ GEMINI.md | 23 +++++---- modules/GEMINI.md | 12 ----- 8 files changed, 14 insertions(+), 167 deletions(-) delete mode 100644 .agents/rules/context-interpolation.md delete mode 100644 .agents/rules/dev-workflow.md delete mode 100644 .agents/rules/fast-factory-driven.md delete mode 100644 .agents/rules/modules-as-resource-managers.md delete mode 100644 .agents/rules/style-guide.md delete mode 100644 .agents/rules/testing-guidelines.md delete mode 100644 modules/GEMINI.md diff --git a/.agents/rules/context-interpolation.md b/.agents/rules/context-interpolation.md deleted file mode 100644 index e2e8cd618..000000000 --- a/.agents/rules/context-interpolation.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -trigger: always_on ---- - -# Context-Based Interpolation - -When designing factory patterns or datasets, you MUST leverage the **context-based interpolation** system. - -A `context` object variable is used to hold maps of known, existing resource IDs (such as `project_ids`, `folder_ids`, `networks`, `iam_principals`). - -## Usage Pattern - -1. **Variables:** Add a `context` variable in `variables.tf`. - ```hcl - variable "context" { - description = "Context-specific interpolations." - type = object({ - project_ids = optional(map(string), {}) - folder_ids = optional(map(string), {}) - }) - default = {} - } - ``` - -2. **Locals:** Build `ctx` and `ctx_p` local variables in `main.tf` by flattening the `var.context` object. - ```hcl - locals { - ctx = { - for k, v in var.context : k => { - for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv - } - } - ctx_p = "$" - } - ``` - -3. **Lookups:** Apply lookups inside resources. - ```hcl - project = lookup(local.ctx.project_ids, var.project_id, var.project_id) - ``` - -4. **YAML Interpolation:** In factory YAML files, use the `$` prefix convention to reference the lookup map keys. - ```yaml - # Instead of hardcoding the folder ID: parent: folders/1234567890 - parent: $folder_ids:teams/team-a - ``` - -This pattern makes YAML configuration files highly portable across installations and environments by substituting mnemonic keys for hardcoded values. \ No newline at end of file diff --git a/.agents/rules/dev-workflow.md b/.agents/rules/dev-workflow.md deleted file mode 100644 index dac4f3f14..000000000 --- a/.agents/rules/dev-workflow.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -trigger: always_on ---- - -# Always make sure edited code passes linting checks - -- run `tools/tfdoc.py` if variable or output definitions changed -- run `tools/check_documentation.py` to ensure variables are sorted alphabetically and READMEs are consistent -- run `tools/check_boilerplate.py` to ensure license headers are present -- run `terraform fmt` on any edited Terraform file, and hcl examples in README files -- run `yamllint -c .yamllint --no-warnings ` on any edited YAML files -- a schema change should be reflected in all the other places that use the same schema, those are documented in `tools/duplicate-diff.py` -- always make sure both example and module (or stage) level tests run for all the modules/stages where code was edited \ No newline at end of file diff --git a/.agents/rules/fast-factory-driven.md b/.agents/rules/fast-factory-driven.md deleted file mode 100644 index 1de2b9562..000000000 --- a/.agents/rules/fast-factory-driven.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -trigger: always_on ---- - -# Use factory datasets for resource configuration - -**FAST stages should leverage module-backed factories (e.g., `project-factory`) when available, but must implement their own stage-level factories when macro-modules don't exist.** - -Stages in the FAST folder are split between Terraform code and datasets. - -Code is used to implement and wire together "factories" that implement resource management, while the actual description of resources and their relationships is implemented via YAML configurations read by those factories. - -- YAML configurations are grouped in "datasets" which implement a complete design for the stage -- each factory has a reference JSON schema used to describe and validate the YAML data -- factories are generally implemented in the underlying modules, not in FAST stages, *unless* the stage needs to iterate over standard modules or resources (e.g., `dns` zones, `net-firewall-policy`, `ncc_hubs`). -- modules deal with one specific resource set (eg an instance and its disks, a project and its org policies, IAM, etc.) and generally implement a single factory -- the project and VPC factory modules are the exception, as they are designed as "macro modules" to support interrelated creation of resources pertaining to a larger scope -- modules that do not manage "sets" of resources (e.g. one project, one folder, one dataset, etc.) typically do not have an associated factory, or only do for sub-resources (e.g. rules in a single policy), those factories are either implemented in the "macro modules" or directly in FAST diff --git a/.agents/rules/modules-as-resource-managers.md b/.agents/rules/modules-as-resource-managers.md deleted file mode 100644 index cf0d72926..000000000 --- a/.agents/rules/modules-as-resource-managers.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -trigger: always_on ---- - -Modules are designed to be containers for all aspects related to uage of a resource type. The pattern is a module is focused on a specific resource (e.g. folder, project, vpc, etc.) and implements all the functionality needed to create/manage that resources so that it is ready for user consumption. This includes: IAM, sub resources (eg subnets and routes for a network), org policies where applicable, etc. - -Unrelated resources like a dataset for a project should never be part of the same module, except in the two "aggregation modules" (project and vpc factory) where that makes sense to allow consumers to create baseline infrastructure ready to receive application-level resources. - -Never, ever break this boundary as a first approach, and always, always check in with the user if this looks like the only plan of action, as the criteria and constraints that led to the plan might need to be revised instead. \ No newline at end of file diff --git a/.agents/rules/style-guide.md b/.agents/rules/style-guide.md deleted file mode 100644 index 537b7b09e..000000000 --- a/.agents/rules/style-guide.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -trigger: always_on ---- - -# Strictly Adhere to the Fabric Style Guide - -When modifying or generating Terraform code, you MUST follow these specific style conventions: - -- **Line Length:** Enforce a 79-character line length limit for legibility. This rule is relaxed *only* for long resource attribute names and descriptions. -- **Ternary Operators:** Wrap complex ternary operators in parentheses and break lines to align the `?` and `:` tokens clearly. Example: - ```hcl - locals { - parent_id = ( - var.parent == null || startswith(coalesce(var.parent, "-"), "$") - ? var.parent - : split("/", var.parent)[1] - ) - } - ``` -- **Function Calls:** Split function calls with many arguments across multiple lines, typically breaking after the opening parenthesis and before the closing one. -- **Alphabetical Ordering:** You MUST ensure variables and outputs are strictly sorted alphabetically. Run `tools/check_documentation.py` to verify this. -- **Locals Separation:** - - Use module-level locals for values referenced directly by resources or outputs (e.g., `svpc_host_config`). - - Use block-level "private" locals prefixed with an underscore (`_`) for intermediate transformations only referenced within the same block (e.g., `_svpc_service_iam`). -- **Complex Transformations:** Move complex data transformations in `for` or `for_each` loops to `locals` to keep resource blocks clean. -- **Variable Spaces:** Leverage `optional()` defaults extensively within object variables to reduce verbosity. -- **Naming:** NEVER use random strings for resource naming. Instead, implement and use an optional `prefix` variable in all modules. diff --git a/.agents/rules/testing-guidelines.md b/.agents/rules/testing-guidelines.md deleted file mode 100644 index 6de060f3c..000000000 --- a/.agents/rules/testing-guidelines.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -trigger: always_on ---- - -# Use Example-Based Testing in README.md - -When testing modules, you MUST prefer example-based testing over writing legacy Python `pytest` functions. - -Example-based tests are triggered from HCL Markdown fenced code blocks inside a module's `README.md` file using a special `# tftest` comment at the bottom. - -## How to structure a test -1. Write a clear HCL code block demonstrating the functionality. -2. Add the `tftest` directive, declaring expected counts. -3. If validating values, point to a YAML inventory file. - -```hcl -module "my-module" { - source = "./modules/my-module" - name = "test-example" -} -# tftest modules=1 resources=2 inventory=my-inventory.yaml -``` - -## Inventory Files -Inventory files are YAML datasets used to assert that the generated plan matches expectations. -- Place them in the `tests/modules//examples/` directory. -- You can generate an inventory automatically by running a successful plan using `pytest -s` (refer to `CONTRIBUTING.md` for the exact command format). -- **DO NOT hand-code inventory files from scratch.** Extract only the necessary bits relevant to the test scenario from the generated output. - -## tftest-based tests (Advanced) -If a module lacks an example in its `README.md` or you are testing a FAST stage without examples, use `tftest`-based tests using `tfvars` and `yaml` files. Refer to the "Testing via `tfvars` and `yaml`" section in `CONTRIBUTING.md`. \ No newline at end of file diff --git a/GEMINI.md b/GEMINI.md index b3506edb9..89553fa60 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -11,7 +11,8 @@ Cloud Foundation Fabric is a comprehensive suite of Terraform modules and end-to ### 1. Modules (`/modules`) -* **Philosophy:** Lean, composable, and close to the underlying provider resources. +* **Philosophy:** Lean, composable, and close to the underlying provider resources. Modules are designed to be containers for all aspects related to usage of a resource type (e.g., folder, project, vpc, etc.). This includes IAM, sub-resources (e.g. subnets and routes for a network), and org policies where applicable. +* **Boundary:** Unrelated resources (like a dataset for a project) should never be part of the same module, except in the two "aggregation modules" (`project-factory` and `net-vpc-factory`). Never break this boundary as a first approach. * **Structure:** * Standardized interfaces: IAM, logging, organization policies, etc. * Self-contained: Dependency injection via context variables is preferred over complex remote state lookups within modules. @@ -25,7 +26,7 @@ Cloud Foundation Fabric is a comprehensive suite of Terraform modules and end-to * **Purpose:** Rapidly set up a secure, scalable GCP organization. * **Architecture:** Divided into sequential "stages" (0-org-setup, 1-vpcsc, 2-security, 2-networking, etc.). -* **Factories:** Extensively uses YAML-based datasets and module factory patterns to drive configuration at scale, acting as a "translation machine" that expresses different architectural designs without changing the underlying stage code. +* **Factories:** Extensively uses YAML-based datasets and module factory patterns to drive configuration at scale, acting as a "translation machine" that expresses different architectural designs without changing the underlying stage code. Factories are generally implemented in the underlying modules, not in FAST stages, *unless* the stage needs to iterate over standard modules or resources (e.g., `dns` zones, `net-firewall-policy`, `ncc_hubs`). ### 3. Tools (`/tools`) @@ -64,6 +65,8 @@ python3 tools/check_documentation.py modules/ # Regenerate README variables/outputs tables when check fails # Note: tfdoc uses special HTML comments () in READMEs. Do not manually edit these sections. +# You can configure tfdoc via HTML comments in the README (e.g., ). +# To add a file description to the generated table, use a comment in the .tf file: # tfdoc:file:description My description. python3 tools/tfdoc.py --replace modules/ # YAML linting @@ -71,6 +74,10 @@ yamllint -c .yamllint --no-warnings # License/boilerplate check python3 tools/check_boilerplate.py --scan-files + +# Schema changes +# A schema change should be reflected in all the other places that use the same schema. +# These are documented in and can be checked via tools/duplicate-diff.py. ``` **Common gotcha — unsorted variables (`[SV]` error):** `check_documentation.py` requires variables in `variables.tf` to be in strict alphabetical order. When adding a new variable, insert it at the correct alphabetical position, not at the top of the file. @@ -89,7 +96,9 @@ module "my-module" { # tftest modules=1 resources=2 inventory=my-inventory.yaml ``` -* **Inventory files (`YAML`):** Used to assert specific values, resource counts, or outputs from the terraform plan against an expected dataset. +* **Inventory files (`YAML`):** Used to assert specific values, resource counts, or outputs from the terraform plan against an expected dataset. **DO NOT hand-code inventory files from scratch.** Extract only the necessary bits relevant to the test scenario from the generated output. +* **External Files:** If a README test requires external files (e.g., for factories), mock them using the `# tftest-file id=myid path=path/to/file.yaml` directive in a separate YAML block, and add `files=myid` to the `tftest` directive. +* **FAST Stages & `tftest.yaml`:** FAST stages often lack README examples. For these, use `tftest`-based tests by creating `tfvars` and `yaml` inventory pairs in `tests/fast/...` and tying them together with a `tftest.yaml` file. * **Legacy Tests:** Python-based tests using `pytest` and `tftest` are supported but example-based tests should be used whenever possible. ```bash @@ -183,6 +192,8 @@ ip_address = ( ) ``` +**5. YAML Interpolation:** In factory YAML files, use the `$` prefix convention to reference the lookup map keys (e.g., `parent: $folder_ids:teams/team-a`). + ### Tests Add a `context` test alongside existing module tests: @@ -213,9 +224,3 @@ Modify one existing README example (do not add a new one) to demonstrate context * **Locals Separation:** Use module-level locals for values referenced directly by resources/outputs. Use block-level "private" locals prefixed with an underscore (`_`) for intermediate transformations. * **Complex Transformations:** Move complex data transformations in `for` or `for_each` loops to `locals` to keep resource blocks clean. -## Preferred Workflow - -- Always break down complex requests into small, iterative tasks. -- For each task, propose the necessary edits and explicitly wait for user confirmation or discussion before proceeding. -- Always use the `replace` tool to both perform and cleanly display the proposed text modifications. Do not silently overwrite files or use shell commands for text edits. -- **CRITICAL:** NEVER use shell redirections (like `cat << 'EOF' > file` or `echo "text" > file`) or `sed`/`awk` scripts for editing files. Always use the `replace` tool for targeted edits or the `write_file` tool for new/small files. diff --git a/modules/GEMINI.md b/modules/GEMINI.md deleted file mode 100644 index ea2846883..000000000 --- a/modules/GEMINI.md +++ /dev/null @@ -1,12 +0,0 @@ -# Module testing notes - -## Generating test inventory files - -1. Specify `inventory=filename.yaml` in the `tftest` annotation in `README.md`. -2. Create the empty inventory file in `tests/modules//examples/` (note the underscore in module name, e.g., `compute_vm`). -3. Place the standard copyright blurb at the top. -4. Add `counts: { foo: 1 }` to the inventory file. -5. Run the specific test using `pytest "tests/examples/test_plan.py::test_example[terraform:modules/:Heading Name:Index]"`. -6. Use the test failure output to replace `counts` with the actual resource types and counts. -7. Add `values: { foo: 1 }` to the inventory file and run the test again. -8. Use the output to replace `values` with the actual mapped attributes. Remove unnecessary or overly verbose keys like large instance configurations, and use `{}` for resources where specific values don't need validation. \ No newline at end of file