- Bump go1.26 to fix CVE-2025-68121 - Bump github action go version - Update: adjust code following bump of go.26 (update of vet, ...) - Update: adjust test following changes
Version Package
Production-ready version and license management for Go applications with support for 11 open-source licenses, Go version constraints, and automatic package path extraction.
Table of Contents
- Overview
- Key Features
- Installation
- Architecture
- Quick Start
- Performance
- Use Cases
- API Reference
- Best Practices
- Testing
- Contributing
- Future Enhancements
- License
Overview
The version package provides comprehensive version and license management for Go applications. It handles build metadata, release versions, license information, and Go version constraints with thread-safe operations and minimal memory overhead.
Design Philosophy
- Immutable After Creation: Version instances are read-only after construction
- Thread-Safe: All methods are safe for concurrent use
- Zero Dependencies: Only depends on
github.com/nabbar/golib/errorsandgithub.com/hashicorp/go-version - Reflection-Based: Automatic package path extraction using Go reflection
- License Compliant: Built-in support for major open-source licenses
Key Features
- Version Management: Build hash, release tag, date, author, and description
- License Support: 11 open-source licenses with full legal text and boilerplate
- MIT, Apache 2.0, GPL v3, AGPL v3, LGPL v3
- Mozilla PL v2.0, Unlicense, CC0, CC BY, CC BY-SA
- SIL Open Font License v1.1
- Go Version Constraints: Validate runtime Go version with flexible operators
- Package Path Extraction: Automatic detection via reflection
- Formatted Output: Headers, info strings, and license text generation
- Thread-Safe: Immutable design ensures safe concurrent access
- High Performance: ~94ns for version creation, 0 allocations for license retrieval
Installation
go get github.com/nabbar/golib/version
Requirements:
- Go ≥ 1.18
- CGO enabled for race detection (optional)
Architecture
Package Structure
version/
├── version.go # Core Version interface and NewVersion()
├── license.go # License type and constants
├── license_*.go # Individual license implementations
├── error.go # Error codes and messages
└── doc.go # Package documentation
Component Overview
┌─────────────────────────────────────────────────────────┐
│ Version Interface │
│ GetRelease(), GetBuild(), GetDate(), GetAuthor() │
│ GetLicense*(), CheckGo(), PrintInfo() │
└──────────────┬──────────────┬──────────────┬────────────┘
│ │ │
┌────────▼─────┐ ┌────▼─────┐ ┌────▼────────┐
│ Metadata │ │ Licenses │ │ Validation │
│ │ │ │ │ │
│ Build info │ │ 11 types │ │ Go version │
│ Package path │ │ Legal │ │ constraints │
└──────────────┘ └──────────┘ └─────────────┘
Data Flow
NewVersion() → Reflection → Package Path Extraction
│ │
├──────────────┴──→ Parse Date (RFC3339)
│
└──→ Create immutable Version instance
│
├──→ GetHeader() / GetInfo()
├──→ GetLicense*()
└──→ CheckGo()
Quick Start
Basic Usage
package main
import (
"fmt"
"github.com/nabbar/golib/version"
)
// Define a struct for reflection-based package path extraction
type MyApp struct{}
func main() {
// Create version instance
v := version.NewVersion(
version.License_MIT, // License type
"MyApp", // Package name
"My Application Description", // Description
"2024-01-15T10:30:00Z", // Build date (RFC3339)
"abc123def", // Build hash (git commit)
"v1.2.3", // Release version
"John Doe", // Author
"MYAPP", // Prefix for env vars
MyApp{}, // Empty struct for reflection
0, // Number of parent packages
)
// Display version information
fmt.Println(v.GetHeader())
// Output: MyApp (Release: v1.2.3, Build: abc123def, Date: Mon, 15 Jan 2024 10:30:00 UTC)
// Check Go version constraint
if err := v.CheckGo("1.18", ">="); err != nil {
panic(err)
}
// Get license information
fmt.Println(v.GetLicenseName()) // "MIT License"
fmt.Println(v.GetLicenseBoiler()) // Boilerplate for file headers
}
Build Integration
Create a release package to inject build-time variables:
release/release.go:
package release
type EmptyStruct struct{}
var (
// Injected at build time with -ldflags
Release = "v0.0.0"
Build = "dev"
Date = "2024-01-01T00:00:00Z"
Package = "myapp"
Description = "My Application"
Author = "Author Name"
Prefix = "MYAPP"
)
release/version.go:
package release
import "github.com/nabbar/golib/version"
var vers version.Version
func init() {
vers = version.NewVersion(
version.License_MIT,
Package,
Description,
Date,
Build,
Release,
Author,
Prefix,
EmptyStruct{},
1, // Go up one directory from release/ to app root
)
}
func GetVersion() version.Version {
return vers
}
Build command:
go build -ldflags "\
-X release.Release=$(git describe --tags HEAD) \
-X release.Build=$(git rev-parse --short HEAD) \
-X release.Date=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
-X release.Package=$(basename $(pwd)) \
-X release.Description='My Application' \
-X release.Author='John Doe'"
Performance
Benchmark Results
Operation Time/op Memory/op Allocs/op
─────────────────────────────────────────────────────────────
NewVersion 94.00 ns 144 B 1
GetHeader 383.3 ns 272 B 8
GetInfo 296.1 ns 160 B 5
GetLicenseLegal 19.50 ns 0 B 0
GetLicenseBoiler 817.0 ns 1201 B 5
GetLicenseFull 4.088 µs 12384 B 11
CheckGo 4.749 µs 1982 B 27
ConcurrentAccess 331.3 ns 432 B 13
Performance Characteristics
- Version Creation: Constant time O(1), single allocation
- License Retrieval: Zero allocations for legal text (pre-compiled strings)
- Thread-Safe: No locks needed due to immutable design
- Memory Efficient: ~144 bytes per version instance
- Concurrent Access: Linear scaling with CPU cores
Use Cases
1. Application Version Display
v := release.GetVersion()
fmt.Printf("Starting %s\n", v.GetHeader())
// Output: Starting MyApp (Release: v1.2.3, Build: abc123, Date: ...)
2. License File Generation
v := release.GetVersion()
licenseText := v.GetLicenseFull()
os.WriteFile("LICENSE", []byte(licenseText), 0644)
3. Go Version Validation
v := release.GetVersion()
// Require Go >= 1.18
if err := v.CheckGo("1.18", ">="); err != nil {
log.Fatal("This application requires Go 1.18 or later")
}
// Pessimistic constraint (allow patch updates)
if err := v.CheckGo("1.20", "~>"); err != nil {
log.Fatal("This application requires Go 1.20.x")
}
4. Multi-License Projects
v := version.NewVersion(version.License_MIT, ...)
// Combine multiple licenses
combined := v.GetLicenseFull(
version.License_Apache_v2,
version.License_Mozilla_PL_v2,
)
5. File Header Boilerplate
v := release.GetVersion()
header := v.GetLicenseBoiler()
// Add to source files
fileContent := header + "\n\npackage main\n\n// ..."
API Reference
Version Interface
type Version interface {
// Version information
GetPackage() string
GetDescription() string
GetRelease() string
GetBuild() string
GetDate() string
GetTime() time.Time
GetAuthor() string
GetPrefix() string
GetRootPackagePath() string
// Formatted output
GetHeader() string
GetInfo() string
GetAppId() string
// License methods
GetLicenseName() string
GetLicenseLegal(addMoreLicence ...license) string
GetLicenseBoiler(addMoreLicence ...license) string
GetLicenseFull(addMoreLicence ...license) string
// Validation
CheckGo(RequireGoVersion, RequireGoContraint string) liberr.Error
// Output methods
PrintInfo()
PrintLicense(addlicence ...license)
}
License Constants
const (
License_MIT
License_Apache_v2
License_GNU_GPL_v3
License_GNU_Affero_GPL_v3
License_GNU_Lesser_GPL_v3
License_Mozilla_PL_v2
License_Unlicense
License_Creative_Common_Zero_v1
License_Creative_Common_Attribution_v4_int
License_Creative_Common_Attribution_Share_Alike_v4_int
License_SIL_Open_Font_1_1
)
Go Version Constraint Operators
==: Exact version match!=: Not equal>: Greater than>=: Greater than or equal<: Less than<=: Less than or equal~>: Pessimistic constraint (allows patch-level changes)
Error Codes
const (
ErrorParamEmpty // Required parameter is empty
ErrorGoVersionInit // Failed to parse version constraint
ErrorGoVersionRuntime // Failed to extract runtime Go version
ErrorGoVersionConstraint // Go version constraint not satisfied
)
Best Practices
1. Use Build-Time Injection
Always inject version information at build time rather than hardcoding:
go build -ldflags "-X package.Variable=value"
2. Validate Go Version Early
Check Go version constraints during application initialization:
func init() {
if err := release.GetVersion().CheckGo("1.18", ">="); err != nil {
log.Fatal(err)
}
}
3. Single Version Instance
Create one version instance per application (singleton pattern):
var version = version.NewVersion(...) // Package-level variable
func GetVersion() version.Version {
return version
}
4. Package Path Extraction
Use numSubPackage to navigate from nested packages:
// In myapp/internal/release/version.go
v := version.NewVersion(..., EmptyStruct{}, 2)
// Goes up 2 levels: internal/release → internal → myapp
5. License Selection
Choose the appropriate license for your project:
- Permissive: MIT, Apache 2.0, Unlicense
- Copyleft (Strong): GPL v3, AGPL v3
- Copyleft (Weak): LGPL v3, Mozilla PL v2.0
- Creative Commons: CC0, CC BY, CC BY-SA (for content/docs)
- Fonts: SIL Open Font License
Testing
Test Suite: 173 specs using Ginkgo v2 and Gomega (93.8% coverage)
# Run tests
go test ./...
# With coverage
go test -cover ./...
# With race detection (recommended)
CGO_ENABLED=1 go test -race ./...
Coverage Areas
- Version creation and metadata management
- License operations (all 11 licenses)
- Go version constraint validation
- Error handling and edge cases
- Thread safety and concurrent access
Quality Assurance
- ✅ Zero data races (verified with
-race) - ✅ Thread-safe concurrent operations
- ✅ Immutable design pattern
- ✅ High test coverage (93.8%)
See TESTING.md for detailed testing documentation.
Contributing
Contributions are welcome! Please follow these guidelines:
Code Contributions
- Do not use AI to generate package implementation code
- AI may assist with tests, documentation, and bug fixing
- All contributions must pass
go test -race - Maintain or improve test coverage (≥90%)
- Follow existing code style and patterns
Documentation
- Update README.md for new features
- Add examples for common use cases
- Keep TESTING.md synchronized with test changes
Testing
- Write tests for all new features
- Test edge cases and error conditions
- Verify thread safety with race detector
- Add comments explaining complex scenarios
Pull Requests
- Provide clear description of changes
- Reference related issues
- Include test results
- Update documentation
See CONTRIBUTING.md for detailed guidelines.
Future Enhancements
Potential improvements for future versions:
Additional Licenses
- BSD 2-Clause / 3-Clause
- ISC License
- Boost Software License
Enhanced Metadata
- Git branch information
- CI/CD build numbers
- Semantic version parsing
Output Formats
- JSON/YAML version info export
- SPDX license identifiers
- Machine-readable metadata
Validation
- Semantic version validation
- License compatibility checking
- Dependency version tracking
Integration
- GitHub Actions integration
- Docker label generation
- Kubernetes annotation support
Suggestions and contributions are welcome via GitHub issues.
AI Transparency Notice
In accordance with Article 50.4 of the EU AI Act, AI assistance has been used for testing, documentation, and bug fixing under human supervision.
License
MIT License - See LICENSE file for details.
Resources
- Issues: GitHub Issues
- Documentation: GoDoc
- Testing Guide: TESTING.md
- Contributing: CONTRIBUTING.md
- Error Handling: github.com/nabbar/golib/errors
- Version Constraints: github.com/hashicorp/go-version
- License Information: choosealicense.com
- Semantic Versioning: semver.org