From ba5acc4f8516706b941dc7cfc263b52bf9c603a0 Mon Sep 17 00:00:00 2001 From: ethanrous Date: Sun, 10 May 2026 08:34:43 -0400 Subject: [PATCH] Add version as text in UI --- api/api/openapi.yaml | 8 +++++ api/docs/TowerInfo.md | 26 +++++++++++++++++ api/model_tower_info.go | 37 ++++++++++++++++++++++++ api/ts/dist/AllApi.d.ts | 4 +++ api/ts/generated/api.ts | 4 +++ api/ts/generated/docs/TowerInfo.md | 2 ++ docs/docs.go | 4 +++ docs/swagger.json | 4 +++ docs/swagger.yaml | 5 ++++ modules/config/buildversion.go | 31 ++++++++++++++++++++ modules/wlog/log.go | 28 ++++-------------- modules/wlstructs/tower.go | 4 +++ services/reshape/tower.go | 9 +++++- services/reshape/tower_test.go | 27 +++++++++++++++++ weblens-vue/weblens-nuxt/pages/files.vue | 14 ++++++--- 15 files changed, 180 insertions(+), 27 deletions(-) create mode 100644 modules/config/buildversion.go create mode 100644 services/reshape/tower_test.go diff --git a/api/api/openapi.yaml b/api/api/openapi.yaml index 426599bc..5160d157 100644 --- a/api/api/openapi.yaml +++ b/api/api/openapi.yaml @@ -2608,6 +2608,7 @@ components: instances: - backupSize: 0 lastBackup: 6 + buildVersion: buildVersion role: role logLevel: logLevel userCount: 1 @@ -2619,6 +2620,7 @@ components: coreAddress: coreAddress - backupSize: 0 lastBackup: 6 + buildVersion: buildVersion role: role logLevel: logLevel userCount: 1 @@ -3720,6 +3722,7 @@ components: example: backupSize: 0 lastBackup: 6 + buildVersion: buildVersion role: role logLevel: logLevel userCount: 1 @@ -3733,6 +3736,11 @@ components: backupSize: format: int64 type: integer + buildVersion: + description: |- + BuildVersion is the build version of the running server binary, sourced + from WEBLENS_BUILD_VERSION or the embedded VCS revision. + type: string coreAddress: description: |- Address of the remote server, only if the instance is a core. diff --git a/api/docs/TowerInfo.md b/api/docs/TowerInfo.md index 10c069ec..f04376b3 100644 --- a/api/docs/TowerInfo.md +++ b/api/docs/TowerInfo.md @@ -5,6 +5,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **BackupSize** | **int64** | | +**BuildVersion** | Pointer to **string** | BuildVersion is the build version of the running server binary, sourced from WEBLENS_BUILD_VERSION or the embedded VCS revision. | [optional] **CoreAddress** | **string** | Address of the remote server, only if the instance is a core. Not set for any remotes/backups on core server, as it IS the core | **Id** | **string** | | **LastBackup** | **int64** | | @@ -55,6 +56,31 @@ and a boolean to check if the value has been set. SetBackupSize sets BackupSize field to given value. +### GetBuildVersion + +`func (o *TowerInfo) GetBuildVersion() string` + +GetBuildVersion returns the BuildVersion field if non-nil, zero value otherwise. + +### GetBuildVersionOk + +`func (o *TowerInfo) GetBuildVersionOk() (*string, bool)` + +GetBuildVersionOk returns a tuple with the BuildVersion field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetBuildVersion + +`func (o *TowerInfo) SetBuildVersion(v string)` + +SetBuildVersion sets BuildVersion field to given value. + +### HasBuildVersion + +`func (o *TowerInfo) HasBuildVersion() bool` + +HasBuildVersion returns a boolean if a field has been set. + ### GetCoreAddress `func (o *TowerInfo) GetCoreAddress() string` diff --git a/api/model_tower_info.go b/api/model_tower_info.go index b5e8319e..7cfafe85 100644 --- a/api/model_tower_info.go +++ b/api/model_tower_info.go @@ -22,6 +22,8 @@ var _ MappedNullable = &TowerInfo{} // TowerInfo struct for TowerInfo type TowerInfo struct { BackupSize int64 `json:"backupSize"` + // BuildVersion is the build version of the running server binary, sourced from WEBLENS_BUILD_VERSION or the embedded VCS revision. + BuildVersion *string `json:"buildVersion,omitempty"` // Address of the remote server, only if the instance is a core. Not set for any remotes/backups on core server, as it IS the core CoreAddress string `json:"coreAddress"` Id string `json:"id"` @@ -90,6 +92,38 @@ func (o *TowerInfo) SetBackupSize(v int64) { o.BackupSize = v } +// GetBuildVersion returns the BuildVersion field value if set, zero value otherwise. +func (o *TowerInfo) GetBuildVersion() string { + if o == nil || IsNil(o.BuildVersion) { + var ret string + return ret + } + return *o.BuildVersion +} + +// GetBuildVersionOk returns a tuple with the BuildVersion field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TowerInfo) GetBuildVersionOk() (*string, bool) { + if o == nil || IsNil(o.BuildVersion) { + return nil, false + } + return o.BuildVersion, true +} + +// HasBuildVersion returns a boolean if a field has been set. +func (o *TowerInfo) HasBuildVersion() bool { + if o != nil && !IsNil(o.BuildVersion) { + return true + } + + return false +} + +// SetBuildVersion gets a reference to the given string and assigns it to the BuildVersion field. +func (o *TowerInfo) SetBuildVersion(v string) { + o.BuildVersion = &v +} + // GetCoreAddress returns the CoreAddress field value func (o *TowerInfo) GetCoreAddress() string { if o == nil { @@ -349,6 +383,9 @@ func (o TowerInfo) MarshalJSON() ([]byte, error) { func (o TowerInfo) ToMap() (map[string]interface{}, error) { toSerialize := map[string]interface{}{} toSerialize["backupSize"] = o.BackupSize + if !IsNil(o.BuildVersion) { + toSerialize["buildVersion"] = o.BuildVersion + } toSerialize["coreAddress"] = o.CoreAddress toSerialize["id"] = o.Id toSerialize["lastBackup"] = o.LastBackup diff --git a/api/ts/dist/AllApi.d.ts b/api/ts/dist/AllApi.d.ts index 5db3c4b8..5d2f1847 100644 --- a/api/ts/dist/AllApi.d.ts +++ b/api/ts/dist/AllApi.d.ts @@ -441,6 +441,10 @@ declare const TowerHealthStatusEnum: { type TowerHealthStatusEnum = typeof TowerHealthStatusEnum[keyof typeof TowerHealthStatusEnum]; interface TowerInfo { 'backupSize': number; + /** + * BuildVersion is the build version of the running server binary, sourced from WEBLENS_BUILD_VERSION or the embedded VCS revision. + */ + 'buildVersion'?: string; /** * Address of the remote server, only if the instance is a core. Not set for any remotes/backups on core server, as it IS the core */ diff --git a/api/ts/generated/api.ts b/api/ts/generated/api.ts index 188559b4..1f92d4af 100644 --- a/api/ts/generated/api.ts +++ b/api/ts/generated/api.ts @@ -345,6 +345,10 @@ export type TowerHealthStatusEnum = typeof TowerHealthStatusEnum[keyof typeof To export interface TowerInfo { 'backupSize': number; + /** + * BuildVersion is the build version of the running server binary, sourced from WEBLENS_BUILD_VERSION or the embedded VCS revision. + */ + 'buildVersion'?: string; /** * Address of the remote server, only if the instance is a core. Not set for any remotes/backups on core server, as it IS the core */ diff --git a/api/ts/generated/docs/TowerInfo.md b/api/ts/generated/docs/TowerInfo.md index 58cd2a44..5ccff6c0 100644 --- a/api/ts/generated/docs/TowerInfo.md +++ b/api/ts/generated/docs/TowerInfo.md @@ -6,6 +6,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **backupSize** | **number** | | [default to undefined] +**buildVersion** | **string** | BuildVersion is the build version of the running server binary, sourced from WEBLENS_BUILD_VERSION or the embedded VCS revision. | [optional] [default to undefined] **coreAddress** | **string** | Address of the remote server, only if the instance is a core. Not set for any remotes/backups on core server, as it IS the core | [default to undefined] **id** | **string** | | [default to undefined] **lastBackup** | **number** | | [default to undefined] @@ -24,6 +25,7 @@ import { TowerInfo } from './api'; const instance: TowerInfo = { backupSize, + buildVersion, coreAddress, id, lastBackup, diff --git a/docs/docs.go b/docs/docs.go index 8947a80b..5f3f690b 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -4387,6 +4387,10 @@ const docTemplate = `{ "type": "integer", "format": "int64" }, + "buildVersion": { + "description": "BuildVersion is the build version of the running server binary, sourced\nfrom WEBLENS_BUILD_VERSION or the embedded VCS revision.", + "type": "string" + }, "coreAddress": { "description": "Address of the remote server, only if the instance is a core.\nNot set for any remotes/backups on core server, as it IS the core", "type": "string" diff --git a/docs/swagger.json b/docs/swagger.json index 13004e79..f1dcd9da 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -4385,6 +4385,10 @@ "type": "integer", "format": "int64" }, + "buildVersion": { + "description": "BuildVersion is the build version of the running server binary, sourced\nfrom WEBLENS_BUILD_VERSION or the embedded VCS revision.", + "type": "string" + }, "coreAddress": { "description": "Address of the remote server, only if the instance is a core.\nNot set for any remotes/backups on core server, as it IS the core", "type": "string" diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 4045c1a2..e44a324d 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -544,6 +544,11 @@ definitions: backupSize: format: int64 type: integer + buildVersion: + description: |- + BuildVersion is the build version of the running server binary, sourced + from WEBLENS_BUILD_VERSION or the embedded VCS revision. + type: string coreAddress: description: |- Address of the remote server, only if the instance is a core. diff --git a/modules/config/buildversion.go b/modules/config/buildversion.go new file mode 100644 index 00000000..6a095a49 --- /dev/null +++ b/modules/config/buildversion.go @@ -0,0 +1,31 @@ +package config + +import ( + "os" + "runtime/debug" +) + +const unknownBuildVersion = "unknown" + +// BuildVersion returns the build version of the running binary. It checks the +// WEBLENS_BUILD_VERSION environment variable first, falling back to the +// embedded VCS revision from runtime/debug. Returns "unknown" if neither is +// available. +func BuildVersion() string { + if v := os.Getenv("WEBLENS_BUILD_VERSION"); v != "" { + return v + } + + buildInfo, ok := debug.ReadBuildInfo() + if !ok { + return unknownBuildVersion + } + + for _, s := range buildInfo.Settings { + if s.Key == "vcs.revision" { + return s.Value + } + } + + return unknownBuildVersion +} diff --git a/modules/wlog/log.go b/modules/wlog/log.go index 9f4ec5bf..636998b5 100644 --- a/modules/wlog/log.go +++ b/modules/wlog/log.go @@ -6,7 +6,6 @@ import ( "io" "os" "runtime" - "runtime/debug" "strings" "sync" @@ -105,31 +104,16 @@ func NewZeroLogger(opts ...CreateOpts) *zerolog.Logger { } } - config := config.GetConfig() + wlVersion := config.BuildVersion() + cfg := config.GetConfig() var logWriter io.Writer - if config.LogFormat == "dev" { + if cfg.LogFormat == "dev" { logWriter = newDevLogger(outputLocation) } else { logWriter = outputLocation } - wlVersion := os.Getenv("WEBLENS_BUILD_VERSION") - if wlVersion == "" { - wlVersion = "unknown" - - buildInfo, ok := debug.ReadBuildInfo() - if ok { - for _, v := range buildInfo.Settings { - if v.Key == "vcs.revision" { - wlVersion = v.Value - - break - } - } - } - } - level := zerolog.InfoLevel if o.Level != zerolog.Disabled { level = o.Level @@ -140,7 +124,7 @@ func NewZeroLogger(opts ...CreateOpts) *zerolog.Logger { // If no options are provided, set the global loggers if o.Global { - zerolog.SetGlobalLevel(config.LogLevel) + zerolog.SetGlobalLevel(cfg.LogLevel) loggerMu.Lock() @@ -152,9 +136,9 @@ func NewZeroLogger(opts ...CreateOpts) *zerolog.Logger { loggerMu.Unlock() - log.Info().CallerSkipFrame(2).Msgf("Weblens logger initialized [%s][%s]", log.GetLevel(), config.LogFormat) + log.Info().CallerSkipFrame(2).Msgf("Weblens logger initialized [%s][%s]", log.GetLevel(), cfg.LogFormat) } else { - log.Debug().CallerSkipFrame(1).Msgf("Created new Weblens logger [%s][%s]", log.GetLevel(), config.LogFormat) + log.Debug().CallerSkipFrame(1).Msgf("Created new Weblens logger [%s][%s]", log.GetLevel(), cfg.LogFormat) } return &log diff --git a/modules/wlstructs/tower.go b/modules/wlstructs/tower.go index b7720366..6e146b56 100644 --- a/modules/wlstructs/tower.go +++ b/modules/wlstructs/tower.go @@ -33,6 +33,10 @@ type TowerInfo struct { Started bool `json:"started" validate:"required"` LogLevel string `json:"logLevel,omitempty"` + + // BuildVersion is the build version of the running server binary, sourced + // from WEBLENS_BUILD_VERSION or the embedded VCS revision. + BuildVersion string `json:"buildVersion,omitempty"` } // @name TowerInfo // TowerHealth represents the health status of the server instance. diff --git a/services/reshape/tower.go b/services/reshape/tower.go index 28b16463..7c8519c2 100644 --- a/services/reshape/tower.go +++ b/services/reshape/tower.go @@ -5,6 +5,7 @@ import ( openapi "github.com/ethanrous/weblens/api" tower_model "github.com/ethanrous/weblens/models/tower" + "github.com/ethanrous/weblens/modules/config" "github.com/ethanrous/weblens/modules/wlstructs" context_service "github.com/ethanrous/weblens/services/ctxservice" ) @@ -31,7 +32,7 @@ func TowerToTowerInfo(ctx context.Context, tower tower_model.Instance) wlstructs } } - return wlstructs.TowerInfo{ + info := wlstructs.TowerInfo{ ID: tower.TowerID, Name: tower.Name, Role: string(tower.Role), @@ -45,6 +46,12 @@ func TowerToTowerInfo(ctx context.Context, tower tower_model.Instance) wlstructs ReportedRole: string(tower.Role), Online: online, } + + if tower.IsThisTower { + info.BuildVersion = config.BuildVersion() + } + + return info } // TowerInfoToTower converts a TowerInfo from the API to a tower Instance. diff --git a/services/reshape/tower_test.go b/services/reshape/tower_test.go new file mode 100644 index 00000000..4f8ee689 --- /dev/null +++ b/services/reshape/tower_test.go @@ -0,0 +1,27 @@ +package reshape_test + +import ( + "testing" + + tower_model "github.com/ethanrous/weblens/models/tower" + "github.com/ethanrous/weblens/services/reshape" +) + +func TestTowerToTowerInfo_LocalIncludesBuildVersion(t *testing.T) { + t.Setenv("WEBLENS_BUILD_VERSION", "test-version-1.2.3") + + ctx := newTestAppContext(t) + + tower := tower_model.Instance{ + TowerID: "test-tower", + Name: "Test Tower", + Role: tower_model.RoleCore, + IsThisTower: true, + } + + info := reshape.TowerToTowerInfo(ctx, tower) + + if info.BuildVersion != "test-version-1.2.3" { + t.Fatalf("expected BuildVersion %q, got %q", "test-version-1.2.3", info.BuildVersion) + } +} diff --git a/weblens-vue/weblens-nuxt/pages/files.vue b/weblens-vue/weblens-nuxt/pages/files.vue index 228928f0..9a1d9be8 100644 --- a/weblens-vue/weblens-nuxt/pages/files.vue +++ b/weblens-vue/weblens-nuxt/pages/files.vue @@ -68,10 +68,15 @@ - + @@ -96,6 +101,7 @@ const locationStore = useLocationStore() const presentationStore = usePresentationStore() const filesStore = useFilesStore() const mediaStore = useMediaStore() +const towerStore = useTowerStore() const presentingFile = computed(() => { return filesStore.getFileByID(presentationStore.presentationFileID)