Skip to content

Inject version information into binary during build #31

@sgaunet

Description

@sgaunet

Problem

Version is hardcoded as "development" with no way to inject build version during release.

Current Implementation

// cmd/logwrap/main.go:18
const version = "development"

Output:

$ logwrap --version
logwrap version development

Issues:

  • Can't identify which release is installed
  • No build metadata (commit hash, build date)
  • Hard to troubleshoot issues ("which version are you running?")

Proposed Solution

Use -ldflags during build to inject version information.

Implementation

// cmd/logwrap/main.go

var (
    // Injected at build time via -ldflags
    version   = "development"
    commit    = "unknown"
    buildDate = "unknown"
)

func printVersion() {
    fmt.Printf("logwrap version %s\n", version)
    fmt.Printf("  commit: %s\n", commit)
    fmt.Printf("  built:  %s\n", buildDate)
}

Build Command

# Manual build
go build -ldflags "\
  -X main.version=v1.0.0 \
  -X main.commit=$(git rev-parse HEAD) \
  -X main.buildDate=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  ./cmd/logwrap

# Output:
$ logwrap --version
logwrap version v1.0.0
  commit: a1b2c3d4e5f6...
  built:  2024-01-15T14:30:00Z

GoReleaser Integration

Update .goreleaser.yml:

builds:
  - id: logwrap
    binary: logwrap
    main: ./cmd/logwrap
    ldflags:
      - -s -w
      - -X main.version={{.Version}}
      - -X main.commit={{.Commit}}
      - -X main.buildDate={{.Date}}
    env:
      - CGO_ENABLED=0
    goos:
      - linux
      - darwin
      - windows
    goarch:
      - amd64
      - arm64

Taskfile Integration

Update Taskfile.yml:

vars:
  VERSION:
    sh: git describe --tags --always --dirty
  COMMIT:
    sh: git rev-parse HEAD
  BUILD_DATE:
    sh: date -u +%Y-%m-%dT%H:%M:%SZ

tasks:
  build:
    desc: Build the binary
    cmds:
      - go build -ldflags "
        -X main.version={{.VERSION}}
        -X main.commit={{.COMMIT}}
        -X main.buildDate={{.BUILD_DATE}}"
        -o bin/logwrap ./cmd/logwrap

GitHub Actions Integration

# .github/workflows/release.yml
- name: Build with version info
  run: |
    VERSION=${{ github.ref_name }}
    COMMIT=${{ github.sha }}
    BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
    
    go build -ldflags "
      -X main.version=${VERSION}
      -X main.commit=${COMMIT}
      -X main.buildDate=${BUILD_DATE}" \
      -o logwrap ./cmd/logwrap

Version Output Formats

Simple (current)

$ logwrap --version
logwrap version v1.0.0

Detailed

$ logwrap --version
logwrap version v1.0.0
  commit: a1b2c3d4e5f6...
  built:  2024-01-15T14:30:00Z
  go:     go1.22.1
  os/arch: linux/amd64

JSON (for scripts)

$ logwrap --version --json
{
  "version": "v1.0.0",
  "commit": "a1b2c3d4e5f6...",
  "buildDate": "2024-01-15T14:30:00Z",
  "goVersion": "go1.22.1",
  "os": "linux",
  "arch": "amd64"
}

Implementation Checklist

Required:

  • Change version from const to var
  • Add commit and buildDate variables
  • Update .goreleaser.yml with ldflags
  • Update Taskfile.yml with version injection
  • Test with task build

Nice to Have:

  • Add detailed version output
  • Add JSON format for --version
  • Add runtime info (Go version, OS, arch)
  • Update GitHub Actions workflows

Testing

# Build with version
task build

# Check version
./bin/logwrap --version
# Should show actual version, not "development"

# Verify ldflags worked
go version -m ./bin/logwrap | grep main
# Should show injected values

Benefits

User Support:

  • Easy to identify installed version
  • Better bug reports ("I'm using v1.2.3")
  • Easier troubleshooting

Release Management:

  • Track which commits are in production
  • Verify binary freshness
  • Audit trail for deployments

Development:

  • Distinguish dev builds from releases
  • CI/CD integration
  • Automated versioning

Alternative: Embed Version File

Instead of ldflags, embed version file:

//go:embed VERSION
var version string

func init() {
    version = strings.TrimSpace(version)
}

Pros: Simpler, no build flags needed
Cons: Requires VERSION file, less flexible

Recommendation: Use ldflags (industry standard, works with GoReleaser)

Related Issues

  • None directly, but good DevOps practice

References

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions