Skip to content

Commit 756db57

Browse files
committed
feat: implement release workflow, cross-compilation, and versioning support
1 parent 3f325c0 commit 756db57

8 files changed

Lines changed: 204 additions & 11 deletions

File tree

.github/workflows/release.yml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
build:
13+
name: Build ${{ matrix.suffix }}
14+
runs-on: ubuntu-latest
15+
strategy:
16+
matrix:
17+
include:
18+
- goos: darwin
19+
goarch: amd64
20+
suffix: darwin-amd64
21+
- goos: darwin
22+
goarch: arm64
23+
suffix: darwin-arm64
24+
- goos: linux
25+
goarch: amd64
26+
suffix: linux-amd64
27+
- goos: linux
28+
goarch: arm64
29+
suffix: linux-arm64
30+
- goos: windows
31+
goarch: amd64
32+
suffix: windows-amd64
33+
ext: .exe
34+
35+
steps:
36+
- name: Checkout
37+
uses: actions/checkout@v4
38+
39+
- name: Set up Go
40+
uses: actions/setup-go@v5
41+
with:
42+
go-version: "1.22"
43+
44+
- name: Build binary
45+
env:
46+
GOOS: ${{ matrix.goos }}
47+
GOARCH: ${{ matrix.goarch }}
48+
CGO_ENABLED: "0"
49+
run: |
50+
VERSION="${GITHUB_REF_NAME}"
51+
BINARY="termf1${{ matrix.ext }}"
52+
go build \
53+
-ldflags="-s -w -X main.version=${VERSION}" \
54+
-o "${BINARY}" .
55+
56+
- name: Package archive
57+
id: archive
58+
run: |
59+
VERSION="${GITHUB_REF_NAME}"
60+
BINARY="termf1${{ matrix.ext }}"
61+
ARCHIVE="termf1-${VERSION}-${{ matrix.suffix }}"
62+
if [ "${{ matrix.goos }}" = "windows" ]; then
63+
zip "${ARCHIVE}.zip" "${BINARY}" README.md .env.example
64+
echo "asset=${ARCHIVE}.zip" >> "$GITHUB_OUTPUT"
65+
else
66+
tar czf "${ARCHIVE}.tar.gz" "${BINARY}" README.md .env.example
67+
echo "asset=${ARCHIVE}.tar.gz" >> "$GITHUB_OUTPUT"
68+
fi
69+
70+
- name: Upload to release
71+
uses: softprops/action-gh-release@v2
72+
with:
73+
files: ${{ steps.archive.outputs.asset }}
74+
generate_release_notes: true

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
termf1
2+
termf1.exe
23
.env
3-
go.sum
4+
dist/

Makefile

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
BINARY = termf1
2-
BUILD = go build -o $(BINARY) .
3-
RUN = ./$(BINARY)
2+
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
3+
LDFLAGS = -ldflags "-s -w -X main.version=$(VERSION)"
4+
BUILD = go build $(LDFLAGS) -o $(BINARY) .
5+
RUN = ./$(BINARY)
46

5-
.PHONY: all build run install clean tidy
7+
DIST_DIR = dist
8+
PLATFORMS = \
9+
darwin/amd64 \
10+
darwin/arm64 \
11+
linux/amd64 \
12+
linux/arm64 \
13+
windows/amd64
14+
15+
.PHONY: all build run install clean tidy dist snapshot
616

717
all: build
818

@@ -22,9 +32,34 @@ install:
2232
tidy:
2333
go mod tidy
2434

25-
## clean: remove the compiled binary
35+
## dist: cross-compile release archives for all platforms into dist/
36+
dist: tidy
37+
@mkdir -p $(DIST_DIR)
38+
@for platform in $(PLATFORMS); do \
39+
GOOS=$$(echo $$platform | cut -d/ -f1); \
40+
GOARCH=$$(echo $$platform | cut -d/ -f2); \
41+
OUT=$(DIST_DIR)/$(BINARY)-$(VERSION)-$$GOOS-$$GOARCH; \
42+
if [ "$$GOOS" = "windows" ]; then EXT=.exe; else EXT=""; fi; \
43+
echo "→ building $$GOOS/$$GOARCH"; \
44+
CGO_ENABLED=0 GOOS=$$GOOS GOARCH=$$GOARCH \
45+
go build $(LDFLAGS) -o $$OUT$$EXT . ; \
46+
if [ "$$GOOS" = "windows" ]; then \
47+
(cd $(DIST_DIR) && zip $(BINARY)-$(VERSION)-$$GOOS-$$GOARCH.zip $(BINARY)-$(VERSION)-$$GOOS-$$GOARCH$$EXT README.md .env.example && rm $(BINARY)-$(VERSION)-$$GOOS-$$GOARCH$$EXT); \
48+
else \
49+
(cd $(DIST_DIR) && tar czf $(BINARY)-$(VERSION)-$$GOOS-$$GOARCH.tar.gz $(BINARY)-$(VERSION)-$$GOOS-$$GOARCH$$EXT README.md .env.example && rm $(BINARY)-$(VERSION)-$$GOOS-$$GOARCH$$EXT); \
50+
fi; \
51+
done
52+
@echo "\nDist archives:"; ls -lh $(DIST_DIR)/
53+
54+
## snapshot: quick local build for current platform (no archive)
55+
snapshot:
56+
$(BUILD)
57+
@echo "Built $(BINARY) $(VERSION)"
58+
59+
## clean: remove the compiled binary and dist/
2660
clean:
2761
rm -f $(BINARY)
62+
rm -rf $(DIST_DIR)
2863

