GenVer is a tool that improves accessibility to your Go project's dependency information. GenVer comes in two flavours -- a CLI that generates code and an importable package used to retrieve version information during runtime.
This idea came about when a project I was working on required the precise version of each dependency be logged for auditing purposes. Following Go 1.18, this information because available at runtime using the runtime/debug
or runtime/buildinfo
library. However, the data returned is not easily queryable and not performant to interact with.
This can be useful:
- If you need to know external dependency versions at runtime for auditing or logging purposes.
- If you need to build tooling that requires precise dependency information at runtime.
genver
CLI:
go install github.com/gregfurman/genver/cmd/genver@latest
genver
package:
go get github.com/gregfurman/genver
The genver
binary takes, as an argument, the full content of your Go modules in JSON
form. This can easily be retrieved with cmd/go
with the go list -m -json all
command. See Flags for more options and CLI flags. See CLI implementation example for a potential approach.
genver [options] required_json_module_dependencies
options:
-out string
location of generated dependency information. (default "versions.gen.go")
-package string
package of generated dependency information. (default "genver")
-validate
if enabled, uses the Go AST parser to check if the generated file has valid syntax (default true)
All are optional and have reasonable defaults:
--out
: directs the location of the generated code. (default"versions.gen.go"
)--package
: names the package of the generated code. (default"genver"
)--validate
: validate the generated file against the Go AST parser (defaulttrue
)
go list -m -json all | genver
The generated code file will contain information of each package, defined as two const
values per module imported, one for module's Version
and Path
, respectively. The generic naming of each constant provides information in the form <PATH>_<DOMAIN>_<TYPE>
. See this repo'sversion.gen.go
or the CLI example.
Example #1 cloud.google.com/go/[email protected]
:
// Currently using cloud.google.com/go/analytics v0.12.0
const (
GoAnalytics_Google_Version = "v0.12.0"
GoAnalytics_Google_Path = "cloud.google.com/go/analytics"
)
Example #2 [email protected]
:
// Currently using go.opencensus.io v0.24.0
const (
Opencensus_Version = "v0.24.0"
Opencensus_Path = "go.opencensus.io"
)
Example #3 sigs.k8s.io/[email protected]
:
// Currently using sigs.k8s.io/yaml v1.3.0
const (
Yaml_K8s_Version = "v1.3.0"
Yaml_K8s_Path = "sigs.k8s.io/yaml"
)
The package exposes a NewDependencyVersionStore
function that returns a *DependencyStore
. This will pull in dependencies from runtime and populate each module name and version into a trie. This exposes the following methods:
FindVersionFromPath(path string) any
: Pass in an arbitrary pathname and get its version as output (ornil
if not found)FindVersionFromData(data any) any
: Pass in an arbitrary data type and the version of the module that imported it in get the module (ornil
if not found)
import (
"github.com/gregfurman/genver"
"google.golang.org/grpc/codes" // v1.59.0
"google.golang.org/protobuf/proto" // v1.31.0
)
func main() {
versionStore := genver.NewDependencyVersionStore()
// Find the dependency version from a pathname
grpcVersion := versionStore.FindVersionFromPath("google.golang.org/grpc/codes")
fmt.Println("Using grpc@%s", grpcVersion)
// Find the module version from a dependency's attribute
// where proto.String is a signature to an exported function
pbVersion := versionStore.FindVersionFromData(proto.String)
fmt.Println("Using protobuf@%s", pbVersion)
}
Using [email protected]
Using [email protected]
- Provide more code examples
- Potentially fix installation guide
- Give more rationale behind using a trie
- Explain more on rationale behind creation
- Add makefile and github actions
- Add more tests and documentation -- perhaps consolidate all code into single
internal
package - Look into using cobra and improve CLI docs
- Perhaps add args to generated files