How do you read debug VCS version info from a Go 1.18 binary?
Asked Answered
A

2

8

I'm trying to read version control information from my Go binaries, but the build info doesn't seem to contain any VCS info.

From the Go 1.18 release notes:

The go command now embeds version control information in binaries. It includes the currently checked-out revision, commit time, and a flag indicating whether edited or untracked files are present. Version control information is embedded if the go command is invoked in a directory within a Git, Mercurial, Fossil, or Bazaar repository, and the main package and its containing main module are in the same repository. This information may be omitted using the flag -buildvcs=false.

Sample program:

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    if bi, ok := debug.ReadBuildInfo(); ok {
        fmt.Printf("%+v\n", bi)
    }
}

Using this code, here's my output:

go      go1.18
path    [redacted]
mod     [redacted]     (devel)
dep     [redacted]        v1.2.3  
build   -compiler=gc
build   CGO_ENABLED=1
build   CGO_CFLAGS=
build   CGO_CPPFLAGS=
build   CGO_CXXFLAGS=
build   CGO_LDFLAGS=
build   GOARCH=amd64
build   GOOS=windows
build   GOAMD64=v1

I expected to see this included, but didn't:

build   vcs=git
build   vcs.revision=[sha]
build   vcs.time=2022-03-28T03:11:12Z
build   vcs.modified=true

I'm running the go command from the root directory of a git repo, and the repo has commits. The program I'm building is in a subdirectory of the repo.

I have tried a number of different variations for building the binary, but none of them worked:

go run client/main.go
go build -o client.exe client/main.go && ./client.exe
go build -o client.exe client/main.go && ./client.exe
go build -o client.exe -buildvcs=true client/main.go && ./client.exe

To be clear, the program builds and executes, but doesn't contain embedded VCS information.

I also tried using go version -m client.exe to see if that could read something that the binary couldn't read about itself, but the results were the same.

I'm using the first release of Go 1.18, so -buildvcs should still default to true as far as I know. I saw on the issue tracker that a future minor release will likely change the default to -buildvcs=auto.

As far as I can tell, both conditions listed in the release notes (go is invoked in a directory inside a git repo, and the main package is in the same repo) should both be satisfied when building from the project root. What might I be doing wrong?

Afghani answered 28/3, 2022 at 4:19 Comment(6)
The go command operates on packages -- you usually never want to use filename arguments. Using go build with the full package name, or your relative path from within the module (e.g. go build ./client) would suffice.Cell
@Cell interesting. I had tried using go build client/ from the project root and it resulted in an error of "package is not in GOROOT", but go build ./client worked as you suggested. If you were the one that downvoted the question could you explain how I might improve the quality of the question?Afghani
Yes, a non-relative path argument will always be interpreted as a package name, and the package "client" isn't going to be found globally. The question looks fine, it was probably the fact that "don't use filename arguments" has to be repeated so frequently here as a fundamental cause of errors.Cell
@Cell I think some of that confusion stems from the fact that every other cli tool that I can remember interacting with (as well as browser url paths) would interpret a name as relative to the current working directory, but go explicitly requires the "./" prefix on the argument in order to build from a relative path. Perhaps it's a relic from when go assumed all go code existed in GOROOT.Afghani
GOROOT only ever contained the stdlib packages, but it's sort of the "end of the line" when it runs out of places to search, so that's the error you see. If your module were called client and contained a go package at the root level, the command would then be valid, though it would build the current directory rather that ./client. You either have to be explicit by using the full package name, or a non-ambiguous path.Cell
@Cell my mistake, I confused GOROOT for GOPATH, and also seem to be confusing filesystem paths for package name paths. I'll have to read through the full go command documentation at some point. Does my updated answer at least seem to be correct enough?Afghani
A
7

I believe my issue was due to incorrect usage of the go run and go build commands. I was running and building against file patterns because I had originally tried go build client from the project root and gotten an error of "package is not in GOROOT", so I was building file patterns instead without understanding the difference (because it compiled and ran successfully).

I found that if I change directories into the subdirectory with the program I want to build (rather than build from the project root) the build command will embed the version information as expected.

e.g.:

cd client/ && go build

As @JimB pointed out in the comments, the go command is meant to operate on packages.

When building from the project root, this is the command I should have been using that will properly embed VCS version info inside the binary:

go build ./client

This behavior around relative path names is still confusing behavior to me, since I had expected go build client and go build ./client to be equivalent (which they are apparently not). Behavior around relative paths seems to be documented here: https://pkg.go.dev/cmd/[email protected]#hdr-Relative_import_paths

Afghani answered 28/3, 2022 at 4:19 Comment(0)
R
0

I was facing an issue where debug.ReadBuildInfo() didn't have vcs.revision set for a binary built within Docker, based off an alpine image.

Git needs to be available for this info to be added to the build. Installing git resolved the issue.

Before

FROM golang:1.22-alpine as builder

# some commands...

# build
RUN go build -v -o /usr/local/bin/my_app some.domain.dev/cmd/api

# Another stage with only the binary...

After

FROM golang:1.22-alpine as builder

# some commands...

# install Git

RUN apk add git

# build
RUN go build -v -o /usr/local/bin/my_app some.domain.dev/cmd/api

# Another stage with only the binary...
Retroflex answered 8/8 at 18:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.