Move to setting version info explicitly in linker flags
Some checks failed
/ golint (push) Failing after 12s

We don't have go built-in VCS information in a nix build because the git
repository isn't present. After struggling to build an overlay that
would provide it, I decided this path is easier of just injecting in the
data that we need.

Issue: #5
This commit is contained in:
Eli Ribble 2026-05-19 19:45:04 +00:00
parent 81826f853e
commit d4cbfb960e
No known key found for this signature in database
8 changed files with 119 additions and 79 deletions

View file

@ -322,7 +322,7 @@ type tegolaURLs struct {
} }
func getRoot(ctx context.Context, r *http.Request, q resource.QueryParams) (*about, *nhttp.ErrorWithStatus) { func getRoot(ctx context.Context, r *http.Request, q resource.QueryParams) (*about, *nhttp.ErrorWithStatus) {
v := version.Get() v := GetVersionInfo()
return &about{ return &about{
Environment: config.Environment, Environment: config.Environment,
SentryDSN: config.SentryDSNFrontend, SentryDSN: config.SentryDSNFrontend,

View file

@ -11,7 +11,6 @@ import (
"source.gleipnir.technology/Gleipnir/nidus-sync/lint" "source.gleipnir.technology/Gleipnir/nidus-sync/lint"
"source.gleipnir.technology/Gleipnir/nidus-sync/platform" "source.gleipnir.technology/Gleipnir/nidus-sync/platform"
"source.gleipnir.technology/Gleipnir/nidus-sync/platform/event" "source.gleipnir.technology/Gleipnir/nidus-sync/platform/event"
"source.gleipnir.technology/Gleipnir/nidus-sync/version"
) )
var connectionsSSE map[*ConnectionSSE]bool = make(map[*ConnectionSSE]bool, 0) var connectionsSSE map[*ConnectionSSE]bool = make(map[*ConnectionSSE]bool, 0)
@ -41,7 +40,7 @@ type Status struct {
func (c *ConnectionSSE) SendEvent(w http.ResponseWriter, m platform.Event) error { func (c *ConnectionSSE) SendEvent(w http.ResponseWriter, m platform.Event) error {
if m.Type == event.EventTypeShutdown { if m.Type == event.EventTypeShutdown {
v := version.Get() v := GetVersionInfo()
return send(w, Status{ return send(w, Status{
BuildTime: v.BuildTime, BuildTime: v.BuildTime,
IsModified: v.IsModified, IsModified: v.IsModified,
@ -121,7 +120,7 @@ func streamEvents(w http.ResponseWriter, r *http.Request, u platform.User) {
log.Debug().Int32("org", u.Organization.ID).Int("user", u.ID).Str("id", uid.String()).Msg("connected SSE client") log.Debug().Int32("org", u.Organization.ID).Int("user", u.ID).Str("id", uid.String()).Msg("connected SSE client")
// Send an initial connected event // Send an initial connected event
v := version.Get() v := GetVersionInfo()
status := Status{ status := Status{
BuildTime: v.BuildTime, BuildTime: v.BuildTime,
IsModified: v.IsModified, IsModified: v.IsModified,

16
api/version.go Normal file
View file

@ -0,0 +1,16 @@
package api
import (
"source.gleipnir.technology/Gleipnir/nidus-sync/version"
)
var (
versionInfo version.VersionInfo
)
func GetVersionInfo() version.VersionInfo {
return versionInfo
}
func SetVersionInfo(v version.VersionInfo) {
versionInfo = v
}

View file

@ -1,4 +1,4 @@
{ pkgs ? import <nixpkgs> { }, proj ? pkgs.proj }: { ldflags, version, pkgs ? import <nixpkgs> { }, proj ? pkgs.proj }:
let let
# Get commit timestamp at eval time (pure) # Get commit timestamp at eval time (pure)
@ -12,24 +12,12 @@ let
else "0"; else "0";
in in
pkgs.buildGoModule rec { pkgs.buildGoModule rec {
inherit ldflags version;
pname = "nidus-sync"; pname = "nidus-sync";
version = "0.0.12";
src = ./.; src = ./.;
gitRevision =
if builtins.pathExists ./.git
then pkgs.lib.commitIdFromGitRepo ./.git
else "unknown";
buildTime = getCommitTime; buildTime = getCommitTime;
ldflags = [
"-s"
"-w"
"-X main.Version=${version}"
"-X main.Commit=${gitRevision}"
"-X main.BuildTime=${buildTime}"
];
meta = { meta = {
description = "Nidus Sync"; description = "Nidus Sync";
homepage = "https://github.com/Gleipnir-Technology/nidus-sync"; homepage = "https://github.com/Gleipnir-Technology/nidus-sync";
@ -74,4 +62,5 @@ pkgs.buildGoModule rec {
cp -r vite/rmo/static/gen/rmo $out/share/frontend/ cp -r vite/rmo/static/gen/rmo $out/share/frontend/
cp -r vite/rmo/static/gen/sync $out/share/frontend/ cp -r vite/rmo/static/gen/sync $out/share/frontend/
''; '';
} }

6
flake.lock generated
View file

@ -75,11 +75,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1772542754, "lastModified": 1778869304,
"narHash": "sha256-WGV2hy+VIeQsYXpsLjdr4GvHv5eECMISX1zKLTedhdg=", "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "8c809a146a140c5c8806f13399592dbcb1bb5dc4", "rev": "d233902339c02a9c334e7e593de68855ad26c4cb",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -21,8 +21,32 @@
projPkg = proj.packages.${system}.default; projPkg = proj.packages.${system}.default;
package = pkgs.callPackage ./default.nix { package = pkgs.callPackage ./default.nix {
proj = projPkg; ldflags = ldflags;
}; proj = projPkg;
version = packageVersion;
};
# Pull VCS metadata from the flake's own git source.
# These fields are available when the flake lives inside a git checkout.
gitRevision = self.sourceInfo.rev or "dirty";
gitShortRev = self.sourceInfo.shortRev or "dirty";
gitDateTime = self.sourceInfo.lastModifiedDate or "unknown";
# Derive a human-readable version from the git date + short rev.
# lastModifiedDate is YYYYMMDDHHMMSS; pull out just YYYY-MM-DD.
packageVersion =
let
y = builtins.substring 0 4 gitDateTime;
m = builtins.substring 4 2 gitDateTime;
d = builtins.substring 6 2 gitDateTime;
in "${y}-${m}-${d}.${gitShortRev}";
ldflags = [
"-s" "-w"
"-X main.BuildTime=${gitDateTime}"
"-X main.Revision=${gitRevision}"
"-X main.Version=${packageVersion}"
];
in in
{ {
packages.default = package; packages.default = package;

70
main.go
View file

@ -9,6 +9,7 @@ import (
"os" "os"
"os/signal" "os/signal"
"runtime/debug" "runtime/debug"
//"strconv"
"syscall" "syscall"
"time" "time"
@ -33,6 +34,13 @@ import (
"source.gleipnir.technology/Gleipnir/nidus-sync/version" "source.gleipnir.technology/Gleipnir/nidus-sync/version"
) )
// These will be set by ldflags at build time by Nix
var (
BuildTime = "unknown"
Revision = "unknown"
Version = "dev"
)
func main() { func main() {
err := config.Parse() err := config.Parse()
if err != nil { if err != nil {
@ -46,8 +54,19 @@ func main() {
log.Warn().Msg("Forcing production mode for testing templates") log.Warn().Msg("Forcing production mode for testing templates")
config.Environment = "PRODUCTION" config.Environment = "PRODUCTION"
} }
v := version.Get() v := GetVersionInfo()
log.Info().Str("environment", config.Environment).Bool("is-prod", config.IsProductionEnvironment()).Str("revision", v.Revision).Str("build_time", v.BuildTime.String()).Bool("is modified", v.IsModified).Msg("Starting") log.Info().
Str("environment", config.Environment).
Bool("is-prod", config.IsProductionEnvironment()).
Str("revision", v.Revision).
Str("build_time", v.BuildTime.String()).
Bool("is modified", v.IsModified).
Str("Version", Version).
Str("Revision", Revision).
Str("BuildDate", BuildTime).
Msg("Starting")
api.SetVersionInfo(v)
err = sentry.Init(sentry.ClientOptions{ err = sentry.Init(sentry.ClientOptions{
Debug: false, //!config.IsProductionEnvironment(), Debug: false, //!config.IsProductionEnvironment(),
Dsn: config.SentryDSN, Dsn: config.SentryDSN,
@ -285,3 +304,50 @@ func LoggerMiddleware(logger *zerolog.Logger) func(next http.Handler) http.Handl
return http.HandlerFunc(fn) return http.HandlerFunc(fn)
} }
} }
func GetVersionInfo() (version.VersionInfo, error) {
// Try ldflags first (set by Nix build)
if Revision != "" && Revision != "unknown" {
build_time, err := parseNixTime(BuildTime)
if err != nil {
return version.VersionInfo{}, fmt.Errorf("parse nix time: %w", err)
}
return version.VersionInfo{
BuildTime: build_time,
IsModified: Revision == "dirty",
Revision: Revision,
}, nil
}
// Fallback to debug.ReadBuildInfo() for development
info, ok := debug.ReadBuildInfo()
if !ok {
return version.VersionInfo{}, fmt.Errorf("read build info not ok")
}
var v version.VersionInfo
for _, setting := range info.Settings {
switch setting.Key {
case "vcs.modified":
v.IsModified = setting.Value == "true"
case "vcs.revision":
if len(setting.Value) > 7 {
v.Revision = setting.Value[:7]
} else {
v.Revision = setting.Value
}
case "vcs.time":
if t, err := time.Parse(time.RFC3339, setting.Value); err == nil {
v.BuildTime = t
}
}
}
return v, nil
}
func parseNixTime(timestamp string) (time.Time, error) {
layout := "20060102150405"
t, err := time.Parse(layout, timestamp)
if err != nil {
return time.Time{}, err
}
return t, nil
}

View file

@ -1,65 +1,11 @@
package version package version
import ( import (
"runtime/debug"
"strconv"
"time" "time"
) )
// These will be set by ldflags at build time
var (
Version = "dev"
Commit = ""
BuildTime = "0"
IsModified = "false"
)
type VersionInfo struct { type VersionInfo struct {
BuildTime time.Time `json:"build_time"` BuildTime time.Time `json:"build_time"`
IsModified bool `json:"is_modified"` IsModified bool `json:"is_modified"`
Revision string `json:"revision"` Revision string `json:"revision"`
} }
func Get() VersionInfo {
// Try ldflags first (set by Nix build)
if Commit != "" && Commit != "unknown" {
var buildTime time.Time
if timestamp, err := strconv.ParseInt(BuildTime, 10, 64); err == nil && timestamp > 0 {
buildTime = time.Unix(timestamp, 0)
}
return VersionInfo{
Revision: Commit,
BuildTime: buildTime,
IsModified: IsModified == "true",
}
}
// Fallback to debug.ReadBuildInfo() for development
info, ok := debug.ReadBuildInfo()
if !ok {
return VersionInfo{
Revision: "unknown",
}
}
var version VersionInfo
for _, setting := range info.Settings {
switch setting.Key {
case "vcs.modified":
version.IsModified = setting.Value == "true"
case "vcs.revision":
if len(setting.Value) > 7 {
version.Revision = setting.Value[:7]
} else {
version.Revision = setting.Value
}
case "vcs.time":
if t, err := time.Parse(time.RFC3339, setting.Value); err == nil {
version.BuildTime = t
}
}
}
return version
}