From d631f6b3aaee855c781ffbdc7515d805548e2508 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Fri, 25 Sep 2026 08:07:56 +0000 Subject: [PATCH] fix: render GoReleaser channel templates when GORELEASER_CHANNEL is unset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v0.1.1 release build failed before publishing anything: 'template: failed to apply "{{ eq .Env.GORELEASER_CHANNEL "main" }}": map has no entry for key "GORELEASER_CHANNEL"'. The release workflow never sets GORELEASER_CHANNEL, and GoReleaser rejects a template that reads a missing .Env key. The changelog template already used index .Env; the release and dockers_v2 disable templates did not. - .goreleaser.yaml: read the channel with index .Env in all three templates. - release.yaml: set GORELEASER_CHANNEL=release explicitly. - hack/main_publish_test.go: TestReleaseAndImageChannels renders the release and both dockers_v2 disable templates with missingkey=error for main, empty, release and unset channels, and checks which pipes run. It fails on the old config with the same "map has no entry" error. Signed-off-by: Thomas Kosiewski --- _Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `medium`_ Change-Id: Id1d8ac1debbd39f9690857d9b075c2545c311ec0 --- .github/workflows/release.yaml | 1 + .goreleaser.yaml | 6 ++-- hack/main_publish_test.go | 61 ++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 50a97085..9357d4fb 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -43,6 +43,7 @@ jobs: args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GORELEASER_CHANNEL: release - name: Compute release image tag id: release_image_tag diff --git a/.goreleaser.yaml b/.goreleaser.yaml index b43c9a66..44d7303e 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -31,11 +31,11 @@ archives: - README.md release: - disable: '{{ eq .Env.GORELEASER_CHANNEL "main" }}' + disable: '{{ eq (index .Env "GORELEASER_CHANNEL") "main" }}' dockers_v2: - id: coder-k8s-image-main - disable: '{{ ne .Env.GORELEASER_CHANNEL "main" }}' + disable: '{{ ne (index .Env "GORELEASER_CHANNEL") "main" }}' dockerfile: Dockerfile.goreleaser ids: - coder-k8s @@ -64,7 +64,7 @@ dockers_v2: - "--provenance=false" - id: coder-k8s-image-release - disable: '{{ eq .Env.GORELEASER_CHANNEL "main" }}' + disable: '{{ eq (index .Env "GORELEASER_CHANNEL") "main" }}' dockerfile: Dockerfile.goreleaser ids: - coder-k8s diff --git a/hack/main_publish_test.go b/hack/main_publish_test.go index e03728b2..efc1a005 100644 --- a/hack/main_publish_test.go +++ b/hack/main_publish_test.go @@ -51,6 +51,67 @@ func TestChangelogChannels(t *testing.T) { } } +// The release workflow does not set GORELEASER_CHANNEL, and GoReleaser fails a template that reads a missing +// .Env key ("map has no entry"). Every channel-dependent disable template must render for an unset channel, +// and pick the right pipes per channel. +func TestReleaseAndImageChannels(t *testing.T) { + data, err := os.ReadFile("../.goreleaser.yaml") + if err != nil { + t.Fatal(err) + } + var config struct { + Release struct { + Disable string `json:"disable"` + } `json:"release"` + DockersV2 []struct { + ID string `json:"id"` + Disable string `json:"disable"` + } `json:"dockers_v2"` + } + if err := yaml.Unmarshal(data, &config); err != nil { + t.Fatal(err) + } + // want maps each template to whether it must be disabled on the main channel (and enabled otherwise). + type check struct { + name, text string + disabledOnMain bool + } + checks := []check{{name: "release", text: config.Release.Disable, disabledOnMain: true}} + for _, d := range config.DockersV2 { + switch d.ID { + case "coder-k8s-image-main": + checks = append(checks, check{name: d.ID, text: d.Disable, disabledOnMain: false}) + case "coder-k8s-image-release": + checks = append(checks, check{name: d.ID, text: d.Disable, disabledOnMain: true}) + default: + t.Fatalf("unexpected dockers_v2 id %q; add it to this test", d.ID) + } + } + if len(checks) != 3 { + t.Fatalf("expected release plus two dockers_v2 entries, got %d checks", len(checks)) + } + for _, c := range checks { + tmpl, err := template.New(c.name).Option("missingkey=error").Parse(c.text) + if err != nil { + t.Fatalf("%s: %v", c.name, err) + } + for _, channel := range []string{"main", "", "release", "unset"} { + env := map[string]string{} + if channel != "unset" { + env["GORELEASER_CHANNEL"] = channel + } + var result strings.Builder + if err := tmpl.Execute(&result, struct{ Env map[string]string }{Env: env}); err != nil { + t.Fatalf("%s, channel %q: %v", c.name, channel, err) + } + wantDisabled := (channel == "main") == c.disabledOnMain + if (result.String() == "true") != wantDisabled { + t.Fatalf("%s disabled = %q for channel %q", c.name, result.String(), channel) + } + } + } +} + // `goreleaser release --clean` deletes its output directory. dist/ holds the tracked install bundle, so // GoReleaser writes to .goreleaser-dist instead, and .gitignore must ignore that whole directory to keep the // release worktree clean.