Skip to content

Commit

Permalink
buildstamp: Offer version string via function call
Browse files Browse the repository at this point in the history
Currently, version info is available as part of the version information
returned pre-formatted for printing to a console/log. However, we want
to get version info to share with an external server, so this change
creates such a function.

If the binary has no stamped version info, the function will error
(which implies that during development, testing against a real server
will require version stamping to be enabled).

If the released binary is a real release, it will return a common
version string (`vX.Y.Z`). If the binary is not a real release, it will
return a pseudo-version similar to the format used for [Go
modules](https://go.dev/ref/mod#pseudo-versions) - namely,
`v0.0.0-yyyymmddhhmmss-abcdefabcdef`, where the date is the build date,
and the commit is the commit the tool's build was based on. No component
of the version string will indicate if the code tree was dirty.

Tested: unit tests only
Bug: linear/CUS-385
  • Loading branch information
minor-fixes committed Aug 14, 2024
1 parent d0a943e commit bc7afcf
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 2 deletions.
9 changes: 8 additions & 1 deletion internal/buildstamp/BUILD
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
load("@rules_go//go:def.bzl", "go_library")
load("@rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "buildstamp",
Expand All @@ -16,3 +16,10 @@ go_library(
"buildTimestamp": "{BUILD_TIMESTAMP}",
},
)

go_test(
name = "buildstamp_test",
srcs = ["buildstamp_test.go"],
embed = [":buildstamp"],
deps = ["@com_github_stretchr_testify//assert"],
)
37 changes: 36 additions & 1 deletion internal/buildstamp/buildstamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package buildstamp

import (
"errors"
"fmt"
"regexp"
"runtime/debug"
Expand Down Expand Up @@ -53,6 +54,8 @@ var (
emptyValues Vars

gitOfficialBranchRe = regexp.MustCompile(`main|release/v[0-9]+\.[0-9]+`)

ErrStampingDisabled = errors.New("build metadata is unavailable (build with bazel's `--stamp` flag to enable)")
)

func init() {
Expand Down Expand Up @@ -116,7 +119,7 @@ func lookupBuildSetting(info *debug.BuildInfo, name string) (string, bool) {

func (v Vars) String() string {
if v == emptyValues {
return "build metadata is unavailable (build with bazel's `--stamp` flag to enable)"
return ErrStampingDisabled.Error()
}
var sb strings.Builder
if v.ReleaseVersion != unknown {
Expand All @@ -129,3 +132,35 @@ func (v Vars) String() string {
fmt.Fprintf(&sb, "clean build: %v\n", v.IsClean)
return sb.String()
}

type ErrUnofficialBuild struct {
parent *Vars
}

func (e ErrUnofficialBuild) Error() string {
why := "unknown reason"
if !e.parent.IsClean {
why = "built with modified source files"
} else if !gitOfficialBranchRe.MatchString(e.parent.SourceBranch) {
why = "built from non-release branch"
}
return "build is not official: " + why
}

func (v Vars) GetVersion() (string, error) {
if v == emptyValues {
return "", ErrStampingDisabled
}
if !v.IsOfficial || v.ReleaseVersion == unknown {
return fmt.Sprintf("v0.0.0-%s-%s", compactTime(v.BuildTimestamp), shortHash(v.SourceRevision)), nil
}
return v.ReleaseVersion, nil
}

func compactTime(t time.Time) string {
return t.UTC().Format("20060102150405")
}

func shortHash(commitStr string) string {
return commitStr[:11]
}
89 changes: 89 additions & 0 deletions internal/buildstamp/buildstamp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package buildstamp

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestGetVersion(t *testing.T) {
testCases := []struct {
desc string
vars Vars
wantVersion string
wantErr string
}{
{
desc: "stamped official build",
vars: Vars{
ReleaseVersion: "v1.0.2",
SourceBranch: "main",
SourceRevision: "abcdefbutnotg",
IsClean: true,
IsOfficial: true,
BuildTimestamp: time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC),
},
wantVersion: "v1.0.2",
},
{
desc: "unstamped build",
vars: emptyValues,
wantErr: ErrStampingDisabled.Error(),
},
{
desc: "unofficial due to dirty repo",
vars: Vars{
ReleaseVersion: unknown,
SourceBranch: "main",
SourceRevision: "abcdefbutnotg",
IsClean: false,
IsOfficial: false,
BuildTimestamp: time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC),
},
wantVersion: "v0.0.0-20240102030405-abcdefbutno",
},
{
desc: "unofficial due to wrong branch",
vars: Vars{
ReleaseVersion: unknown,
SourceBranch: "some_dev_branch",
SourceRevision: "abcdefbutnotg",
IsClean: true,
IsOfficial: false,
BuildTimestamp: time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC),
},
wantVersion: "v0.0.0-20240102030405-abcdefbutno",
},
{
desc: "unofficial due to unknown reason",
vars: Vars{
ReleaseVersion: "v1.0.2",
SourceBranch: "main",
SourceRevision: "abcdefbutnotg",
IsClean: true,
IsOfficial: false,
BuildTimestamp: time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC),
},
wantVersion: "v0.0.0-20240102030405-abcdefbutno",
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
got, gotErr := tc.vars.GetVersion()
t.Logf("got: %v; gotErr: %v", got, gotErr)

if tc.wantErr != "" {
assert.ErrorContains(t, gotErr, tc.wantErr)
} else {
assert.NoError(t, gotErr)
}

if gotErr != nil {
return
}

assert.Equal(t, tc.wantVersion, got)
})
}
}

0 comments on commit bc7afcf

Please sign in to comment.