From 30a78ba93ada58a39741aed2fa38b9a2bfb37acf Mon Sep 17 00:00:00 2001 From: Thibault Le Ouay Ducasse Date: Sat, 25 Jul 2026 17:43:05 +0200 Subject: [PATCH] terraform generate improve --- internal/cmd/app.go | 2 +- internal/cmd/app_test.go | 4 +-- internal/terraform/generate_test.go | 41 +++++++++++++++++++++++++++-- internal/terraform/hcl.go | 26 +++++++++++++----- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/internal/cmd/app.go b/internal/cmd/app.go index a5abb4a..c30a97f 100644 --- a/internal/cmd/app.go +++ b/internal/cmd/app.go @@ -44,7 +44,7 @@ Get started: openstatus pl list List your private locations https://docs.openstatus.dev | https://github.com/openstatusHQ/cli/issues/new`, - Version: "v1.3.0", + Version: "v1.3.1", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "json", diff --git a/internal/cmd/app_test.go b/internal/cmd/app_test.go index 5cc720c..190c162 100644 --- a/internal/cmd/app_test.go +++ b/internal/cmd/app_test.go @@ -20,8 +20,8 @@ func Test_NewApp(t *testing.T) { t.Errorf("Expected app name 'openstatus', got %s", app.Name) } - if app.Version != "v1.3.0" { - t.Errorf("Expected version 'v1.3.0', got %s", app.Version) + if app.Version != "v1.3.1" { + t.Errorf("Expected version 'v1.3.1', got %s", app.Version) } if !app.Suggest { diff --git a/internal/terraform/generate_test.go b/internal/terraform/generate_test.go index f6fc83c..a9bd4d4 100644 --- a/internal/terraform/generate_test.go +++ b/internal/terraform/generate_test.go @@ -504,10 +504,25 @@ func TestGenerateStatusPagesFile_EmailDomainAccess(t *testing.T) { content := string(gen.GenerateStatusPagesFile().Bytes()) mustContain(t, content, `access_type = "email-domain"`) - mustContain(t, content, `auth_email_domains = ["acme.com", "example.com"]`) + mustContain(t, content, `auth_email_domains = ["example.com", "acme.com"]`) mustNotContain(t, content, "REPLACE_ME") } +func TestGenerateStatusPagesFile_EmailDomainsDeduped(t *testing.T) { + page := &status_pagev1.StatusPage{} + page.SetId("p1") + page.SetTitle("Internal") + page.SetSlug("internal") + page.SetAccessType(status_pagev1.PageAccessType_PAGE_ACCESS_TYPE_AUTHENTICATED) + page.SetAuthEmailDomains([]string{"example.com", "acme.com", "example.com"}) + + data := &WorkspaceData{StatusPages: []StatusPageData{{Page: page}}} + gen := NewGenerator(data) + content := string(gen.GenerateStatusPagesFile().Bytes()) + + mustContain(t, content, `auth_email_domains = ["example.com", "acme.com"]`) +} + func TestGenerateStatusPagesFile_EmailDomainEmptyFallback(t *testing.T) { page := &status_pagev1.StatusPage{} page.SetId("p1") @@ -543,10 +558,32 @@ func TestGenerateStatusPagesFile_ThemeLocaleAllowIndex(t *testing.T) { mustContain(t, content, `theme = "dark"`) mustContain(t, content, `default_locale = "fr"`) - mustContain(t, content, `locales = ["en", "fr"]`) + mustContain(t, content, `locales = ["fr", "en"]`) mustContain(t, content, `allow_index = true`) } +// Regression: the API can return duplicate locales. Emitting them made apply fail +// with "Provider produced inconsistent result after apply" once the API deduped. +func TestGenerateStatusPagesFile_LocalesDeduped(t *testing.T) { + page := &status_pagev1.StatusPage{} + page.SetId("p1") + page.SetTitle("Meow Meow") + page.SetSlug("meow-meow") + page.SetLocales([]status_pagev1.Locale{ + status_pagev1.Locale_LOCALE_EN, + status_pagev1.Locale_LOCALE_FR, + status_pagev1.Locale_LOCALE_DE, + status_pagev1.Locale_LOCALE_EN, + status_pagev1.Locale_LOCALE_EN, + }) + + data := &WorkspaceData{StatusPages: []StatusPageData{{Page: page}}} + gen := NewGenerator(data) + content := string(gen.GenerateStatusPagesFile().Bytes()) + + mustContain(t, content, `locales = ["en", "fr", "de"]`) +} + func TestGenerateStatusPagesFile_DefaultsOmitted(t *testing.T) { page := &status_pagev1.StatusPage{} page.SetId("p1") diff --git a/internal/terraform/hcl.go b/internal/terraform/hcl.go index 4b7b32a..75dc47d 100644 --- a/internal/terraform/hcl.go +++ b/internal/terraform/hcl.go @@ -443,8 +443,7 @@ func (g *Generator) GenerateStatusPagesFile() *hclwrite.File { b.SetAttributeValue("password", cty.StringVal("REPLACE_ME")) case "email-domain": b.SetAttributeValue("access_type", cty.StringVal("email-domain")) - domains := append([]string(nil), page.GetAuthEmailDomains()...) - sort.Strings(domains) + domains := dedupe(page.GetAuthEmailDomains()) if len(domains) > 0 { vals := make([]cty.Value, len(domains)) for i, d := range domains { @@ -472,14 +471,13 @@ func (g *Generator) GenerateStatusPagesFile() *hclwrite.File { b.SetAttributeValue("default_locale", cty.StringVal(dl)) } if locs := page.GetLocales(); len(locs) > 0 { - strs := make([]string, 0, len(locs)) + raw := make([]string, 0, len(locs)) for _, l := range locs { if s := localeToString(l); s != "" { - strs = append(strs, s) + raw = append(raw, s) } } - if len(strs) > 0 { - sort.Strings(strs) + if strs := dedupe(raw); len(strs) > 0 { vals := make([]cty.Value, len(strs)) for i, s := range strs { vals[i] = cty.StringVal(s) @@ -639,6 +637,22 @@ func writeRegions(b *hclwrite.Body, regions []monitorv1.Region) { b.SetAttributeValue("regions", cty.ListVal(vals)) } +// dedupe drops repeats while keeping the API's order. Both callers feed provider +// attributes typed as List: sorting or duplicating them makes the applied result +// differ from the plan, which Terraform rejects as an inconsistent result. +func dedupe(values []string) []string { + out := make([]string, 0, len(values)) + seen := make(map[string]bool, len(values)) + for _, v := range values { + if seen[v] { + continue + } + seen[v] = true + out = append(out, v) + } + return out +} + // stringMapValue returns cty.NilVal for an empty map so callers can omit the // attribute entirely rather than emit an empty one. func stringMapValue(m map[string]string) cty.Value { -- 2.51.2