Skip to content

Commit 2ffc854

Browse files
authored
feat: goreleaser configuration for cross-platform distribution (#503)
[STACKED] feat: goreleaser configuration for cross-platform distribution
2 parents 9dd1b48 + e30510f commit 2ffc854

5 files changed

Lines changed: 190 additions & 14 deletions

File tree

go/.goreleaser.yml

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
version: 2
2+
3+
project_name: tfenv
4+
5+
dist: ../dist
6+
7+
builds:
8+
- id: tfenv
9+
dir: .
10+
main: ./cmd/tfenv
11+
binary: tfenv
12+
env:
13+
- CGO_ENABLED=0
14+
goos:
15+
- linux
16+
- darwin
17+
- windows
18+
- freebsd
19+
goarch:
20+
- amd64
21+
- arm64
22+
- arm
23+
goarm:
24+
- "7"
25+
ignore:
26+
- goos: darwin
27+
goarch: arm
28+
- goos: windows
29+
goarch: arm
30+
- goos: freebsd
31+
goarch: arm
32+
- goos: freebsd
33+
goarch: arm64
34+
ldflags:
35+
- -s -w
36+
- -X main.version={{.Version}}
37+
- -X main.commit={{.Commit}}
38+
- -X main.date={{.Date}}
39+
40+
# Second build: the terraform shim entry point.
41+
# Same source as tfenv — multi-call dispatch uses the invoked basename
42+
# to decide whether to act as the CLI or the terraform shim.
43+
- id: terraform-shim
44+
dir: .
45+
main: ./cmd/tfenv
46+
binary: terraform
47+
env:
48+
- CGO_ENABLED=0
49+
goos:
50+
- linux
51+
- darwin
52+
- windows
53+
- freebsd
54+
goarch:
55+
- amd64
56+
- arm64
57+
- arm
58+
goarm:
59+
- "7"
60+
ignore:
61+
- goos: darwin
62+
goarch: arm
63+
- goos: windows
64+
goarch: arm
65+
- goos: freebsd
66+
goarch: arm
67+
- goos: freebsd
68+
goarch: arm64
69+
ldflags:
70+
- -s -w
71+
- -X main.version={{.Version}}
72+
- -X main.commit={{.Commit}}
73+
- -X main.date={{.Date}}
74+
75+
archives:
76+
- id: default
77+
builds:
78+
- tfenv
79+
- terraform-shim
80+
format_overrides:
81+
- goos: windows
82+
format: zip
83+
files:
84+
- src: ../LICENSE
85+
dst: .
86+
- src: ../README.md
87+
dst: .
88+
89+
checksum:
90+
name_template: 'checksums.txt'
91+
algorithm: sha256
92+
93+
nfpms:
94+
- id: packages
95+
package_name: tfenv-go
96+
vendor: tfutils
97+
homepage: https://github.com/tfutils/tfenv
98+
maintainer: Mike Peachey <mike.peachey@bjss.com>
99+
description: Terraform version manager (Go edition)
100+
license: MIT
101+
formats:
102+
- deb
103+
- rpm
104+
bindir: /usr/local/bin
105+
contents:
106+
- src: ../LICENSE
107+
dst: /usr/share/doc/tfenv-go/LICENSE
108+
- src: ../README.md
109+
dst: /usr/share/doc/tfenv-go/README.md
110+
111+
brews:
112+
- name: tfenv-go
113+
repository:
114+
owner: tfutils
115+
name: homebrew-tap
116+
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
117+
directory: Formula
118+
homepage: https://github.com/tfutils/tfenv
119+
description: Terraform version manager (Go edition)
120+
license: MIT
121+
install: |
122+
bin.install "tfenv"
123+
bin.install "terraform"
124+
test: |
125+
system "#{bin}/tfenv", "--version"
126+
skip_upload: auto
127+
128+
release:
129+
draft: true
130+
name_template: "tfenv {{.Version}}"
131+
132+
changelog:
133+
use: github-native

go/cmd/tfenv/main.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@ import (
2525
_ "github.com/tfutils/tfenv/go/internal/use"
2626
)
2727

28-
// version is set at build time via -ldflags "-X main.version=...".
29-
// It defaults to "dev" for local builds.
30-
var version = "dev"
28+
// Build-time variables injected via -ldflags.
29+
// Defaults are used for local `go build` invocations.
30+
var (
31+
version = "dev"
32+
commit = "none"
33+
date = "unknown"
34+
)
3135

3236
func main() {
3337
basename := filepath.Base(os.Args[0])
@@ -41,6 +45,11 @@ func main() {
4145
case "terraform":
4246
os.Exit(shim.Run(os.Args[1:]))
4347
default:
44-
os.Exit(cli.Run(version, os.Args[1:]))
48+
info := cli.BuildInfo{
49+
Version: version,
50+
Commit: commit,
51+
Date: date,
52+
}
53+
os.Exit(cli.Run(info, os.Args[1:]))
4554
}
4655
}

go/internal/cli/cli.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,32 @@ func Register(name string, description string, handler Handler) {
3232
}
3333
}
3434

