From ca2fa3b97ce60cbe4c4de5561a4b4a433c1329e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 Aug 2025 14:20:39 +0000 Subject: [PATCH 1/3] Initial plan From 3d8767bb1502ec28ee46820e5ad745c7fa3048cc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 Aug 2025 14:30:37 +0000 Subject: [PATCH 2/3] Implement SEMAPHORE_BUILD_INFO environment variable override for version information Co-authored-by: fiftin <914224+fiftin@users.noreply.github.com> --- util/version.go | 7 ++++ util/version_test.go | 80 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 util/version_test.go diff --git a/util/version.go b/util/version.go index f837f1cbe..c85bbc9a3 100644 --- a/util/version.go +++ b/util/version.go @@ -1,6 +1,7 @@ package util import ( + "os" "strings" ) @@ -11,6 +12,12 @@ var ( ) func Version() string { + // Check for SEMAPHORE_BUILD_INFO environment variable override + if buildInfo := os.Getenv("SEMAPHORE_BUILD_INFO"); buildInfo != "" { + return buildInfo + } + + // Fall back to default version construction return strings.Join([]string{ Ver, Commit, diff --git a/util/version_test.go b/util/version_test.go new file mode 100644 index 000000000..07be13482 --- /dev/null +++ b/util/version_test.go @@ -0,0 +1,80 @@ +package util + +import ( + "os" + "testing" +) + +func TestVersion_Default(t *testing.T) { + // Ensure no environment variable is set + os.Unsetenv("SEMAPHORE_BUILD_INFO") + + // Set some test values for the build variables + originalVer := Ver + originalCommit := Commit + originalDate := Date + + Ver = "v1.0.0" + Commit = "abc123" + Date = "1234567890" + + defer func() { + Ver = originalVer + Commit = originalCommit + Date = originalDate + }() + + expected := "v1.0.0-abc123-1234567890" + actual := Version() + + if actual != expected { + t.Errorf("Expected version '%s', but got '%s'", expected, actual) + } +} + +func TestVersion_EnvironmentOverride(t *testing.T) { + // Set environment variable + customVersion := "MyCustomVersion-2.0.0-beta" + err := os.Setenv("SEMAPHORE_BUILD_INFO", customVersion) + if err != nil { + t.Fatalf("Failed to set environment variable: %v", err) + } + defer os.Unsetenv("SEMAPHORE_BUILD_INFO") + + actual := Version() + + if actual != customVersion { + t.Errorf("Expected version '%s', but got '%s'", customVersion, actual) + } +} + +func TestVersion_EmptyEnvironmentFallsBackToDefault(t *testing.T) { + // Set empty environment variable + err := os.Setenv("SEMAPHORE_BUILD_INFO", "") + if err != nil { + t.Fatalf("Failed to set environment variable: %v", err) + } + defer os.Unsetenv("SEMAPHORE_BUILD_INFO") + + // Set some test values for the build variables + originalVer := Ver + originalCommit := Commit + originalDate := Date + + Ver = "v1.2.3" + Commit = "def456" + Date = "9876543210" + + defer func() { + Ver = originalVer + Commit = originalCommit + Date = originalDate + }() + + expected := "v1.2.3-def456-9876543210" + actual := Version() + + if actual != expected { + t.Errorf("Expected version '%s', but got '%s'", expected, actual) + } +} \ No newline at end of file From 72fcd0aeef8382d8e213829652894f1ef544c2e0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 Aug 2025 14:33:16 +0000 Subject: [PATCH 3/3] Add documentation for SEMAPHORE_BUILD_INFO environment variable Co-authored-by: fiftin <914224+fiftin@users.noreply.github.com> --- deployment/docker/README.md | 15 +++++++ util/api_integration_test.go | 76 ++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 util/api_integration_test.go diff --git a/deployment/docker/README.md b/deployment/docker/README.md index 97dbe7e7f..29c63b774 100644 --- a/deployment/docker/README.md +++ b/deployment/docker/README.md @@ -31,6 +31,21 @@ variables to the [Task][gotask] command: * `DOCKER_RUNNER`: Define a different name for the runner image, defaults to `runner` * `DOCKER_CMD`: Use another command to build the image, defaults to `docker` +## Customizing Version Information + +If you are building downstream Docker images based on Semaphore and want to display your own version string in the Semaphore UI, you can use the `SEMAPHORE_BUILD_INFO` environment variable: + +```console +# Set custom version info at runtime +docker run -e SEMAPHORE_BUILD_INFO="v2.14.9-mycompany-1.0.0" semaphoreui/semaphore:v2.14.9 + +# Or set it when building your own image +FROM semaphoreui/semaphore:v2.14.9 +ENV SEMAPHORE_BUILD_INFO="v2.14.9-downstream-custom" +``` + +When `SEMAPHORE_BUILD_INFO` is set, it will override the default version display format (`{version}-{commit}-{date}`) in both the CLI and web UI. This is useful for downstream distributions that want to show their own version information to users. + ## Test We defined tasks to handle some linting and to verify the images contain the diff --git a/util/api_integration_test.go b/util/api_integration_test.go new file mode 100644 index 000000000..86ca4bba1 --- /dev/null +++ b/util/api_integration_test.go @@ -0,0 +1,76 @@ +package util + +import ( + "encoding/json" + "os" + "testing" +) + +// TestAPIIntegration simulates how the API uses the Version() function +func TestAPIIntegration_Version(t *testing.T) { + // Test case 1: Default behavior (no environment variable) + os.Unsetenv("SEMAPHORE_BUILD_INFO") + + // Set some known values + originalVer := Ver + originalCommit := Commit + originalDate := Date + + Ver = "v2.14.9" + Commit = "66611eb" + Date = "1746447063" + + defer func() { + Ver = originalVer + Commit = originalCommit + Date = originalDate + }() + + // Simulate what the API does + systemInfo := map[string]interface{}{ + "version": Version(), + "ansible": "some-ansible-version", + "web_host": "localhost", + } + + expected := "v2.14.9-66611eb-1746447063" + if systemInfo["version"] != expected { + t.Errorf("Expected version '%s', but got '%s'", expected, systemInfo["version"]) + } + + // Test case 2: With environment variable override + customVersion := "v2.14.9-downstream-custom" + err := os.Setenv("SEMAPHORE_BUILD_INFO", customVersion) + if err != nil { + t.Fatalf("Failed to set environment variable: %v", err) + } + defer os.Unsetenv("SEMAPHORE_BUILD_INFO") + + // Simulate API call with environment variable set + systemInfo2 := map[string]interface{}{ + "version": Version(), + "ansible": "some-ansible-version", + "web_host": "localhost", + } + + if systemInfo2["version"] != customVersion { + t.Errorf("Expected version '%s', but got '%s'", customVersion, systemInfo2["version"]) + } + + // Test that it can be JSON serialized (as the API does) + jsonData, err := json.Marshal(systemInfo2) + if err != nil { + t.Errorf("Failed to marshal system info to JSON: %v", err) + } + + // Verify the JSON contains our custom version + var unmarshaled map[string]interface{} + err = json.Unmarshal(jsonData, &unmarshaled) + if err != nil { + t.Errorf("Failed to unmarshal JSON: %v", err) + } + + if unmarshaled["version"] != customVersion { + t.Errorf("JSON version mismatch. Expected '%s', got '%s'", customVersion, unmarshaled["version"]) + } +} \ No newline at end of file