Skip to content

Commit f6fcf64

Browse files
committed
vcs: version int 32 and revision method v0.7.6
1 parent 3b4ccac commit f6fcf64

1 file changed

Lines changed: 76 additions & 4 deletions

File tree

vcs/vcs.go

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ package vcs
77
import (
88
"fmt"
99
"runtime/debug"
10+
"strconv"
1011
)
1112

12-
// Version returns the build time and revision information.
13-
// The returned string format is "time-revision" or "time-revision-dirty"
13+
// Version returns the build time, revision hash, and revision integer.
14+
// The returned string format is "time-revision-int" or "time-revision-int-dirty"
1415
// if the working directory had uncommitted changes at build time.
16+
// The integer is derived from the first 4 bytes of the git revision hash.
1517
func Version() string {
1618
var (
1719
time string
@@ -35,9 +37,79 @@ func Version() string {
3537
}
3638
}
3739

40+
revInt, err := revisionToInt(revision)
41+
if err != nil {
42+
revInt = 0
43+
}
44+
3845
if modified {
39-
return fmt.Sprintf("%s-%s-dirty", time, revision)
46+
return fmt.Sprintf("%s-%s-%d-dirty", time, revision, revInt)
47+
}
48+
49+
return fmt.Sprintf("%s-%s-%d", time, revision, revInt)
50+
}
51+
52+
// revisionToInt converts the first 4 bytes (8 hex characters) of a git revision hash
53+
// to a signed 32-bit integer suitable for use as SQLite user_version.
54+
// The conversion uses the first 8 hex characters of the hash, which are
55+
// cryptographically random and uniformly distributed.
56+
//
57+
// Example: "abc123def456..." -> 0xabc123de -> -1412567330
58+
//
59+
// Returns an error if the revision is too short (< 8 characters) or contains
60+
// invalid hexadecimal characters.
61+
func revisionToInt(revision string) (int32, error) {
62+
if len(revision) < 8 {
63+
return 0, fmt.Errorf("revision too short: need at least 8 characters, got %d", len(revision))
64+
}
65+
66+
// Take first 8 hex characters (4 bytes)
67+
hexStr := revision[:8]
68+
69+
// Parse as uint32 first to handle the full range
70+
val, err := strconv.ParseUint(hexStr, 16, 32)
71+
if err != nil {
72+
return 0, fmt.Errorf("failed to parse revision hex: %w", err)
73+
}
74+
75+
// Convert to int32 (may be negative due to sign bit)
76+
return int32(val), nil
77+
}
78+
79+
// VersionInt32 returns the git revision hash and its integer representation.
80+
// The integer is derived from the first 4 bytes (8 hex characters) of the
81+
// revision hash and is suitable for use as SQLite user_version (signed 32-bit integer).
82+
//
83+
// This function extracts the vcs.revision value from the build information
84+
// embedded in the binary during compilation with -buildvcs flag.
85+
//
86+
// Returns:
87+
// - revision: The full git commit hash (e.g., "abc123def456...")
88+
// - revisionInt: A signed 32-bit integer representation (e.g., -1412567330)
89+
// - error: Non-nil if build info is unavailable, revision not found, or parsing fails
90+
func VersionInt32() (string, int32, error) {
91+
var revision string
92+
93+
bi, ok := debug.ReadBuildInfo()
94+
if !ok {
95+
return "", 0, fmt.Errorf("build info not available")
96+
}
97+
98+
for _, s := range bi.Settings {
99+
if s.Key == "vcs.revision" {
100+
revision = s.Value
101+
break
102+
}
103+
}
104+
105+
if revision == "" {
106+
return "", 0, fmt.Errorf("vcs.revision not found in build info")
107+
}
108+
109+
revInt, err := revisionToInt(revision)
110+
if err != nil {
111+
return revision, 0, err
40112
}
41113

42-
return fmt.Sprintf("%s-%s", time, revision)
114+
return revision, revInt, nil
43115
}

0 commit comments

Comments
 (0)