diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 0166f936..ff7b420b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -163,7 +163,7 @@ resource "meshstack_building_block_definition" "this" { - **Cross-cutting concerns** like workload identity federation settings may be grouped into an `object({})` typed variable (e.g. `variable "workload_identity"`) when the fields are logically inseparable - Only `variable "meshstack"` and `variable "hub"` use shared `object({})` conventions across all integrations - Pin provider versions with `~> X.Y.Z` (allow patch updates, not minor/major). **Exception:** the `meshcloud/meshstack` provider is pre-1.0, so pin to the minor version with `~> 0.Y.0` (e.g. `~> 0.20.0`) -- Terraform baseline: `>= 1.11.0` to cover OpenTofu v1.11.0 with write-only/ephemeral attribute support +- Terraform baseline: `>= 1.12.0` to cover OpenTofu v1.12.0 with `const` variable support (requires OpenTofu ≥ 1.12 or Terraform ≥ 1.15) --- diff --git a/.github/workflows/update-module-refs.yml b/.github/workflows/update-module-refs.yml deleted file mode 100644 index a1d61ee5..00000000 --- a/.github/workflows/update-module-refs.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: update-module-refs - -on: - push: - branches: [main] - pull_request: - branches: [main] - -permissions: - contents: write - -jobs: - update-module-refs: - runs-on: ubuntu-latest - environment: ${{ github.event_name == 'push' && 'main' || '' }} - steps: - - uses: actions/checkout@v6 - with: - # Full history needed for git log to resolve commit SHAs - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.ref || github.ref_name }} - # On main: use SSH deploy key so the push is authenticated via the deploy key. - # On PRs: secret is empty, checkout falls back to GITHUB_TOKEN (push is dry-run anyway). - ssh-key: ${{ secrets.REPO_DEPLOY_KEY }} - - - uses: actions/setup-go@v6 - with: - go-version-file: tools/update-module-refs/go.mod - cache-dependency-path: tools/update-module-refs/go.sum - - - name: Build - working-directory: tools/update-module-refs - run: go build -o update-module-refs . - - - name: Update module refs - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - tools/update-module-refs/update-module-refs - - - name: Push ref updates${{ github.event_name == 'pull_request' && ' (dry-run)' || '' }} - run: | - git diff --patch-with-stat --color-words @{upstream} - git push ${{ github.event_name == 'pull_request' && '--dry-run' || '' }} diff --git a/modules/aks/github-connector/meshstack_integration.tf b/modules/aks/github-connector/meshstack_integration.tf index 6999a13e..00c32b34 100644 --- a/modules/aks/github-connector/meshstack_integration.tf +++ b/modules/aks/github-connector/meshstack_integration.tf @@ -33,6 +33,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true default = {} description = <<-EOT `git_ref`: Hub reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of meshcloud/meshstack-hub repo.
@@ -225,6 +226,8 @@ EOT } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/modules/aks/meshstack_integration.tf b/modules/aks/meshstack_integration.tf index bb1c52f3..ce5258f8 100644 --- a/modules/aks/meshstack_integration.tf +++ b/modules/aks/meshstack_integration.tf @@ -174,6 +174,8 @@ data "azuread_domains" "aad_domains" { } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/modules/aks/starterkit/meshstack_integration.tf b/modules/aks/starterkit/meshstack_integration.tf index eda249e7..3487400c 100644 --- a/modules/aks/starterkit/meshstack_integration.tf +++ b/modules/aks/starterkit/meshstack_integration.tf @@ -71,6 +71,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true default = {} description = <<-EOT `git_ref`: Hub reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of meshcloud/meshstack-hub repo.
@@ -330,6 +331,8 @@ EOT } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/modules/aws/route53-dns-alias-record/meshstack_integration.tf b/modules/aws/route53-dns-alias-record/meshstack_integration.tf index 7a128f0f..e0d9c772 100644 --- a/modules/aws/route53-dns-alias-record/meshstack_integration.tf +++ b/modules/aws/route53-dns-alias-record/meshstack_integration.tf @@ -45,6 +45,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true 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. @@ -63,7 +64,7 @@ output "building_block_definition" { data "meshstack_integrations" "integrations" {} module "backplane" { - source = "github.com/meshcloud/meshstack-hub//modules/aws/route53-dns-alias-record/backplane?ref=32698080a28bce13ba224334ad5bfbb2233236ae" + source = "github.com/meshcloud/meshstack-hub//modules/aws/route53-dns-alias-record/backplane?ref=${var.hub.git_ref}" hosted_zone_ids = var.hosted_zone_ids create_oidc_provider = var.create_oidc_provider @@ -226,7 +227,7 @@ resource "meshstack_building_block_definition" "this" { } terraform { - required_version = ">= 1.11.0" + required_version = ">= 1.12.0" required_providers { aws = { diff --git a/modules/aws/route53-dns-record/meshstack_integration.tf b/modules/aws/route53-dns-record/meshstack_integration.tf index 8f3b874d..ec21f5f9 100644 --- a/modules/aws/route53-dns-record/meshstack_integration.tf +++ b/modules/aws/route53-dns-record/meshstack_integration.tf @@ -45,6 +45,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true 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. @@ -63,7 +64,7 @@ output "building_block_definition" { data "meshstack_integrations" "integrations" {} module "backplane" { - source = "github.com/meshcloud/meshstack-hub//modules/aws/route53-dns-record/backplane?ref=32698080a28bce13ba224334ad5bfbb2233236ae" + source = "github.com/meshcloud/meshstack-hub//modules/aws/route53-dns-record/backplane?ref=${var.hub.git_ref}" hosted_zone_ids = var.hosted_zone_ids create_oidc_provider = var.create_oidc_provider @@ -220,7 +221,7 @@ resource "meshstack_building_block_definition" "this" { } terraform { - required_version = ">= 1.11.0" + required_version = ">= 1.12.0" required_providers { aws = { diff --git a/modules/aws/s3_bucket/meshstack_integration.tf b/modules/aws/s3_bucket/meshstack_integration.tf index 9f8bcdee..f8c3e946 100644 --- a/modules/aws/s3_bucket/meshstack_integration.tf +++ b/modules/aws/s3_bucket/meshstack_integration.tf @@ -25,6 +25,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true 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. @@ -41,7 +42,7 @@ output "building_block_definition" { } module "backplane" { - source = "github.com/meshcloud/meshstack-hub//modules/aws/s3_bucket/backplane?ref=b9c1f3f2201e7e22b04dbf71a3ceab7a0246a7b3" + source = "github.com/meshcloud/meshstack-hub//modules/aws/s3_bucket/backplane?ref=${var.hub.git_ref}" workload_identity_federation = { issuer = var.workload_identity.issuer @@ -147,7 +148,7 @@ resource "meshstack_building_block_definition" "this" { } terraform { - required_version = ">= 1.11.0" + required_version = ">= 1.12.0" required_providers { aws = { diff --git a/modules/azure/budget-alert/meshstack_integration.tf b/modules/azure/budget-alert/meshstack_integration.tf index b97619ed..18439344 100644 --- a/modules/azure/budget-alert/meshstack_integration.tf +++ b/modules/azure/budget-alert/meshstack_integration.tf @@ -38,6 +38,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true 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. @@ -56,7 +57,7 @@ output "building_block_definition" { data "meshstack_integrations" "integrations" {} module "backplane" { - source = "github.com/meshcloud/meshstack-hub//modules/azure/budget-alert/backplane?ref=226e58cb166002c7e505d6deb9e7bbf7e9c9edd1" + source = "github.com/meshcloud/meshstack-hub//modules/azure/budget-alert/backplane?ref=${var.hub.git_ref}" name = var.backplane_name scope = var.azure_scope @@ -216,7 +217,7 @@ resource "meshstack_building_block_definition" "this" { } terraform { - required_version = ">= 1.11.0" + required_version = ">= 1.12.0" required_providers { meshstack = { diff --git a/modules/azure/meshstack_integration.tf b/modules/azure/meshstack_integration.tf index d73184c4..2d734120 100644 --- a/modules/azure/meshstack_integration.tf +++ b/modules/azure/meshstack_integration.tf @@ -240,6 +240,8 @@ resource "meshstack_landingzone" "azure_default" { } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/modules/azure/service-principal/meshstack_integration.tf b/modules/azure/service-principal/meshstack_integration.tf index b7fb6c14..aa499e9b 100644 --- a/modules/azure/service-principal/meshstack_integration.tf +++ b/modules/azure/service-principal/meshstack_integration.tf @@ -33,6 +33,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true 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. @@ -51,7 +52,7 @@ output "building_block_definition" { data "meshstack_integrations" "integrations" {} module "backplane" { - source = "github.com/meshcloud/meshstack-hub//modules/azure/service-principal/backplane?ref=ce07c0a83c58ab924fc4ab3e62907dde1a44edf3" + source = "github.com/meshcloud/meshstack-hub//modules/azure/service-principal/backplane?ref=${var.hub.git_ref}" name = var.backplane_name scope = var.azure_scope @@ -251,7 +252,7 @@ resource "meshstack_building_block_definition" "this" { } terraform { - required_version = ">= 1.11.0" + required_version = ">= 1.12.0" required_providers { meshstack = { diff --git a/modules/azure/storage-account/meshstack_integration.tf b/modules/azure/storage-account/meshstack_integration.tf index 9b2ea684..a3933cfb 100644 --- a/modules/azure/storage-account/meshstack_integration.tf +++ b/modules/azure/storage-account/meshstack_integration.tf @@ -44,6 +44,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true 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. @@ -62,7 +63,7 @@ output "building_block_definition" { data "meshstack_integrations" "integrations" {} module "backplane" { - source = "github.com/meshcloud/meshstack-hub//modules/azure/storage-account/backplane?ref=0a6d313e509e1c9052712f0d9c41c2d0a96f9a39" + source = "github.com/meshcloud/meshstack-hub//modules/azure/storage-account/backplane?ref=${var.hub.git_ref}" name = var.backplane_name scope = var.azure_scope @@ -211,7 +212,7 @@ resource "meshstack_building_block_definition" "this" { } terraform { - required_version = ">= 1.11.0" + required_version = ">= 1.12.0" required_providers { meshstack = { diff --git a/modules/gcp/storage-bucket/meshstack_integration.tf b/modules/gcp/storage-bucket/meshstack_integration.tf index a8dde5e8..ff19bfdb 100644 --- a/modules/gcp/storage-bucket/meshstack_integration.tf +++ b/modules/gcp/storage-bucket/meshstack_integration.tf @@ -25,6 +25,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true 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. @@ -46,7 +47,7 @@ output "building_block_definition" { data "meshstack_integrations" "integrations" {} module "backplane" { - source = "github.com/meshcloud/meshstack-hub//modules/gcp/storage-bucket/backplane?ref=b9c1f3f2201e7e22b04dbf71a3ceab7a0246a7b3" + source = "github.com/meshcloud/meshstack-hub//modules/gcp/storage-bucket/backplane?ref=${var.hub.git_ref}" project_id = var.gcp_project_id workload_identity_federation = { @@ -176,6 +177,8 @@ EOT } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/modules/github/repository/meshstack_integration.tf b/modules/github/repository/meshstack_integration.tf index bee200df..5c23522d 100644 --- a/modules/github/repository/meshstack_integration.tf +++ b/modules/github/repository/meshstack_integration.tf @@ -26,6 +26,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true default = {} description = <<-EOT `git_ref`: Hub reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of meshcloud/meshstack-hub repo.
@@ -248,6 +249,8 @@ EOT } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/modules/kubernetes/manifest/meshstack_integration.tf b/modules/kubernetes/manifest/meshstack_integration.tf index cf2a108b..fc7a05e4 100644 --- a/modules/kubernetes/manifest/meshstack_integration.tf +++ b/modules/kubernetes/manifest/meshstack_integration.tf @@ -54,6 +54,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true 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. @@ -70,7 +71,7 @@ output "building_block_definition" { } module "backplane" { - source = "github.com/meshcloud/meshstack-hub//modules/kubernetes/manifest/backplane?ref=50756692c3b74dde5a2ec0b080e43108e0d0c9d9" + source = "github.com/meshcloud/meshstack-hub//modules/kubernetes/manifest/backplane?ref=${var.hub.git_ref}" kubeconfig_admin = var.kubernetes_kubeconfig namespace = var.kubernetes_namespace @@ -196,6 +197,8 @@ resource "meshstack_building_block_definition" "this" { } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/modules/meshstack/noop/meshstack_integration.tf b/modules/meshstack/noop/meshstack_integration.tf index c1ddceed..870f035f 100644 --- a/modules/meshstack/noop/meshstack_integration.tf +++ b/modules/meshstack/noop/meshstack_integration.tf @@ -11,6 +11,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true 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. @@ -189,6 +190,8 @@ resource "meshstack_building_block_definition" "this" { } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/modules/ske/forgejo-connector/meshstack_integration.tf b/modules/ske/forgejo-connector/meshstack_integration.tf index 8d5ee192..3ccda16d 100644 --- a/modules/ske/forgejo-connector/meshstack_integration.tf +++ b/modules/ske/forgejo-connector/meshstack_integration.tf @@ -55,6 +55,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true default = {} description = <<-EOT `git_ref`: Hub release reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of meshcloud/meshstack-hub repo.
@@ -262,6 +263,8 @@ resource "meshstack_building_block_definition" "this" { } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/modules/ske/ske-starterkit/meshstack_integration.tf b/modules/ske/ske-starterkit/meshstack_integration.tf index e88794ae..ce460d92 100644 --- a/modules/ske/ske-starterkit/meshstack_integration.tf +++ b/modules/ske/ske-starterkit/meshstack_integration.tf @@ -66,6 +66,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true default = {} description = <<-EOT `git_ref`: Hub reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of meshcloud/meshstack-hub repo.
@@ -263,6 +264,8 @@ resource "meshstack_building_block_definition" "this" { } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/modules/stackit/git-repository/meshstack_integration.tf b/modules/stackit/git-repository/meshstack_integration.tf index 6154c621..d0703807 100644 --- a/modules/stackit/git-repository/meshstack_integration.tf +++ b/modules/stackit/git-repository/meshstack_integration.tf @@ -40,6 +40,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true default = {} description = <<-EOT `git_ref`: Hub release reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of meshcloud/meshstack-hub repo.
@@ -56,7 +57,7 @@ output "building_block_definition" { } module "backplane" { - source = "github.com/meshcloud/meshstack-hub//modules/stackit/git-repository/backplane?ref=a3843c80c76c4a0298769eea8d93807bb2b271fc" + source = "github.com/meshcloud/meshstack-hub//modules/stackit/git-repository/backplane?ref=${var.hub.git_ref}" forgejo_base_url = var.forgejo_base_url forgejo_token = var.forgejo_token @@ -264,6 +265,8 @@ resource "meshstack_building_block_definition" "this" { } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/modules/stackit/meshstack_integration.tf b/modules/stackit/meshstack_integration.tf index 15acd77a..eb4bcb93 100644 --- a/modules/stackit/meshstack_integration.tf +++ b/modules/stackit/meshstack_integration.tf @@ -43,6 +43,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true default = {} description = <<-EOT `git_ref`: Hub release reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of meshcloud/meshstack-hub repo. @@ -51,7 +52,7 @@ variable "hub" { } module "backplane" { - source = "github.com/meshcloud/meshstack-hub//modules/stackit/project/backplane?ref=f157cea7511aa6e8a6c0e3197a6acaa723488b2b" + source = "github.com/meshcloud/meshstack-hub//modules/stackit/project/backplane?ref=${var.hub.git_ref}" project_id = var.stackit_project_id organization_id = var.stackit_organization_id @@ -236,6 +237,8 @@ resource "meshstack_building_block_definition" "this" { } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/modules/stackit/storage-bucket/meshstack_integration.tf b/modules/stackit/storage-bucket/meshstack_integration.tf index fd198d24..922b0f38 100644 --- a/modules/stackit/storage-bucket/meshstack_integration.tf +++ b/modules/stackit/storage-bucket/meshstack_integration.tf @@ -15,6 +15,7 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) + const = true default = {} description = <<-EOT `git_ref`: Hub release reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of meshcloud/meshstack-hub repo. @@ -31,7 +32,7 @@ output "building_block_definition" { } module "backplane" { - source = "github.com/meshcloud/meshstack-hub//modules/stackit/storage-bucket/backplane?ref=aeb06b72d8580f0581db36a26569d07d88320858" + source = "github.com/meshcloud/meshstack-hub//modules/stackit/storage-bucket/backplane?ref=${var.hub.git_ref}" project_id = var.stackit_project_id } @@ -169,6 +170,8 @@ resource "meshstack_building_block_definition" "this" { } terraform { + required_version = ">= 1.12.0" + required_providers { meshstack = { source = "meshcloud/meshstack" diff --git a/tools/update-module-refs/.gitignore b/tools/update-module-refs/.gitignore deleted file mode 100644 index 69d09e6a..00000000 --- a/tools/update-module-refs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/update-module-refs diff --git a/tools/update-module-refs/.golangci.yml b/tools/update-module-refs/.golangci.yml deleted file mode 100644 index c779674d..00000000 --- a/tools/update-module-refs/.golangci.yml +++ /dev/null @@ -1,57 +0,0 @@ -version: "2" - -issues: - max-same-issues: 0 - -formatters: - enable: - - gci - - gofmt - settings: - gci: - sections: - - standard - - default - - localmodule - -linters: - default: none - enable: - - depguard - - durationcheck - - errcheck - - copyloopvar - - forcetypeassert - - godot - - ineffassign - - makezero - - misspell - - nilerr - - predeclared - - staticcheck - - usetesting - - unconvert - - unparam - - unused - - govet - settings: - depguard: - rules: - main: - files: - - "!$test" - list-mode: strict - allow: - - $gostd - - github.com/meshcloud/meshstack-hub/tools/update-module-refs - - github.com/hashicorp/hcl/v2 - - github.com/zclconf/go-cty - test: - files: - - "$test" - list-mode: strict - allow: - - $gostd - - github.com/meshcloud/meshstack-hub/tools/update-module-refs - - github.com/hashicorp/hcl/v2 - - github.com/stretchr/testify diff --git a/tools/update-module-refs/Taskfile.yml b/tools/update-module-refs/Taskfile.yml deleted file mode 100644 index c24f0cb7..00000000 --- a/tools/update-module-refs/Taskfile.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: '3' - -tasks: - build: - cmds: - - go build . - - lint: - desc: Run golangci-lint - cmds: - - golangci-lint run {{.CLI_ARGS}} - - test: - cmds: - - go test ./... -v {{.CLI_ARGS}} - diff --git a/tools/update-module-refs/dependency/dep.go b/tools/update-module-refs/dependency/dep.go deleted file mode 100644 index 1925626d..00000000 --- a/tools/update-module-refs/dependency/dep.go +++ /dev/null @@ -1,69 +0,0 @@ -package dependency - -import "fmt" - -// Dependency represents a node with dependencies on other nodes. -type Dependency[T comparable] struct { - This T - DependsOn []T -} - -// Dependencies is a collection of Dependency nodes. -type Dependencies[T comparable] []Dependency[T] - -// CalculateWaves returns the deps as waves starting with dependencies having empty DependsOn, -// then the next wave containing deps only on the previous wave, and so on. -func (deps Dependencies[T]) CalculateWaves() ([][]T, error) { - // Build a set of all known nodes and their remaining dependencies. - remaining := make(map[T]map[T]bool, len(deps)) - known := make(map[T]bool, len(deps)) - - for _, d := range deps { - known[d.This] = true - } - - for _, d := range deps { - needs := make(map[T]bool) - for _, dep := range d.DependsOn { - // Only track dependencies on nodes within the graph. - if known[dep] { - needs[dep] = true - } - } - remaining[d.This] = needs - } - - resolved := make(map[T]bool) - var waves [][]T - - for len(remaining) > 0 { - // Find all nodes whose dependencies are fully resolved. - var wave []T - for node, needs := range remaining { - ready := true - for dep := range needs { - if !resolved[dep] { - ready = false - break - } - } - if ready { - wave = append(wave, node) - } - } - - if len(wave) == 0 { - return nil, fmt.Errorf("circular dependency detected among remaining nodes") - } - - // Mark wave nodes as resolved and remove from remaining. - for _, node := range wave { - resolved[node] = true - delete(remaining, node) - } - - waves = append(waves, wave) - } - - return waves, nil -} diff --git a/tools/update-module-refs/dependency/dep_test.go b/tools/update-module-refs/dependency/dep_test.go deleted file mode 100644 index 57dbd467..00000000 --- a/tools/update-module-refs/dependency/dep_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package dependency_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/meshcloud/meshstack-hub/tools/update-module-refs/dependency" -) - -func TestCalculateWaves_Linear(t *testing.T) { - // c -> b -> a (a is leaf) - deps := dependency.Dependencies[string]{ - {This: "a"}, - {This: "b", DependsOn: []string{"a"}}, - {This: "c", DependsOn: []string{"b"}}, - } - - waves, err := deps.CalculateWaves() - require.NoError(t, err) - require.Len(t, waves, 3) - - assert.ElementsMatch(t, []string{"a"}, waves[0]) - assert.ElementsMatch(t, []string{"b"}, waves[1]) - assert.ElementsMatch(t, []string{"c"}, waves[2]) -} - -func TestCalculateWaves_Diamond(t *testing.T) { - // d -> b, c; b -> a; c -> a - deps := dependency.Dependencies[string]{ - {This: "a"}, - {This: "b", DependsOn: []string{"a"}}, - {This: "c", DependsOn: []string{"a"}}, - {This: "d", DependsOn: []string{"b", "c"}}, - } - - waves, err := deps.CalculateWaves() - require.NoError(t, err) - require.Len(t, waves, 3) - - assert.ElementsMatch(t, []string{"a"}, waves[0]) - assert.ElementsMatch(t, []string{"b", "c"}, waves[1]) - assert.ElementsMatch(t, []string{"d"}, waves[2]) -} - -func TestCalculateWaves_AllIndependent(t *testing.T) { - deps := dependency.Dependencies[string]{ - {This: "x"}, - {This: "y"}, - {This: "z"}, - } - - waves, err := deps.CalculateWaves() - require.NoError(t, err) - require.Len(t, waves, 1) - - assert.ElementsMatch(t, []string{"x", "y", "z"}, waves[0]) -} - -func TestCalculateWaves_Circular(t *testing.T) { - deps := dependency.Dependencies[string]{ - {This: "a", DependsOn: []string{"b"}}, - {This: "b", DependsOn: []string{"a"}}, - } - - _, err := deps.CalculateWaves() - assert.Error(t, err) - assert.Contains(t, err.Error(), "circular dependency") -} - -func TestCalculateWaves_ExternalDeps(t *testing.T) { - // b depends on "ext" which is not in the deps list. - deps := dependency.Dependencies[string]{ - {This: "a"}, - {This: "b", DependsOn: []string{"a", "ext"}}, - } - - waves, err := deps.CalculateWaves() - require.NoError(t, err) - require.Len(t, waves, 2) - - assert.ElementsMatch(t, []string{"a"}, waves[0]) - assert.ElementsMatch(t, []string{"b"}, waves[1]) -} diff --git a/tools/update-module-refs/git/git.go b/tools/update-module-refs/git/git.go deleted file mode 100644 index 6883306c..00000000 --- a/tools/update-module-refs/git/git.go +++ /dev/null @@ -1,97 +0,0 @@ -package git - -import ( - "fmt" - "os/exec" - "path/filepath" - "strings" -) - -// Log returns the commit SHA of the most recent commit that touched dirPath. -// dirPath can be absolute or relative to the repository root. -func Log(dirPath string) (string, error) { - root, err := repoRoot() - if err != nil { - return "", err - } - - relPath, err := toRelPath(root, dirPath) - if err != nil { - return "", err - } - - cmd := exec.Command("git", "log", "-n1", "--pretty=%H", "--", relPath) //nolint:gosec // dirPath is not user input - cmd.Dir = root - - out, err := cmd.Output() - if err != nil { - return "", fmt.Errorf("git log for %q: %w", relPath, err) - } - - sha := strings.TrimSpace(string(out)) - if sha == "" { - return "", fmt.Errorf("git log for %q: no commits found", relPath) - } - - return sha, nil -} - -// AddAndCommit stages all changes under dirPath and commits with the given message. -// dirPath can be absolute or relative to the repository root. -func AddAndCommit(dirPath string, message string) error { - root, err := repoRoot() - if err != nil { - return err - } - - relPath, err := toRelPath(root, dirPath) - if err != nil { - return err - } - - add := exec.Command("git", "add", relPath) //nolint:gosec // dirPath is not user input - add.Dir = root - if out, err := add.CombinedOutput(); err != nil { - return fmt.Errorf("git add %q: %w\n%s", relPath, err, out) - } - - // Check if there are staged changes before committing. - diff := exec.Command("git", "diff", "--cached", "--quiet") - diff.Dir = root - if err := diff.Run(); err == nil { - // Exit code 0 means no staged changes. - return nil - } - - commit := exec.Command("git", "commit", "-m", message) //nolint:gosec // message is not user input - commit.Dir = root - if out, err := commit.CombinedOutput(); err != nil { - return fmt.Errorf("git commit: %w\n%s", err, out) - } - - return nil -} - -func repoRoot() (string, error) { - cmd := exec.Command("git", "rev-parse", "--show-toplevel") - out, err := cmd.Output() - if err != nil { - return "", fmt.Errorf("git rev-parse --show-toplevel: %w", err) - } - - return strings.TrimSpace(string(out)), nil -} - -// toRelPath converts dirPath to a path relative to root if it is absolute. -func toRelPath(root, dirPath string) (string, error) { - if filepath.IsAbs(dirPath) { - rel, err := filepath.Rel(root, dirPath) - if err != nil { - return "", fmt.Errorf("making %q relative to %q: %w", dirPath, root, err) - } - - return rel, nil - } - - return dirPath, nil -} diff --git a/tools/update-module-refs/git/git_test.go b/tools/update-module-refs/git/git_test.go deleted file mode 100644 index bd0558e6..00000000 --- a/tools/update-module-refs/git/git_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package git_test - -import ( - "os" - "os/exec" - "path/filepath" - "regexp" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/meshcloud/meshstack-hub/tools/update-module-refs/git" -) - -func TestLog(t *testing.T) { - sha, err := git.Log(".") - require.NoError(t, err) - assert.Regexp(t, regexp.MustCompile(`^[0-9a-f]{40}$`), sha) -} - -func TestLogAbsolutePath(t *testing.T) { - // Resolve repo root to get an absolute path. - out, err := exec.Command("git", "rev-parse", "--show-toplevel").Output() - require.NoError(t, err) - - absPath := strings.TrimSpace(string(out)) - sha, err := git.Log(absPath) - require.NoError(t, err) - assert.Regexp(t, regexp.MustCompile(`^[0-9a-f]{40}$`), sha) -} - -func TestAddAndCommit(t *testing.T) { - // Create a temporary git repo for an isolated test. - dir := t.TempDir() - - run := func(args ...string) { - t.Helper() - cmd := exec.Command(args[0], args[1:]...) //nolint:gosec - cmd.Dir = dir - out, err := cmd.CombinedOutput() - require.NoError(t, err, "command %v failed: %s", args, out) - } - - run("git", "init") - run("git", "config", "user.email", "test@test.com") - run("git", "config", "user.name", "Test") - - // Create an initial commit so the branch exists. - require.NoError(t, os.WriteFile(filepath.Join(dir, "init.txt"), []byte("init"), 0o644)) - run("git", "add", ".") - run("git", "commit", "-m", "initial") - - // Create a file and commit it via AddAndCommit. - subDir := filepath.Join(dir, "sub") - require.NoError(t, os.MkdirAll(subDir, 0o755)) - require.NoError(t, os.WriteFile(filepath.Join(subDir, "test.txt"), []byte("hello"), 0o644)) - - // Chdir into the temp repo so git commands resolve to it. - t.Chdir(dir) - - err := git.AddAndCommit("sub", "test commit") - require.NoError(t, err) - - // Verify the commit exists. - cmd := exec.Command("git", "log", "-n1", "--pretty=%s") - cmd.Dir = dir - out, err := cmd.Output() - require.NoError(t, err) - assert.Equal(t, "test commit", strings.TrimSpace(string(out))) -} diff --git a/tools/update-module-refs/go.mod b/tools/update-module-refs/go.mod deleted file mode 100644 index a0f4a626..00000000 --- a/tools/update-module-refs/go.mod +++ /dev/null @@ -1,23 +0,0 @@ -module github.com/meshcloud/meshstack-hub/tools/update-module-refs - -go 1.25.7 - -require ( - github.com/hashicorp/hcl/v2 v2.24.0 - github.com/stretchr/testify v1.11.1 - github.com/zclconf/go-cty v1.16.3 -) - -require ( - github.com/agext/levenshtein v1.2.1 // indirect - github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/tools/update-module-refs/go.sum b/tools/update-module-refs/go.sum deleted file mode 100644 index 5e4ae799..00000000 --- a/tools/update-module-refs/go.sum +++ /dev/null @@ -1,34 +0,0 @@ -github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= -github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= -github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= -github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= -github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE= -github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk= -github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= -github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= -github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tools/update-module-refs/main.go b/tools/update-module-refs/main.go deleted file mode 100644 index 151e314f..00000000 --- a/tools/update-module-refs/main.go +++ /dev/null @@ -1,231 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "io/fs" - "log" - "os" - "path/filepath" - "strings" - - "github.com/meshcloud/meshstack-hub/tools/update-module-refs/dependency" - "github.com/meshcloud/meshstack-hub/tools/update-module-refs/git" - "github.com/meshcloud/meshstack-hub/tools/update-module-refs/tf" -) - -// modulesDir is the subdirectory (relative to the repo root) that contains all modules. -const modulesDir = "modules" - -func main() { - repoURL := flag.String("repo", "github.com/meshcloud/meshstack-hub", "repository host+path to match module sources against") - dryRun := flag.Bool("dry-run", false, "only print what would be updated, do not write or commit") - flag.Parse() - - cwd, err := os.Getwd() - if err != nil { - log.Fatalf("failed to get working directory: %v", err) - } - - fsys := os.DirFS(cwd) - modules, err := scanModules(fsys, *repoURL, modulesDir) - if err != nil { - log.Fatalf("failed to scan modules: %v", err) - } - - log.Printf("found modules in %d directories", len(modules)) - - deps := buildDependencies(modules, *repoURL) - waves, err := deps.CalculateWaves() - if err != nil { - log.Fatalf("failed to calculate dependency waves: %v", err) - } - - log.Printf("calculated %d dependency waves", len(waves)) - - if *dryRun { - log.Println("WARNING: dry-run mode — refs shown may not be accurate as changes are not committed") - } - - for i, wave := range waves { - log.Printf("wave %d: %v", i, wave) - - for _, dir := range wave { - if err := processDirectory(cwd, dir, modules[dir], *repoURL, *dryRun); err != nil { - log.Fatalf("failed to process %q: %v", dir, err) - } - } - } -} - -func processDirectory(cwd, dir string, files []tf.File, repoURL string, dryRun bool) error { - dirChanged := false - - for _, f := range files { - fileChanged := false - - for _, m := range f.Modules { - depPath := extractModulePath(repoURL, m.Source()) - - sha, err := git.Log(depPath) - if err != nil { - return fmt.Errorf("git log for %q: %w", depPath, err) - } - - newSource := repoURL + "//" + depPath + "?ref=" + sha - if m.Source() == newSource { - log.Printf(" %s/%s -> %s (up to date)", dir, m.Name, depPath) - continue - } - - log.Printf(" %s/%s -> %s ref=%s", dir, m.Name, depPath, sha) - m.SetSource(newSource) - fileChanged = true - } - - if !fileChanged { - continue - } - - dirChanged = true - - if dryRun { - log.Printf(" would write %s", filepath.Join(dir, f.Path)) - continue - } - - outPath := filepath.Join(cwd, dir, f.Path) - if err := os.WriteFile(outPath, f.HCL.Bytes(), 0o644); err != nil { - return fmt.Errorf("writing %q: %w", outPath, err) - } - - log.Printf(" wrote %s", filepath.Join(dir, f.Path)) - } - - if !dirChanged { - log.Printf(" %s: all refs up to date, nothing to commit", dir) - return nil - } - - commitMsg := fmt.Sprintf("chore: update module refs in %s", dir) - - if dryRun { - log.Printf(" would commit %s: %q", dir, commitMsg) - return nil - } - - absDir := filepath.Join(cwd, dir) - if err := git.AddAndCommit(absDir, commitMsg); err != nil { - return fmt.Errorf("commit %q: %w", dir, err) - } - - log.Printf(" committed %s", dir) - - return nil -} - -// scanModules walks all directories under root in fsys and returns only those -// containing terraform modules with sources matching repoURL. -// root is the subdirectory to scan (e.g. "modules"), so returned map keys are -// root-prefixed (e.g. "modules/aws/budget/buildingblock") matching the paths -// produced by extractModulePath. -func scanModules(fsys fs.FS, repoURL string, root string) (map[string][]tf.File, error) { - result := make(map[string][]tf.File) - - err := fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - - if !d.IsDir() { - return nil - } - - sub, err := fs.Sub(fsys, path) - if err != nil { - return err - } - - files, err := tf.Load(sub) - if err != nil { - return err - } - - // Filter to only files that have at least one module source matching repoURL. - var matched []tf.File - for _, f := range files { - var matchingModules []tf.ModuleSource - for _, m := range f.Modules { - if strings.HasPrefix(m.Source(), repoURL+"//") { - matchingModules = append(matchingModules, m) - } - } - - if len(matchingModules) > 0 { - matched = append(matched, tf.File{ - Path: f.Path, - HCL: f.HCL, - Modules: matchingModules, - }) - } - } - - if len(matched) > 0 { - result[path] = matched - } - - return nil - }) - - return result, err -} - -// buildDependencies creates dependency graph entries from the module map. -// Each directory becomes a node, its dependencies are the repo-internal module paths it references. -func buildDependencies(modules map[string][]tf.File, repoURL string) dependency.Dependencies[string] { - var deps dependency.Dependencies[string] - - for dir, files := range modules { - seen := make(map[string]bool) - - for _, f := range files { - for _, m := range f.Modules { - depPath := extractModulePath(repoURL, m.Source()) - if depPath != "" && !seen[depPath] { - seen[depPath] = true - } - } - } - - var dependsOn []string - for dep := range seen { - dependsOn = append(dependsOn, dep) - } - - deps = append(deps, dependency.Dependency[string]{ - This: dir, - DependsOn: dependsOn, - }) - } - - return deps -} - -// extractModulePath extracts the repo-relative path from a terraform module source. -// e.g. "github.com/meshcloud/meshstack-hub//modules/azure/postgresql?ref=v1.0.0" -// returns "modules/azure/postgresql". -func extractModulePath(repoURL, source string) string { - prefix := repoURL + "//" - if !strings.HasPrefix(source, prefix) { - return "" - } - - path := strings.TrimPrefix(source, prefix) - - // Strip ?ref=... query parameter. - if idx := strings.Index(path, "?"); idx != -1 { - path = path[:idx] - } - - return path -} diff --git a/tools/update-module-refs/main_test.go b/tools/update-module-refs/main_test.go deleted file mode 100644 index f2068ba7..00000000 --- a/tools/update-module-refs/main_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -const testRepoURL = "github.com/meshcloud/meshstack-hub" - -// TestScanAndBuildDependencies verifies that scanModules correctly produces -// modules/-prefixed directory keys (matching extractModulePath output) and -// that buildDependencies + CalculateWaves orders them into the correct waves. -// -// Fixture layout (testdata/modules/): -// -// aws/budget/buildingblock — no hub deps → wave 0 -// azure/postgresql/buildingblock — depends on aws/budget → wave 1 -// stackit/project/buildingblock — depends on azure/postgresql → wave 2 -func TestScanAndBuildDependencies(t *testing.T) { - fsys := os.DirFS("testdata") - - modules, err := scanModules(fsys, testRepoURL, "modules") - require.NoError(t, err) - - // All three buildingblock directories should be found. - assert.Len(t, modules, 2, "only directories with hub module references should be scanned") - - // Keys must be modules/-prefixed to match extractModulePath output. - for dir := range modules { - assert.True(t, len(dir) > len("modules/") && dir[:len("modules/")] == "modules/", - "scan key %q must be prefixed with 'modules/'", dir) - } - - // azure and stackit dirs reference hub modules; aws/budget has none. - assert.Contains(t, modules, "modules/azure/postgresql/buildingblock") - assert.Contains(t, modules, "modules/stackit/project/buildingblock") - - deps := buildDependencies(modules, testRepoURL) - waves, err := deps.CalculateWaves() - require.NoError(t, err) - - // Should produce at least 2 waves (azure depends on aws, stackit depends on azure). - require.GreaterOrEqual(t, len(waves), 2, "expected at least 2 dependency waves") - - // Flatten waves into ordered positions. - position := map[string]int{} - for i, wave := range waves { - for _, dir := range wave { - position[dir] = i - } - } - - azurePos := position["modules/azure/postgresql/buildingblock"] - stackitPos := position["modules/stackit/project/buildingblock"] - - assert.Less(t, azurePos, stackitPos, - "azure/postgresql must be updated before stackit/project") -} diff --git a/tools/update-module-refs/testdata/modules/aws/budget/buildingblock/main.tf b/tools/update-module-refs/testdata/modules/aws/budget/buildingblock/main.tf deleted file mode 100644 index ea6bfb52..00000000 --- a/tools/update-module-refs/testdata/modules/aws/budget/buildingblock/main.tf +++ /dev/null @@ -1,6 +0,0 @@ -# wave 0 — no dependencies on other hub modules -variable "hub" { - type = object({ - git_ref = string - }) -} diff --git a/tools/update-module-refs/testdata/modules/azure/postgresql/buildingblock/main.tf b/tools/update-module-refs/testdata/modules/azure/postgresql/buildingblock/main.tf deleted file mode 100644 index 3fea0903..00000000 --- a/tools/update-module-refs/testdata/modules/azure/postgresql/buildingblock/main.tf +++ /dev/null @@ -1,11 +0,0 @@ -# wave 1 — depends on aws/budget -variable "hub" { - type = object({ - git_ref = string - }) -} - -module "budget" { - source = "github.com/meshcloud/meshstack-hub//modules/aws/budget/buildingblock?ref=abc123" - hub = var.hub -} diff --git a/tools/update-module-refs/testdata/modules/stackit/project/buildingblock/main.tf b/tools/update-module-refs/testdata/modules/stackit/project/buildingblock/main.tf deleted file mode 100644 index de9a7764..00000000 --- a/tools/update-module-refs/testdata/modules/stackit/project/buildingblock/main.tf +++ /dev/null @@ -1,11 +0,0 @@ -# wave 2 — depends on azure/postgresql -variable "hub" { - type = object({ - git_ref = string - }) -} - -module "postgresql" { - source = "github.com/meshcloud/meshstack-hub//modules/azure/postgresql/buildingblock?ref=abc123" - hub = var.hub -} diff --git a/tools/update-module-refs/tf/load.go b/tools/update-module-refs/tf/load.go deleted file mode 100644 index fb6e14ef..00000000 --- a/tools/update-module-refs/tf/load.go +++ /dev/null @@ -1,102 +0,0 @@ -package tf - -import ( - "fmt" - "io/fs" - "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclwrite" - "github.com/zclconf/go-cty/cty" -) - -// File represents a parsed .tf file containing module blocks. -type File struct { - // Path is the file path relative to the walked fs.FS root. - Path string - // HCL is the underlying hclwrite file for serialization. - HCL *hclwrite.File - // Modules are the module blocks found in this file. - Modules []ModuleSource -} - -// ModuleSource represents a module block found in a .tf file. -type ModuleSource struct { - // Name is the module block label (e.g. "github_repo_bbd"). - Name string - // Body is the hclwrite body of the module block. - Body *hclwrite.Body -} - -// Source returns the raw value of the source attribute. -func (m ModuleSource) Source() string { - srcAttr := m.Body.GetAttribute("source") - if srcAttr == nil { - return "" - } - - src := strings.TrimSpace(string(srcAttr.Expr().BuildTokens(nil).Bytes())) - src = strings.Trim(src, `"`) - return src -} - -// SetSource updates the source attribute value. -func (m ModuleSource) SetSource(source string) { - m.Body.SetAttributeValue("source", cty.StringVal(source)) -} - -// Load reads all *.tf files in the directory represented by fsys, parses them -// with hclwrite, and extracts module blocks with their source attributes. -func Load(fsys fs.FS) ([]File, error) { - entries, err := fs.ReadDir(fsys, ".") - if err != nil { - return nil, err - } - - var results []File - - for _, entry := range entries { - if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".tf") { - continue - } - - content, err := fs.ReadFile(fsys, entry.Name()) - if err != nil { - return nil, err - } - - file, diags := hclwrite.ParseConfig(content, entry.Name(), hcl.InitialPos) - if diags.HasErrors() { - return nil, fmt.Errorf("parsing %s: %s", entry.Name(), diags.Error()) - } - - var modules []ModuleSource - for _, block := range file.Body().Blocks() { - if block.Type() != "module" { - continue - } - labels := block.Labels() - if len(labels) == 0 { - continue - } - if block.Body().GetAttribute("source") == nil { - continue - } - - modules = append(modules, ModuleSource{ - Name: labels[0], - Body: block.Body(), - }) - } - - if len(modules) > 0 { - results = append(results, File{ - Path: entry.Name(), - HCL: file, - Modules: modules, - }) - } - } - - return results, nil -} diff --git a/tools/update-module-refs/tf/load_test.go b/tools/update-module-refs/tf/load_test.go deleted file mode 100644 index 4225ce88..00000000 --- a/tools/update-module-refs/tf/load_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package tf_test - -import ( - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/meshcloud/meshstack-hub/tools/update-module-refs/tf" -) - -func TestLoad(t *testing.T) { - fsys := os.DirFS("testdata/simple") - files, err := tf.Load(fsys) - require.NoError(t, err) - - // Collect all modules across files. - type moduleInfo struct { - path string - source string - } - found := map[string]moduleInfo{} - for _, f := range files { - for _, m := range f.Modules { - found[m.Name] = moduleInfo{path: f.Path, source: m.Source()} - } - } - - assert.Len(t, found, 3) - - assert.Equal(t, moduleInfo{ - path: "main.tf", - source: "github.com/meshcloud/meshstack-hub//modules/github/repository?ref=main", - }, found["github_repo_bbd"]) - - assert.Equal(t, moduleInfo{ - path: "main.tf", - source: "./backplane", - }, found["backplane"]) - - assert.Equal(t, moduleInfo{ - path: "main.tf", - source: "github.com/meshcloud/meshstack-hub//modules/azure/postgresql?ref=v1.0.0", - }, found["postgresql_bbd"]) - - // Verify HCL file is present for serialization. - for _, f := range files { - assert.NotNil(t, f.HCL, "file %q: HCL should not be nil", f.Path) - } -} - -func TestLoadInvalidHCL(t *testing.T) { - fsys := os.DirFS("testdata/invalid") - _, err := tf.Load(fsys) - require.Error(t, err) - assert.Contains(t, err.Error(), "parsing main.tf") -} diff --git a/tools/update-module-refs/tf/testdata/invalid/main.tf b/tools/update-module-refs/tf/testdata/invalid/main.tf deleted file mode 100644 index 4a377be6..00000000 --- a/tools/update-module-refs/tf/testdata/invalid/main.tf +++ /dev/null @@ -1 +0,0 @@ -this is not valid HCL {{{ diff --git a/tools/update-module-refs/tf/testdata/simple/main.tf b/tools/update-module-refs/tf/testdata/simple/main.tf deleted file mode 100644 index 30f7f28b..00000000 --- a/tools/update-module-refs/tf/testdata/simple/main.tf +++ /dev/null @@ -1,25 +0,0 @@ -variable "hub" { - type = object({ - git_ref = string - }) -} - -module "github_repo_bbd" { - source = "github.com/meshcloud/meshstack-hub//modules/github/repository?ref=main" - - hub = var.hub -} - -module "backplane" { - source = "./backplane" -} - -resource "meshstack_building_block_definition" "test" { - metadata = { - owned_by_workspace = "test" - } -} -module "postgresql_bbd" { - source = "github.com/meshcloud/meshstack-hub//modules/azure/postgresql?ref=v1.0.0" -} -