35+
// BuildInfo holds version metadata injected at build time.
36+
type BuildInfo struct {
37+
Version string
38+
Commit string
39+
Date string
40+
}
41+
3542
// Run dispatches to the appropriate subcommand based on args.
3643
// It returns an exit code suitable for os.Exit.
37-
func Run(version string, args []string) int {
44+
func Run(info BuildInfo, args []string) int {
3845
if len(args) == 0 {
39-
printUsage(version)
46+
printUsage(info.Version)
4047
return 0
4148
}
4249

4350
subcmd := args[0]
4451

4552
// Handle --version and version as special cases.
4653
if subcmd == "--version" || subcmd == "version" {
47-
fmt.Fprintf(os.Stdout, "tfenv %s\n", version)
54+
fmt.Fprintf(os.Stdout, "tfenv %s (commit: %s, built: %s)\n", info.Version, info.Commit, info.Date)
4855
return 0
4956
}
5057

5158
// Handle help as a special case.
5259
if subcmd == "help" || subcmd == "--help" || subcmd == "-h" {
53-
printUsage(version)
60+
printUsage(info.Version)
5461
return 0
5562
}
5663

go/internal/cli/cli_test.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,38 @@ import (
44
"testing"
55
)
66

7+
var testInfo = BuildInfo{Version: "1.2.3", Commit: "abc123", Date: "2025-01-01"}
8+
79
func TestRunVersion(t *testing.T) {
8-
exit := Run("1.2.3", []string{"--version"})
10+
exit := Run(testInfo, []string{"--version"})
911
if exit != 0 {
1012
t.Errorf("expected exit code 0, got %d", exit)
1113
}
1214
}
1315

1416
func TestRunVersionSubcommand(t *testing.T) {
15-
exit := Run("1.2.3", []string{"version"})
17+
exit := Run(testInfo, []string{"version"})
1618
if exit != 0 {
1719
t.Errorf("expected exit code 0, got %d", exit)
1820
}
1921
}
2022

2123
func TestRunHelp(t *testing.T) {
22-
exit := Run("1.2.3", []string{"help"})
24+
exit := Run(testInfo, []string{"help"})
2325
if exit != 0 {
2426
t.Errorf("expected exit code 0, got %d", exit)
2527
}
2628
}
2729

2830
func TestRunNoArgs(t *testing.T) {
29-
exit := Run("1.2.3", []string{})
31+
exit := Run(testInfo, []string{})
3032
if exit != 0 {
3133
t.Errorf("expected exit code 0, got %d", exit)
3234
}
3335
}
3436

3537
func TestRunUnknownCommand(t *testing.T) {
36-
exit := Run("1.2.3", []string{"unknown-command"})
38+
exit := Run(testInfo, []string{"unknown-command"})
3739
if exit != 1 {
3840
t.Errorf("expected exit code 1, got %d", exit)
3941
}
@@ -47,7 +49,7 @@ func TestRegisterAndRun(t *testing.T) {
4749
delete(registry, "test-cmd")
4850
}()
4951

50-
exit := Run("1.2.3", []string{"test-cmd"})
52+
exit := Run(testInfo, []string{"test-cmd"})
5153
if exit != 0 {
5254
t.Errorf("expected exit code 0, got %d", exit)
5355
}

go/scripts/post-build.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/bin/sh
2+
# Create the terraform entry point alongside tfenv after a manual local build.
3+
#
4+
# goreleaser uses a dual-build approach (see .goreleaser.yml) so this script
5+
# is only needed for manual `go build` workflows outside goreleaser.
6+
#
7+
# The terraform binary is a copy of tfenv — multi-call dispatch uses the
8+
# invoked basename to decide whether to act as tfenv CLI or terraform shim.
9+
10+
set -eu
11+
12+
src="${1:-tfenv}"
13+
dir="$(dirname "${src}")"
14+
15+
if [ -f "${src}" ]; then
16+
cp "${src}" "${dir}/terraform"
17+
echo "Created ${dir}/terraform"
18+
elif [ -f "${src}.exe" ]; then
19+
cp "${src}.exe" "${dir}/terraform.exe"
20+
echo "Created ${dir}/terraform.exe"
21+
else
22+
echo "Error: ${src} not found. Build tfenv first:" >&2
23+
echo " go build -o tfenv ./cmd/tfenv" >&2
24+
exit 1
25+
fi

0 commit comments

Comments
 (0)