1# https://taskfile.dev
2
3version: "3"
4
5vars:
6 VERSION:
7 sh: git describe --long 2>/dev/null || echo ""
8 RACE:
9 sh: test -f race.log && echo "1" || echo ""
10
11env:
12 CGO_ENABLED: 0
13 GOEXPERIMENT: greenteagc
14
15tasks:
16 lint:install:
17 desc: Install golangci-lint
18 cmds:
19 - go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
20 env:
21 GOTOOLCHAIN: go1.25.0
22
23 lint:
24 desc: Run base linters
25 cmds:
26 - task: lint:log
27 - golangci-lint run --path-mode=abs --config=".golangci.yml" --timeout=5m
28 env:
29 GOEXPERIMENT: null
30
31 lint:log:
32 desc: Check that log messages start with capital letters
33 cmds:
34 - ./scripts/check_log_capitalization.sh
35
36 lint:fix:
37 desc: Run base linters and fix issues
38 cmds:
39 - golangci-lint run --path-mode=abs --config=".golangci.yml" --timeout=5m --fix
40 env:
41 GOEXPERIMENT: null
42
43 build:
44 desc: Run build
45 vars:
46 LDFLAGS: '{{if .VERSION}}-ldflags="-X github.com/charmbracelet/crush/internal/version.Version={{.VERSION}}"{{end}}'
47 cmds:
48 - "go build -v {{if .RACE}}-race{{end}} {{.LDFLAGS}} ."
49 sources:
50 - ./**/*.go
51 - go.mod
52 generates:
53 - crush{{exeExt}}
54
55 run:
56 desc: Run build
57 cmds:
58 - task: build
59 - "./crush{{exeExt}} {{.CLI_ARGS}} {{if .RACE}}2>race.log{{end}}"
60
61 run:catwalk:
62 desc: Run build with local Catwalk
63 env:
64 CATWALK_URL: http://localhost:8080
65 cmds:
66 - task: build
67 - ./crush{{exeExt}} {{.CLI_ARGS}}
68
69 run:onboarding:
70 desc: Run build with custom config to test onboarding
71 env:
72 CRUSH_GLOBAL_DATA: tmp/onboarding/data
73 CRUSH_GLOBAL_CONFIG: tmp/onboarding/config
74 cmds:
75 - task: build
76 - rm -rf tmp/onboarding
77 - ./crush{exeExt} {{.CLI_ARGS}}
78
79 test:
80 desc: Run tests
81 cmds:
82 - go test -race -failfast ./... {{.CLI_ARGS}}
83
84 test:record:
85 desc: Run tests and record all VCR cassettes again
86 aliases: [record]
87 cmds:
88 - rm -r internal/agent/testdata
89 - go test -v -count=1 -timeout=1h ./internal/agent
90
91 fmt:
92 desc: Run gofumpt
93 cmds:
94 - gofumpt -w .
95
96 fmt:html:
97 desc: Run prettier on HTML/CSS/JS files
98 cmds:
99 - prettier --write internal/cmd/stats/index.html internal/cmd/stats/index.css internal/cmd/stats/index.js
100
101 modernize:
102 desc: Run modernize
103 cmds:
104 - go run golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest -fix -test ./...
105
106 dev:
107 desc: Run with profiling enabled
108 env:
109 CRUSH_PROFILE: true
110 cmds:
111 - go run .
112
113 install:
114 desc: Install the application
115 vars:
116 LDFLAGS: '{{if .VERSION}}-ldflags="-X github.com/charmbracelet/crush/internal/version.Version={{.VERSION}}"{{end}}'
117 cmds:
118 - task: fetch-tags
119 - go install {{.LDFLAGS}} -v .
120 sources:
121 - ./**/*.go
122 - go.mod
123
124 profile:cpu:
125 desc: 10s CPU profile
126 cmds:
127 - go tool pprof -http :6061 'http://localhost:6060/debug/pprof/profile?seconds=10'
128
129 profile:heap:
130 desc: Heap profile
131 cmds:
132 - go tool pprof -http :6061 'http://localhost:6060/debug/pprof/heap'
133
134 profile:allocs:
135 desc: Allocations profile
136 cmds:
137 - go tool pprof -http :6061 'http://localhost:6060/debug/pprof/allocs'
138
139 schema:
140 desc: Generate JSON schema for configuration
141 cmds:
142 - go run main.go schema > schema.json
143 - echo "Generated schema.json"
144 generates:
145 - schema.json
146
147 hyper:
148 desc: Update Hyper embedded provider.json
149 cmds:
150 - go generate ./internal/agent/hyper/...
151 generates:
152 - ./internal/agent/hyper/provider.json
153
154 release:
155 desc: Create and push a new tag following semver
156 vars:
157 NEXT:
158 sh: svu next --always || go run github.com/caarlos0/svu/v3@latest next --always
159 prompt: "This will release {{.NEXT}}. Continue?"
160 preconditions:
161 - sh: '[ $(git symbolic-ref --short HEAD) = "main" ]'
162 msg: Not on main branch
163 - sh: "[ $(git status --porcelain=2 | wc -l) = 0 ]"
164 msg: "Git is dirty"
165 - sh: 'gh run list --workflow build.yml --commit $(git rev-parse HEAD) --status success --json conclusion -q ".[0].conclusion" | grep -q success'
166 msg: "Test build for this commit failed or not present"
167 - sh: 'gh run list --workflow snapshot.yml --commit $(git rev-parse HEAD) --status success --json conclusion -q ".[0].conclusion" | grep -q success'
168 msg: "Snapshot build for this commit failed or not present"
169 cmds:
170 - task: fetch-tags
171 - git commit --allow-empty -m "{{.NEXT}}"
172 - git tag --annotate --sign -m "{{.NEXT}}" {{.NEXT}} {{.CLI_ARGS}}
173 - echo "Pushing {{.NEXT}}..."
174 - git push origin main --follow-tags
175
176 fetch-tags:
177 cmds:
178 - git tag -d nightly || true
179 - git fetch --tags
180
181 deps:
182 desc: Update Fantasy and Catwalk
183 cmds:
184 - go get charm.land/fantasy
185 - go get charm.land/catwalk
186 - go mod tidy
187
188 release:fork:
189 desc: Create and push a fork release tag (combined workflow)
190 cmds:
191 - task: release:fork:tag
192 - task: release:fork:push
193
194 release:fork:tag:
195 desc: Create a fork release tag locally (without pushing)
196 vars:
197 UPSTREAM_VERSION:
198 sh: git tag -l "v*" | sort -V | tail -1 || echo "v0.0.0"
199 EXISTING_FORK_TAGS:
200 sh: git tag -l "{{.UPSTREAM_VERSION}}-fork.*" | wc -l
201 NEXT_NUM:
202 sh: echo $(({{.EXISTING_FORK_TAGS}} + 1))
203 TAG: "{{.UPSTREAM_VERSION}}-fork.{{.NEXT_NUM}}"
204 prompt: "Create fork release {{.TAG}}?"
205 preconditions:
206 - sh: '[ $(git symbolic-ref --short HEAD) = "dev" ]'
207 msg: Not on dev branch
208 - sh: "[ $(git status --porcelain=2 | wc -l) = 0 ]"
209 msg: "Git is dirty"
210 cmds:
211 - git tag -d nightly || true
212 - git fetch upstream --tags
213 - >-
214 crush run -m kimi-k2.5h "Please update the mentioned version in the top of the README to {{.TAG}}.
215 You only need to read the first 65 lines for the relevant bits.
216 If the content is already correct, you don't need to do anything.
217 Amend the commit at HEAD, updating its subject/message if necessary.
218 Use the skill."
219 - git show --stat HEAD
220 - defer:
221 task: release:fork:cleanup
222 vars:
223 TAG: "{{.TAG}}"
224 - task: release:fork:confirm
225 - git push --force-with-lease
226 - git tag -a {{.TAG}}
227 - echo "Tagged {{.TAG}} locally. Run 'task release:fork:push' to push."
228
229 release:fork:push:
230 desc: Push an existing fork release tag and notify module proxy
231 vars:
232 TAG:
233 sh: git describe --tags --abbrev=0 --match "*-fork.*" 2>/dev/null || echo ""
234 preconditions:
235 - sh: '[ -n "{{.TAG}}" ]'
236 msg: "No fork tag found. Run 'task release:fork:tag' first."
237 - sh: 'git tag -l "{{.TAG}}" | grep -q "{{.TAG}}"'
238 msg: "Tag {{.TAG}} does not exist locally"
239 prompt: "Push {{.TAG}} to remote and notify module proxy?"
240 cmds:
241 - git push soft {{.TAG}}
242 - echo "Released {{.TAG}}"
243 - go list -m git.secluded.site/crush@{{.TAG}} > /dev/null
244 - echo "Module proxy notified"
245
246 release:fork:confirm:
247 desc: Review LLM-generated commit and confirm release
248 internal: true
249 prompt: "Proceed with tagging?"
250
251 release:fork:cleanup:
252 desc: Undo LLM commit if release aborted
253 internal: true
254 cmds:
255 - git tag -d {{.TAG}} 2>/dev/null || true
256 status:
257 - git tag -l {{.TAG}} | grep -q {{.TAG}}