2964
## vet: run go vet
3065
vet:

README.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,27 @@ A full-featured **Formula 1 terminal dashboard** built with Go and [Charm](https
2020

2121
## Install
2222

23+
### Option 1 — Download pre-built binary (recommended)
24+
25+
Go to [Releases](https://github.com/devkeshwani/termf1/releases/latest) and download the archive for your platform:
26+
27+
| Platform | File |
28+
|----------|------|
29+
| macOS (Apple Silicon) | `termf1-vX.Y.Z-darwin-arm64.tar.gz` |
30+
| macOS (Intel) | `termf1-vX.Y.Z-darwin-amd64.tar.gz` |
31+
| Linux x86-64 | `termf1-vX.Y.Z-linux-amd64.tar.gz` |
32+
| Linux ARM64 | `termf1-vX.Y.Z-linux-arm64.tar.gz` |
33+
| Windows x86-64 | `termf1-vX.Y.Z-windows-amd64.zip` |
34+
35+
```bash
36+
# macOS / Linux example
37+
tar xzf termf1-vX.Y.Z-darwin-arm64.tar.gz
38+
export GROQ_API_KEY=your_key_here
39+
./termf1
40+
```
41+
42+
### Option 2 — Build from source
43+
2344
```bash
2445
git clone https://github.com/devkeshwani/termf1
2546
cd termf1
@@ -28,10 +49,17 @@ source .env
2849
make run
2950
```
3051

31-
Or install the binary globally:
52+
Install globally to `$GOPATH/bin`:
53+
54+
```bash
55+
make install # then just: termf1
56+
```
57+
58+
### Option 3 — `go install` (requires Go 1.22+)
3259

3360
```bash
34-
make install
61+
export GROQ_API_KEY=your_key_here
62+
go install github.com/devkeshwani/termf1@latest
3563
termf1
3664
```
3765

go.sum

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
2+
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
3+
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
4+
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
5+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
6+
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
7+
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
8+
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
9+
github.com/charmbracelet/bubbletea v1.1.0 h1:FjAl9eAL3HBCHenhz/ZPjkKdScmaS5SK69JAK2YJK9c=
10+
github.com/charmbracelet/bubbletea v1.1.0/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4=
11+
github.com/charmbracelet/lipgloss v0.13.1 h1:Oik/oqDTMVA01GetT4JdEC033dNzWoQHdWnHnQmXE2A=
12+
github.com/charmbracelet/lipgloss v0.13.1/go.mod h1:zaYVJ2xKSKEnTEEbX6uAHabh2d975RJ+0yfkFpRBz5U=
13+
github.com/charmbracelet/x/ansi v0.3.2 h1:wsEwgAN+C9U06l9dCVMX0/L3x7ptvY1qmjMwyfE6USY=
14+
github.com/charmbracelet/x/ansi v0.3.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
15+
github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0=
16+
github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0=
17+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
18+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
19+
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
20+
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
21+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
22+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
23+
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
24+
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
25+
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
26+
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
27+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
28+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
29+
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
30+
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
31+
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
32+
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
33+
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
34+
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
35+
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
36+
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
37+
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
38+
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
39+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
40+
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
41+
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
42+
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
43+
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=

internal/config/config.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package config
22

3-
import "os"
3+
import (
4+
"os"
5+
"strconv"
6+
)
47

58
// Config holds all runtime configuration loaded from environment variables.
69
type Config struct {
710
GroqAPIKey string
811
GroqModel string
912
RefreshRate int // seconds between live-data refreshes
13+
Version string
1014
}
1115

1216
func Load() *Config {
@@ -26,7 +30,7 @@ func getEnv(key, fallback string) string {
2630

2731
func getEnvInt(key string, fallback int) int {
2832
if v := os.Getenv(key); v != "" {
29-
if i := parseInt(v); i != 0 {
33+
if i, err := strconv.Atoi(v); err == nil && i != 0 {
3034
return i
3135
}
3236
}

internal/ui/app.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ func (a *App) renderHeader() string {
269269
Padding(0, 1).
270270
Render("🏎 termf1")
271271

272+
// Version badge
273+
v := a.cfg.Version
274+
verBadge := styles.DimStyle.Render(" " + v + " ")
275+
272276
// Tabs
273277
tabs := make([]string, len(tabLabels))
274278
for i, label := range tabLabels {
@@ -280,8 +284,8 @@ func (a *App) renderHeader() string {
280284
}
281285
tabRow := lipgloss.JoinHorizontal(lipgloss.Top, tabs...)
282286

283-
// Top row: brand + tabs
284-
topRow := lipgloss.JoinHorizontal(lipgloss.Top, brand, " ", tabRow)
287+
// Top row: brand + version + tabs
288+
topRow := lipgloss.JoinHorizontal(lipgloss.Top, brand, verBadge, " ", tabRow)
285289

286290
// Separator line
287291
sep := styles.Divider.Render(strings.Repeat("─", a.width))

main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ import (
99
"github.com/devkeshwani/termf1/internal/ui"
1010
)
1111

12+
// version is set at build time via -ldflags "-X main.version=v1.2.3"
13+
var version = "dev"
14+
1215
func main() {
1316
cfg := config.Load()
17+
cfg.Version = version
1418

1519
p := tea.NewProgram(
1620
ui.NewApp(cfg),

0 commit comments

Comments
 (0)