Merged in feature/log-version (pull request #185)

inject and print version at startup for all services

* inject and print version

* coverage
This commit is contained in:
Jay Brown
2025-09-18 21:06:21 +00:00
parent 8a13b6d3b1
commit a6e081e5cd
17 changed files with 233 additions and 5 deletions
+2 -1
View File
@@ -33,13 +33,14 @@ ENV CC=musl-gcc
ARG GIT_VERSION=dev ARG GIT_VERSION=dev
ARG GIT_COMMIT=unknown ARG GIT_COMMIT=unknown
ARG BUILD_TIME=unknown
RUN go build \ RUN go build \
-mod vendor \ -mod vendor \
-tags musl \ -tags musl \
-trimpath \ -trimpath \
-installsuffix cgo \ -installsuffix cgo \
-ldflags "-s -w -linkmode external -extldflags '-static -Wl,--as-needed -Wl,--gc-sections -Wl,-O1' -X main.version=${GIT_VERSION} -X main.gitCommit=${GIT_COMMIT}" \ -ldflags "-s -w -linkmode external -extldflags '-static -Wl,--as-needed -Wl,--gc-sections -Wl,-O1' -X queryorchestration/internal/serviceconfig/build.version=${GIT_VERSION} -X queryorchestration/internal/serviceconfig/build.gitCommit=${GIT_COMMIT} -X queryorchestration/internal/serviceconfig/build.buildTime=${BUILD_TIME}" \
-o bin/ ./cmd/... -o bin/ ./cmd/...
FROM scratch AS final FROM scratch AS final
+4
View File
@@ -13,6 +13,7 @@ import (
clientsyncrunner "queryorchestration/api/clientSyncRunner" clientsyncrunner "queryorchestration/api/clientSyncRunner"
clientsync "queryorchestration/internal/client/sync" clientsync "queryorchestration/internal/client/sync"
"queryorchestration/internal/server/runner" "queryorchestration/internal/server/runner"
"queryorchestration/internal/serviceconfig/build"
"queryorchestration/internal/serviceconfig/queue/documentsync" "queryorchestration/internal/serviceconfig/queue/documentsync"
_ "github.com/lib/pq" _ "github.com/lib/pq"
@@ -24,6 +25,9 @@ type ClientSyncConfig struct {
} }
func main() { func main() {
// Print version information before any environment checks
build.PrintVersionInfo("clientSyncRunner")
ctx := context.Background() ctx := context.Background()
cfg := &ClientSyncConfig{} cfg := &ClientSyncConfig{}
+4
View File
@@ -15,6 +15,7 @@ import (
doccleanrunner "queryorchestration/api/docCleanRunner" doccleanrunner "queryorchestration/api/docCleanRunner"
documentclean "queryorchestration/internal/document/clean" documentclean "queryorchestration/internal/document/clean"
"queryorchestration/internal/server/runner" "queryorchestration/internal/server/runner"
"queryorchestration/internal/serviceconfig/build"
"queryorchestration/internal/serviceconfig/objectstore" "queryorchestration/internal/serviceconfig/objectstore"
"queryorchestration/internal/serviceconfig/queue/documenttext" "queryorchestration/internal/serviceconfig/queue/documenttext"
@@ -28,6 +29,9 @@ type DocCleanConfig struct {
} }
func main() { func main() {
// Print version information before any environment checks
build.PrintVersionInfo("docCleanRunner")
ctx := context.Background() ctx := context.Background()
cfg := &DocCleanConfig{} cfg := &DocCleanConfig{}
+4
View File
@@ -23,6 +23,7 @@ import (
docinitrunner "queryorchestration/api/docInitRunner" docinitrunner "queryorchestration/api/docInitRunner"
documentinit "queryorchestration/internal/document/init" documentinit "queryorchestration/internal/document/init"
"queryorchestration/internal/server/runner" "queryorchestration/internal/server/runner"
"queryorchestration/internal/serviceconfig/build"
documentsyncc "queryorchestration/internal/serviceconfig/queue/documentsync" documentsyncc "queryorchestration/internal/serviceconfig/queue/documentsync"
_ "github.com/lib/pq" _ "github.com/lib/pq"
@@ -34,6 +35,9 @@ type DocInitConfig struct {
} }
func main() { func main() {
// Print version information before any environment checks
build.PrintVersionInfo("docInitRunner")
ctx := context.Background() ctx := context.Background()
cfg := &DocInitConfig{} cfg := &DocInitConfig{}
+4
View File
@@ -19,6 +19,7 @@ import (
"queryorchestration/internal/document" "queryorchestration/internal/document"
documentsync "queryorchestration/internal/document/sync" documentsync "queryorchestration/internal/document/sync"
"queryorchestration/internal/server/runner" "queryorchestration/internal/server/runner"
"queryorchestration/internal/serviceconfig/build"
documentcleanc "queryorchestration/internal/serviceconfig/queue/documentclean" documentcleanc "queryorchestration/internal/serviceconfig/queue/documentclean"
_ "github.com/lib/pq" _ "github.com/lib/pq"
@@ -30,6 +31,9 @@ type DocSyncConfig struct {
} }
func main() { func main() {
// Print version information before any environment checks
build.PrintVersionInfo("docSyncRunner")
ctx := context.Background() ctx := context.Background()
cfg := &DocSyncConfig{} cfg := &DocSyncConfig{}
+4
View File
@@ -15,6 +15,7 @@ import (
doctextrunner "queryorchestration/api/docTextRunner" doctextrunner "queryorchestration/api/docTextRunner"
documenttext "queryorchestration/internal/document/text" documenttext "queryorchestration/internal/document/text"
"queryorchestration/internal/server/runner" "queryorchestration/internal/server/runner"
"queryorchestration/internal/serviceconfig/build"
"queryorchestration/internal/serviceconfig/objectstore" "queryorchestration/internal/serviceconfig/objectstore"
"queryorchestration/internal/serviceconfig/queue/querysync" "queryorchestration/internal/serviceconfig/queue/querysync"
"queryorchestration/internal/serviceconfig/textract" "queryorchestration/internal/serviceconfig/textract"
@@ -30,6 +31,9 @@ type DocTextConfig struct {
} }
func main() { func main() {
// Print version information before any environment checks
build.PrintVersionInfo("docTextRunner")
ctx := context.Background() ctx := context.Background()
cfg := &DocTextConfig{} cfg := &DocTextConfig{}
+5
View File
@@ -5,9 +5,14 @@ import (
"errors" "errors"
"net/http" "net/http"
"os" "os"
"queryorchestration/internal/serviceconfig/build"
) )
func main() { func main() {
// Print version information before any environment checks
build.PrintVersionInfo("healthcheck")
err := check() err := check()
if err != nil { if err != nil {
os.Exit(1) os.Exit(1)
+4
View File
@@ -29,6 +29,7 @@ import (
"queryorchestration/internal/query/result" "queryorchestration/internal/query/result"
"queryorchestration/internal/server/api" "queryorchestration/internal/server/api"
"queryorchestration/internal/serviceconfig" "queryorchestration/internal/serviceconfig"
"queryorchestration/internal/serviceconfig/build"
"queryorchestration/internal/serviceconfig/objectstore" "queryorchestration/internal/serviceconfig/objectstore"
"queryorchestration/internal/serviceconfig/queue/clientsync" "queryorchestration/internal/serviceconfig/queue/clientsync"
"queryorchestration/internal/serviceconfig/queue/queryversionsync" "queryorchestration/internal/serviceconfig/queue/queryversionsync"
@@ -57,6 +58,9 @@ type QueryAPIConfig struct {
} }
func main() { func main() {
// Print version information before any environment checks
build.PrintVersionInfo("queryAPI")
ctx := context.Background() ctx := context.Background()
cfg := &QueryAPIConfig{} cfg := &QueryAPIConfig{}
+4
View File
@@ -18,6 +18,7 @@ import (
resultset "queryorchestration/internal/query/result/set" resultset "queryorchestration/internal/query/result/set"
resultsync "queryorchestration/internal/query/result/sync" resultsync "queryorchestration/internal/query/result/sync"
"queryorchestration/internal/server/runner" "queryorchestration/internal/server/runner"
"queryorchestration/internal/serviceconfig/build"
queryc "queryorchestration/internal/serviceconfig/queue/query" queryc "queryorchestration/internal/serviceconfig/queue/query"
_ "github.com/lib/pq" _ "github.com/lib/pq"
@@ -29,6 +30,9 @@ type QueryConfig struct {
} }
func main() { func main() {
// Print version information before any environment checks
build.PrintVersionInfo("queryRunner")
ctx := context.Background() ctx := context.Background()
cfg := &QueryConfig{} cfg := &QueryConfig{}
+4
View File
@@ -24,6 +24,7 @@ import (
resultsync "queryorchestration/internal/query/result/sync" resultsync "queryorchestration/internal/query/result/sync"
querysync "queryorchestration/internal/query/sync" querysync "queryorchestration/internal/query/sync"
"queryorchestration/internal/server/runner" "queryorchestration/internal/server/runner"
"queryorchestration/internal/serviceconfig/build"
queryc "queryorchestration/internal/serviceconfig/queue/query" queryc "queryorchestration/internal/serviceconfig/queue/query"
_ "github.com/lib/pq" _ "github.com/lib/pq"
@@ -35,6 +36,9 @@ type QuerySyncConfig struct {
} }
func main() { func main() {
// Print version information before any environment checks
build.PrintVersionInfo("querySyncRunner")
ctx := context.Background() ctx := context.Background()
cfg := &QuerySyncConfig{} cfg := &QuerySyncConfig{}
+4
View File
@@ -13,6 +13,7 @@ import (
queryversionsyncrunner "queryorchestration/api/queryVersionSyncRunner" queryversionsyncrunner "queryorchestration/api/queryVersionSyncRunner"
queryversionsync "queryorchestration/internal/query/versionsync" queryversionsync "queryorchestration/internal/query/versionsync"
"queryorchestration/internal/server/runner" "queryorchestration/internal/server/runner"
"queryorchestration/internal/serviceconfig/build"
"queryorchestration/internal/serviceconfig/queue/clientsync" "queryorchestration/internal/serviceconfig/queue/clientsync"
_ "github.com/lib/pq" _ "github.com/lib/pq"
@@ -24,6 +25,9 @@ type QueryVersionSyncConfig struct {
} }
func main() { func main() {
// Print version information before any environment checks
build.PrintVersionInfo("queryVersionSyncRunner")
ctx := context.Background() ctx := context.Background()
cfg := &QueryVersionSyncConfig{} cfg := &QueryVersionSyncConfig{}
+4
View File
@@ -15,6 +15,7 @@ import (
storeeventrunner "queryorchestration/api/storeEventRunner" storeeventrunner "queryorchestration/api/storeEventRunner"
documentstore "queryorchestration/internal/document/store" documentstore "queryorchestration/internal/document/store"
"queryorchestration/internal/server/runner" "queryorchestration/internal/server/runner"
"queryorchestration/internal/serviceconfig/build"
"queryorchestration/internal/serviceconfig/queue/documentinit" "queryorchestration/internal/serviceconfig/queue/documentinit"
_ "github.com/lib/pq" _ "github.com/lib/pq"
@@ -26,6 +27,9 @@ type StoreEventConfig struct {
} }
func main() { func main() {
// Print version information before any environment checks
build.PrintVersionInfo("storeEventRunner")
ctx := context.Background() ctx := context.Background()
cfg := &StoreEventConfig{} cfg := &StoreEventConfig{}
+4
View File
@@ -11,6 +11,7 @@ import (
"queryorchestration/internal/serviceconfig" "queryorchestration/internal/serviceconfig"
"queryorchestration/internal/serviceconfig/aws" "queryorchestration/internal/serviceconfig/aws"
"queryorchestration/internal/serviceconfig/build"
"queryorchestration/internal/serviceconfig/objectstore" "queryorchestration/internal/serviceconfig/objectstore"
"queryorchestration/internal/serviceconfig/queue/querysync" "queryorchestration/internal/serviceconfig/queue/querysync"
"queryorchestration/internal/serviceconfig/textract" "queryorchestration/internal/serviceconfig/textract"
@@ -27,6 +28,9 @@ type Config struct {
} }
func main() { func main() {
// Print version information before any environment checks
build.PrintVersionInfo("textExtractor")
ctx := context.Background() ctx := context.Background()
directory := "files/" directory := "files/"
+4 -1
View File
@@ -45,7 +45,10 @@ func New(ctx context.Context, cfg Config) (func() error, error) {
version := build.GetVersion() version := build.GetVersion()
if cfg.GetLogger() != nil { if cfg.GetLogger() != nil {
cfg.GetLogger().Info("Starting", "version", version) cfg.GetLogger().Info("Starting",
"version", version,
"buildTime", build.GetBuildTime(),
"commit", build.GetGitCommit())
} }
err := database.RunMigrations(ctx, cfg) err := database.RunMigrations(ctx, cfg)
+46 -2
View File
@@ -2,6 +2,7 @@ package build
import ( import (
"fmt" "fmt"
"os"
"runtime/debug" "runtime/debug"
"sync" "sync"
"time" "time"
@@ -11,6 +12,13 @@ const (
SystemName = "doczy" SystemName = "doczy"
) )
// These variables will be set via ldflags during build
var (
version = "(devel)"
gitCommit = "unknown"
buildTime = "unknown"
)
func IsValidUnixVersion(v int64) error { func IsValidUnixVersion(v int64) error {
currentVersion := GetVersionUnixTimestamp() currentVersion := GetVersionUnixTimestamp()
@@ -60,8 +68,9 @@ func GetVersionUnixTimestamp() int64 {
return GetVersionTimestamp().UnixMilli() return GetVersionTimestamp().UnixMilli()
} }
// GetVersion returns the version of the application in the Golang build info format. // GetVersion returns the version of the application.
// This is a raw format and will change depending on the build environment and the git tags. // It first checks if a version was set via ldflags during build.
// If not set via ldflags, it falls back to the Golang build info format.
// When building from a commit with an associated Git tag (e.g., v1.2.3), // When building from a commit with an associated Git tag (e.g., v1.2.3),
// //
// the runtime version uses the tag name without a timestamp. // the runtime version uses the tag name without a timestamp.
@@ -72,9 +81,44 @@ func GetVersionUnixTimestamp() int64 {
// //
// An example of a version v0.0.2-0.20250310175756-a91372dc0602+dirty. // An example of a version v0.0.2-0.20250310175756-a91372dc0602+dirty.
func GetVersion() string { func GetVersion() string {
// Check if version was set via ldflags
if version != "" && version != "(devel)" {
return version
}
// Fall back to debug.ReadBuildInfo
info, ok := debug.ReadBuildInfo() info, ok := debug.ReadBuildInfo()
if !ok { if !ok {
return "unknown" return "unknown"
} }
return info.Main.Version return info.Main.Version
} }
// GetGitCommit returns the git commit hash set during build.
// Returns "unknown" if not set via ldflags.
func GetGitCommit() string {
return gitCommit
}
// GetBuildTime returns the build time set during build.
// Returns "unknown" if not set via ldflags.
func GetBuildTime() string {
return buildTime
}
// PrintVersionInfo prints version information to stderr.
// This should be called at the very beginning of main() before any environment checks.
// It prints to stderr to ensure output even if stdout is redirected or logging isn't initialized.
// Format: Starting <service> version=<version> buildTime=<buildTime> commit=<commit>
func PrintVersionInfo(serviceName string) {
// If version already contains timestamp and commit (format: YYYYMMDD.HHMMSS-commit),
// just use that. Otherwise, construct from parts.
if version != "(devel)" && version != "" {
fmt.Fprintf(os.Stderr, "Starting %s version=%s buildTime=%s commit=%s\n",
serviceName, version, buildTime, gitCommit)
} else {
// Fallback format when not built via Docker
fmt.Fprintf(os.Stderr, "Starting %s version=%s commit=%s\n",
serviceName, GetVersion(), GetGitCommit())
}
}
+125
View File
@@ -1,7 +1,10 @@
package build package build
import ( import (
"bytes"
"os"
"runtime/debug" "runtime/debug"
"strings"
"testing" "testing"
"time" "time"
@@ -129,3 +132,125 @@ func TestGetVersion(t *testing.T) {
t.Errorf("Version not consistent between calls: '%s' != '%s'", version, version2) t.Errorf("Version not consistent between calls: '%s' != '%s'", version, version2)
} }
} }
func TestGetGitCommit(t *testing.T) {
// Test that GetGitCommit returns a non-empty string
commit := GetGitCommit()
if commit == "" {
t.Error("Git commit should not be empty")
}
// In test environment, it should return the default value
if commit != "unknown" && commit != gitCommit {
t.Errorf("Unexpected commit value: %s", commit)
}
}
func TestGetBuildTime(t *testing.T) {
// Test that GetBuildTime returns a non-empty string
bt := GetBuildTime()
if bt == "" {
t.Error("Build time should not be empty")
}
// In test environment, it should return the default value
if bt != "unknown" && bt != buildTime {
t.Errorf("Unexpected build time value: %s", bt)
}
}
func TestPrintVersionInfo(t *testing.T) {
// Save original stderr
oldStderr := os.Stderr
defer func() { os.Stderr = oldStderr }()
// Create a pipe to capture stderr
r, w, _ := os.Pipe()
os.Stderr = w
// Test 1: PrintVersionInfo with default values (devel)
t.Run("with devel version", func(t *testing.T) {
// Save original values
originalVersion := version
originalCommit := gitCommit
originalBuildTime := buildTime
// Set test values
version = "(devel)"
gitCommit = "testcommit"
buildTime = "testtime"
// Call PrintVersionInfo
PrintVersionInfo("testService")
// Close writer and read output
w.Close()
var buf bytes.Buffer
_, err := buf.ReadFrom(r)
if err != nil {
t.Fatalf("Failed to read from pipe: %v", err)
}
output := buf.String()
// Check output contains expected parts
if !strings.Contains(output, "Starting testService") {
t.Errorf("Output should contain service name, got: %s", output)
}
if !strings.Contains(output, "version=") {
t.Errorf("Output should contain version, got: %s", output)
}
// Restore original values
version = originalVersion
gitCommit = originalCommit
buildTime = originalBuildTime
})
// Create new pipe for second test
r2, w2, _ := os.Pipe()
os.Stderr = w2
// Test 2: PrintVersionInfo with custom values
t.Run("with custom version", func(t *testing.T) {
// Save original values
originalVersion := version
originalCommit := gitCommit
originalBuildTime := buildTime
// Set test values
version = "20231201.123456-abc123"
gitCommit = "abc123def456"
buildTime = "2023-12-01T12:34:56Z"
// Call PrintVersionInfo
PrintVersionInfo("customService")
// Close writer and read output
w2.Close()
var buf bytes.Buffer
_, err := buf.ReadFrom(r2)
if err != nil {
t.Fatalf("Failed to read from pipe: %v", err)
}
output := buf.String()
// Check output contains all expected parts
if !strings.Contains(output, "Starting customService") {
t.Errorf("Output should contain service name, got: %s", output)
}
if !strings.Contains(output, "version=20231201.123456-abc123") {
t.Errorf("Output should contain version, got: %s", output)
}
if !strings.Contains(output, "buildTime=2023-12-01T12:34:56Z") {
t.Errorf("Output should contain build time, got: %s", output)
}
if !strings.Contains(output, "commit=abc123def456") {
t.Errorf("Output should contain commit, got: %s", output)
}
// Restore original values
version = originalVersion
gitCommit = originalCommit
buildTime = originalBuildTime
})
}
+7 -1
View File
@@ -5,8 +5,12 @@ version: "3"
vars: vars:
DOCKERFILE: "build/Dockerfile" DOCKERFILE: "build/Dockerfile"
BUILD_TIME:
sh: date -u +"%Y-%m-%dT%H:%M:%SZ"
GIT_SHORT_COMMIT:
sh: git rev-parse --short HEAD
GIT_VERSION: GIT_VERSION:
sh: git describe --tags --always sh: echo "$(date -u +"%Y%m%d.%H%M%S")-$(git rev-parse --short HEAD)"
GIT_COMMIT: GIT_COMMIT:
sh: git rev-parse HEAD sh: git rev-parse HEAD
@@ -21,6 +25,7 @@ tasks:
docker build \ docker build \
--build-arg "GIT_VERSION={{.GIT_VERSION}}" \ --build-arg "GIT_VERSION={{.GIT_VERSION}}" \
--build-arg "GIT_COMMIT={{.GIT_COMMIT}}" \ --build-arg "GIT_COMMIT={{.GIT_COMMIT}}" \
--build-arg "BUILD_TIME={{.BUILD_TIME}}" \
--build-arg BUILDKIT_INLINE_CACHE=1 \ --build-arg BUILDKIT_INLINE_CACHE=1 \
-t {{.IMAGE_NAME}}:latest \ -t {{.IMAGE_NAME}}:latest \
-t {{.IMAGE_NAME}}:{{.GIT_VERSION}} \ -t {{.IMAGE_NAME}}:{{.GIT_VERSION}} \
@@ -32,5 +37,6 @@ tasks:
docker build \ docker build \
--build-arg "GIT_VERSION={{.GIT_VERSION}}" \ --build-arg "GIT_VERSION={{.GIT_VERSION}}" \
--build-arg "GIT_COMMIT={{.GIT_COMMIT}}" \ --build-arg "GIT_COMMIT={{.GIT_COMMIT}}" \
--build-arg "BUILD_TIME={{.BUILD_TIME}}" \
-t {{.IMAGE_NAME}}:debug \ -t {{.IMAGE_NAME}}:debug \
-f build/Dockerfile.debug . -f build/Dockerfile.debug .