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"
-}
-