-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTaskfile.yml
More file actions
346 lines (306 loc) · 14.2 KB
/
Taskfile.yml
File metadata and controls
346 lines (306 loc) · 14.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# Copyright (c) 2019-2026 Truestamp, Inc.
# SPDX-License-Identifier: MIT
#
# https://taskfile.dev
version: "3"
vars:
BINARY: truestamp
MODULE: github.com/truestamp/truestamp-cli
tasks:
default:
desc: List all available tasks
cmds:
- task --list-all
# =============================================================================
# Build
# =============================================================================
build:
desc: Build the CLI binary for the current platform
vars:
VERSION:
# Derive from the nearest git tag. `--dirty` flags uncommitted
# changes, `--always` falls back to a short SHA if no tag exists.
# Leading `v` is stripped so local builds match GoReleaser's
# {{.Version}} format.
sh: git describe --tags --always --dirty 2>/dev/null | sed 's/^v//' | tr -d '[:space:]' || echo dev
GIT_COMMIT:
sh: git rev-parse --short HEAD 2>/dev/null || echo unknown
BUILD_DATE:
sh: date -u +%Y-%m-%dT%H:%M:%SZ
BUILD_YEAR:
sh: date -u +%Y
LDFLAGS: -s -w -X {{.MODULE}}/internal/version.Version={{.VERSION}} -X {{.MODULE}}/internal/version.GitCommit={{.GIT_COMMIT}} -X {{.MODULE}}/internal/version.BuildDate={{.BUILD_DATE}} -X {{.MODULE}}/internal/version.BuildYear={{.BUILD_YEAR}}
cmds:
- mkdir -p build
- go build -ldflags "{{.LDFLAGS}}" -o build/{{.BINARY}} ./cmd/truestamp
sources:
- "**/*.go"
# go:embed pulls non-Go files into the binary at compile time
# (e.g. internal/config/defaults/config.toml). Track them here
# so editing one invalidates the cache and forces a rebuild.
- "internal/config/defaults/*.toml"
- go.mod
- go.sum
generates:
- "build/{{.BINARY}}"
build-all:
desc: Cross-compile for all supported platforms
deps:
- build
vars:
VERSION:
sh: git describe --tags --always --dirty 2>/dev/null | sed 's/^v//' | tr -d '[:space:]' || echo dev
GIT_COMMIT:
sh: git rev-parse --short HEAD 2>/dev/null || echo unknown
BUILD_DATE:
sh: date -u +%Y-%m-%dT%H:%M:%SZ
BUILD_YEAR:
sh: date -u +%Y
LDFLAGS: -s -w -X {{.MODULE}}/internal/version.Version={{.VERSION}} -X {{.MODULE}}/internal/version.GitCommit={{.GIT_COMMIT}} -X {{.MODULE}}/internal/version.BuildDate={{.BUILD_DATE}} -X {{.MODULE}}/internal/version.BuildYear={{.BUILD_YEAR}}
cmds:
- mkdir -p build
- GOOS=darwin GOARCH=arm64 go build -ldflags "{{.LDFLAGS}}" -o build/{{.BINARY}}-{{.VERSION}}-darwin-arm64 ./cmd/truestamp
- GOOS=darwin GOARCH=amd64 go build -ldflags "{{.LDFLAGS}}" -o build/{{.BINARY}}-{{.VERSION}}-darwin-amd64 ./cmd/truestamp
- GOOS=linux GOARCH=amd64 go build -ldflags "{{.LDFLAGS}}" -o build/{{.BINARY}}-{{.VERSION}}-linux-amd64 ./cmd/truestamp
- GOOS=linux GOARCH=arm64 go build -ldflags "{{.LDFLAGS}}" -o build/{{.BINARY}}-{{.VERSION}}-linux-arm64 ./cmd/truestamp
- GOOS=windows GOARCH=amd64 go build -ldflags "{{.LDFLAGS}}" -o build/{{.BINARY}}-{{.VERSION}}-windows-amd64.exe ./cmd/truestamp
- GOOS=windows GOARCH=arm64 go build -ldflags "{{.LDFLAGS}}" -o build/{{.BINARY}}-{{.VERSION}}-windows-arm64.exe ./cmd/truestamp
- echo "Built v{{.VERSION}} binaries in build/"
# =============================================================================
# Run
# =============================================================================
run:
desc: "Run verify without building (usage: task run -- proof.json [--skip-external] [--skip-signatures] [--json])"
cmds:
- go run ./cmd/truestamp verify {{.CLI_ARGS}}
upgrade-test-local:
desc: "Build with a fake older version and exercise 'upgrade --check' against live GitHub releases"
cmds:
- |
OLD_VERSION=0.0.1
LDFLAGS="-X {{.MODULE}}/internal/version.Version=${OLD_VERSION}"
mkdir -p build
go build -ldflags "$LDFLAGS" -o build/truestamp-upgrade-test ./cmd/truestamp
./build/truestamp-upgrade-test upgrade --check || EXIT=$?
echo "upgrade --check exit code: ${EXIT:-0}"
if [ "${EXIT:-0}" != "1" ] && [ "${EXIT:-0}" != "0" ] && [ "${EXIT:-0}" != "3" ]; then
echo "unexpected exit code"
exit 1
fi
# =============================================================================
# Docs preview (mirrors get.truestamp.com)
# =============================================================================
docs-serve:
desc: "Serve docs/ locally with caddy (mirrors get.truestamp.com). Browse http://localhost:8080, test `curl http://localhost:8080/install.sh | sh` against the staged installer."
cmds:
- mise exec -- caddy file-server --listen :8080 --root docs
# =============================================================================
# Test & Quality
# =============================================================================
test:
desc: Run all Go tests
cmds:
- go test ./...
test-verbose:
desc: Run all Go tests with verbose output
cmds:
- go test -v ./...
test-chaos:
desc: Run wschannel chaos tests under -race (no live server needed)
cmds:
- go test -tags=chaos -count=1 -race -timeout=120s ./internal/wschannel/...
fuzz-codec:
desc: Fuzz wschannel JSON codec and redactor (30s per target)
cmds:
- go test -run=^$ -fuzz=FuzzFrameUnmarshal -fuzztime=30s ./internal/wschannel/
- go test -run=^$ -fuzz=FuzzParseReply -fuzztime=30s ./internal/wschannel/
- go test -run=^$ -fuzz=FuzzRedactSecrets -fuzztime=30s ./internal/wschannel/
test-coverage:
desc: Run tests with coverage summary
cmds:
- go test -cover ./...
test-coverage-report:
desc: Generate HTML coverage report and open in browser
cmds:
- go test -coverprofile=coverage.out ./...
- go tool cover -html=coverage.out -o coverage.html
- open coverage.html
- echo "Coverage report written to coverage.html"
test-coverage-full:
desc: "Accurate coverage including CLI integration tests' subprocess runs (Go 1.20+ instrumented binaries)"
cmds:
# Clean start. Two covdata sub-dirs: `bin` for the subprocess
# binary's runtime data, `test` for go test's per-test-binary data.
- rm -rf coverage && mkdir -p coverage/bin coverage/test coverage/merged
# TRUESTAMP_COVERDIR is read by verify_test.go's TestMain to
# (a) build the subprocess binary with -cover and (b) point its
# GOCOVERDIR at coverage/bin. We don't use GOCOVERDIR directly
# here because `go test -cover` overwrites GOCOVERDIR in the test
# process's environment with its own temp dir.
- TRUESTAMP_COVERDIR=$PWD/coverage/bin go test -cover -coverpkg=./... ./... -args -test.gocoverdir=$PWD/coverage/test >/dev/null
# Merge the two covdata directories into a single textfmt profile.
- go tool covdata merge -i=coverage/bin,coverage/test -o coverage/merged
- go tool covdata textfmt -i=coverage/merged -o coverage.out
- echo "--- per-function coverage (tail) ---"
- go tool cover -func=coverage.out | tail -20
- echo "--- per-package coverage ---"
- |
go tool cover -func=coverage.out | awk '
/^total:/ { next }
{
split($1, a, ":")
pkg = a[1]
sub(/\/[^\/]+$/, "", pkg)
pct = $NF
gsub("%", "", pct)
sum[pkg] += pct
n[pkg]++
}
END {
for (p in sum) printf "%-60s %6.2f%%\n", p, sum[p]/n[p]
}
' | sort
- echo "--- total ---"
- 'go tool cover -func=coverage.out | grep ^total:'
- go tool cover -html=coverage.out -o coverage.html
- echo "HTML report written to coverage.html"
vet:
desc: Run go vet static analysis
cmds:
- go vet ./...
lint:
desc: "All quality checks: vet + fmt-check + staticcheck + gosec. staticcheck and gosec are pinned in .tool-versions and installed by `mise install`."
deps: [vet, fmt-check]
cmds:
- staticcheck ./...
# gosec exclusions, justified:
# G104: unhandled lipgloss.Println / fmt.Fprintf to stdout — errors there
# are not actionable in a CLI.
# G115: wire-format integer conversions where source values are bounded
# by protocol constants (proof version byte, epoch-root counts).
# G304: reading user-specified file paths IS the point of a CLI.
# G301/G302/G306: 0644/0755 are the correct Unix defaults for config
# files and user-installed binaries.
# G401/G501/G505: MD5 and SHA-1 are supported as legacy hash_type
# values (mirrors the Truestamp backend registry); runtime
# emits a stderr warning when either is used.
# G204: subprocess invocations (xattr, cosign) use hard-coded binary
# names resolved via PATH LookPath — no user-controlled command.
# G704: URL-from-variable used only for known upstream services
# (Horizon, Blockstream, keyring); not user-controlled.
- gosec -quiet -exclude-dir=testdata -exclude=G104,G115,G301,G302,G306,G304,G401,G501,G505,G204,G704 ./...
vuln-check:
desc: "Scan go.mod deps for known vulnerabilities. govulncheck is pinned in .tool-versions and installed by `mise install`."
cmds:
- govulncheck ./...
test-race:
desc: "Run all tests under the race detector (~2-20x slower; catches concurrent unsynchronized memory access)"
cmds:
- go test -race ./...
bench:
desc: "Run all benchmarks (BenchmarkXxx) with -benchmem for alloc accounting"
cmds:
- go test -run=^$ -bench=. -benchmem ./...
bench-compare:
desc: "Capture a baseline benchmark profile. Usage: task bench-compare -- baseline.txt (then later: go test -bench=. ... > new.txt && benchstat baseline.txt new.txt)"
cmds:
- |
out="${1:-{{.CLI_ARGS | default "bench.out"}}}"
go test -run=^$ -bench=. -benchmem -count=5 ./... | tee "$out"
echo "Baseline written to $out. Compare later with: benchstat $out new.out"
fuzz:
desc: "Smoke-run every FuzzXxx with its seed corpus (no mutation, ~seconds)"
cmds:
- go test -run=^Fuzz ./...
fuzz-deep:
desc: "Actively fuzz every FuzzXxx for DURATION (default 15s) each. Use for manual hardening; very chatty. Failing targets are retried once to absorb the Go fuzz engine's known shutdown-boundary flake where an in-flight input reports 'context deadline exceeded' when fuzztime expires. A real bug writes a reproducer under the owning package's testdata/fuzz/FuzzXxx/ directory and reproduces on replay instantly. IMPORTANT: any files written under */testdata/fuzz/* must be committed — they are the permanent corpus that `task test` replays as regression tests. Run `find . -path '*/testdata/fuzz/*' -type f` after this task to list new reproducers, and `git add` (or `jj file track`) them before you commit."
vars:
DURATION: '{{.DURATION | default "15s"}}'
cmds:
- |
set -e
failed=0
for pkg in $(go list ./...); do
tests=$(go test -list='^Fuzz' "$pkg" 2>/dev/null | grep '^Fuzz' || true)
for t in $tests; do
echo "==> $pkg :: $t ({{.DURATION}})"
if ! go test -run=^$ -fuzz="^${t}$" -fuzztime={{.DURATION}} "$pkg"; then
echo "==> $pkg :: $t — first attempt failed; retrying once to rule out fuzz-engine shutdown flake"
if ! go test -run=^$ -fuzz="^${t}$" -fuzztime={{.DURATION}} "$pkg"; then
failed=$((failed+1))
fi
fi
done
done
if [ $failed -gt 0 ]; then
echo "FAILED: $failed fuzz target(s) discovered issues; see testdata/fuzz/ for reproducers"
exit 1
fi
fuzz-list:
desc: "List every FuzzXxx target across the repo"
cmds:
- |
for pkg in $(go list ./...); do
tests=$(go test -list='^Fuzz' "$pkg" 2>/dev/null | grep '^Fuzz' || true)
if [ -n "$tests" ]; then
echo "$pkg"
echo "$tests" | sed 's/^/ /'
fi
done
fmt:
desc: Format all Go source files
cmds:
- gofmt -w .
fmt-check:
desc: Check formatting without modifying files
cmds:
- test -z "$(gofmt -l .)" || (echo "Files need formatting:" && gofmt -l . && exit 1)
precommit:
desc: "Fast pre-commit checks (fmt + lint + test + build). Target <10s on a hot cache. Note: 'task test' already replays every FuzzXxx seed corpus as part of the regular test suite, so no separate fuzz step is needed here."
cmds:
- task: fmt
- task: lint
- task: test
- task: build
precommit-full:
desc: "Strict pre-release bundle: precommit + race detector + active mutation fuzzing + vuln scan + all-platform build. Takes ~3-5 min."
cmds:
- task: fmt
- task: lint
- task: test-race
- task: fuzz
- task: vuln-check
- task: build-all
# =============================================================================
# Release (GoReleaser)
# =============================================================================
release-check:
desc: Validate .goreleaser.yaml syntax and configuration
cmds:
- mise exec -- goreleaser check
release-snapshot:
desc: "Local snapshot release into dist/ — skips cosign signing (keyless requires the CI OIDC token) and GitHub publishing, so this runs fully offline and is safe for pre-release inspection."
cmds:
- mise exec -- goreleaser release --snapshot --clean --skip=sign,publish
- 'echo "✅ Snapshot artifacts in dist/"'
# =============================================================================
# Dependencies
# =============================================================================
deps:
desc: Download and tidy module dependencies
cmds:
- go mod tidy
- go mod download
deps-update:
desc: Update all dependencies to latest versions
cmds:
- go get -u ./...
- go mod tidy
# =============================================================================
# Cleanup
# =============================================================================
clean:
desc: Remove build directory and artifacts
cmds:
- rm -rf build/
- echo "Cleaned build artifacts"