Docker images reported commit unknown because the build ran git inside the container while .dockerignore excludes .git, and VERSION was never overridden. script/docker and script/cibuild now compute version, commit and date on the host and pass them as build args; the Dockerfile runs no git and falls back to dev and unknown, never empty, on a bare docker build. Profiling a failing command gave a truncated or missing profile: Entry and each command goroutine called os.Exit(1), skipping the deferred profile writers in main. Entry now returns a status that main exits with after its defers run, and command goroutines report failure through one RunOperation helper, which also restores PID-lock release and graceful shutdown on failure. model: claude-opus-4-8 (implementation, review); claude-fable-5-1 (merge) Co-authored-by: clawbot <clawbot@noreply.example.org>
61 lines
1.6 KiB
Go
61 lines
1.6 KiB
Go
// Package main is the vaultik command-line entry point.
|
|
package main
|
|
|
|
import (
|
|
"os"
|
|
"runtime"
|
|
"runtime/pprof"
|
|
|
|
"sneak.berlin/go/vaultik/internal/cli"
|
|
)
|
|
|
|
func main() {
|
|
os.Exit(run())
|
|
}
|
|
|
|
// run sets up optional profiling, runs the CLI, and returns the process
|
|
// exit code. os.Exit lives in main so it fires only after run's deferred
|
|
// profile writers have flushed. cli.Entry returns a status code rather
|
|
// than calling os.Exit itself: an os.Exit from inside it would skip
|
|
// these defers and truncate the profile of a failing command -- exactly
|
|
// the command one most often wants to profile.
|
|
func run() int {
|
|
// CPU profiling: set VAULTIK_CPUPROFILE=/path/to/cpu.prof
|
|
if cpuProfile := os.Getenv("VAULTIK_CPUPROFILE"); cpuProfile != "" {
|
|
f, err := os.Create(cpuProfile) //nolint:gosec // G304: operator-set path
|
|
if err != nil {
|
|
panic("could not create CPU profile: " + err.Error())
|
|
}
|
|
|
|
defer func() { _ = f.Close() }()
|
|
|
|
err = pprof.StartCPUProfile(f)
|
|
if err != nil {
|
|
panic("could not start CPU profile: " + err.Error())
|
|
}
|
|
|
|
defer pprof.StopCPUProfile()
|
|
}
|
|
|
|
// Memory profiling: set VAULTIK_MEMPROFILE=/path/to/mem.prof
|
|
if memProfile := os.Getenv("VAULTIK_MEMPROFILE"); memProfile != "" {
|
|
defer func() {
|
|
f, err := os.Create(memProfile) //nolint:gosec // G304: operator-set path
|
|
if err != nil {
|
|
panic("could not create memory profile: " + err.Error())
|
|
}
|
|
|
|
defer func() { _ = f.Close() }()
|
|
|
|
runtime.GC() // get up-to-date statistics
|
|
|
|
err = pprof.WriteHeapProfile(f)
|
|
if err != nil {
|
|
panic("could not write memory profile: " + err.Error())
|
|
}
|
|
}()
|
|
}
|
|
|
|
return cli.Entry()
|
|
}
|