Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ jobs:
CGO_CFLAGS: "-mmacosx-version-min=13.0"
CGO_LDFLAGS: "-mmacosx-version-min=13.0"
run: |
LDFLAGS="-s -w -X mcpproxy-go/cmd/mcpproxy.version=${VERSION} -X main.version=${VERSION}"
LDFLAGS="-s -w -X main.version=${VERSION} -X github.com/smart-mcp-proxy/mcpproxy-go/internal/httpapi.buildVersion=${VERSION}"

# Determine clean binary name
if [ "${{ matrix.goos }}" = "windows" ]; then
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/prerelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ jobs:
fi

echo "Building version: ${VERSION}"
LDFLAGS="-s -w -X mcpproxy-go/cmd/mcpproxy.version=${VERSION} -X main.version=${VERSION} -X mcpproxy-go/internal/httpapi.buildVersion=${VERSION}"
# -X paths must use the full module path (go.mod: github.com/smart-mcp-proxy/mcpproxy-go);
# a wrong path is silently ignored and the binary reports "development".
LDFLAGS="-s -w -X main.version=${VERSION} -X github.com/smart-mcp-proxy/mcpproxy-go/internal/httpapi.buildVersion=${VERSION}"

# Determine clean binary name
if [ "${{ matrix.goos }}" = "windows" ]; then
Expand Down
14 changes: 14 additions & 0 deletions internal/updatecheck/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,12 @@ func (c *Checker) compareVersions(current, latest string) bool {
currentSemver := ensureVPrefix(current)
latestSemver := ensureVPrefix(latest)

// semver.Compare ranks an invalid version below every valid one, which
// would flag any published release as an "update" for a dev build.
if !semver.IsValid(currentSemver) {
return false
}

// semver.Compare returns -1 if current < latest
return semver.Compare(currentSemver, latestSemver) < 0
}
Expand Down Expand Up @@ -377,6 +383,14 @@ func (c *Checker) CheckNow() *VersionInfo {
c.logger.Debug("Immediate update check skipped: update checking disabled")
return nil
}
// Same guard as Start(): a non-semver current version (e.g. "development")
// cannot be meaningfully compared, and offering the latest stable release
// against it is a downgrade prompt, not an update.
if !c.isValidSemver() {
c.logger.Debug("Immediate update check skipped: non-semver version",
zap.String("version", c.version))
return nil
}
c.logger.Debug("Performing immediate update check")
c.check()
return c.GetVersionInfo()
Expand Down
33 changes: 33 additions & 0 deletions internal/updatecheck/checker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,35 @@ func TestChecker_CheckNow_NoUpdate(t *testing.T) {
}
}

// TestChecker_CheckNow_NonSemverVersionSkipsCheck verifies that the forced
// refresh path has the same non-semver guard as Start(): a "development"
// build must never run a check, because compareVersions would rank the
// invalid current version below ANY published release and every surface
// (Web UI banner, doctor, status) would offer a stable-version "update" —
// a downgrade prompt on dev/misbuilt binaries.
func TestChecker_CheckNow_NonSemverVersionSkipsCheck(t *testing.T) {
for _, version := range []string{"development", "dev", ""} {
t.Run("version_"+version, func(t *testing.T) {
checker := New(zaptest.NewLogger(t), version)

checked := false
checker.SetCheckFunc(func() (*GitHubRelease, error) {
checked = true
return &GitHubRelease{TagName: "v1.1.0", HTMLURL: "https://example.com"}, nil
})

info := checker.CheckNow()

if checked {
t.Error("CheckNow ran a network check for a non-semver version")
}
if info != nil && info.UpdateAvailable {
t.Errorf("UpdateAvailable = true for non-semver version %q", version)
}
})
}
}

// TestChecker_UpdateAvailableLoggedOncePerVersion verifies the "Update available"
// Info log is emitted exactly once per detected latest version, not on every
// periodic tick (FR-004 of specs/079-upgrade-nudge: no repeated log spam).
Expand Down Expand Up @@ -168,6 +197,10 @@ func TestChecker_CompareVersions(t *testing.T) {
{"1.0.0", "1.1.0", true}, // Without v prefix
{"v0.11.1", "v0.11.3", true},
{"v0.11.2", "v0.11.2", false},
// An invalid current version must never be treated as "older than
// everything" — that turns a dev build into a permanent downgrade nudge.
{"development", "v1.0.0", false},
{"", "v1.0.0", false},
}

for _, tc := range tests {
Expand Down
4 changes: 2 additions & 2 deletions scripts/build-windows-installer.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Write-Host " Building mcpproxy.exe..." -ForegroundColor White
$env:CGO_ENABLED = "0"
$CoreBinary = Join-Path $BinDir "mcpproxy.exe"
$CoreCmd = Join-Path $RepoRoot "cmd/mcpproxy"
go build -buildvcs=false -ldflags "-s -w -X main.version=$Version" -o $CoreBinary $CoreCmd
go build -buildvcs=false -ldflags "-s -w -X main.version=$Version -X github.com/smart-mcp-proxy/mcpproxy-go/internal/httpapi.buildVersion=$Version" -o $CoreBinary $CoreCmd

if ($LASTEXITCODE -ne 0) {
Write-Host " Failed to build mcpproxy.exe" -ForegroundColor Red
Expand All @@ -61,7 +61,7 @@ Write-Host " Building mcpproxy-tray.exe..." -ForegroundColor White
$env:CGO_ENABLED = "1"
$TrayBinary = Join-Path $BinDir "mcpproxy-tray.exe"
$TrayCmd = Join-Path $RepoRoot "cmd/mcpproxy-tray"
go build -buildvcs=false -ldflags "-s -w -X main.version=$Version -H windowsgui" -o $TrayBinary $TrayCmd
go build -buildvcs=false -ldflags "-s -w -X main.version=$Version -X github.com/smart-mcp-proxy/mcpproxy-go/internal/httpapi.buildVersion=$Version -H windowsgui" -o $TrayBinary $TrayCmd

if ($LASTEXITCODE -ne 0) {
Write-Host " Failed to build mcpproxy-tray.exe" -ForegroundColor Red
Expand Down
11 changes: 5 additions & 6 deletions scripts/build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Write-Host "Commit: $Commit" -ForegroundColor Green
Write-Host "Date: $Date" -ForegroundColor Green
Write-Host ""

$LDFLAGS = "-X main.version=$Version -X main.commit=$Commit -X main.date=$Date -s -w"
$LDFLAGS = "-X main.version=$Version -X main.commit=$Commit -X main.date=$Date -X github.com/smart-mcp-proxy/mcpproxy-go/internal/httpapi.buildVersion=$Version -s -w"

# Array to track built binaries
$BuiltBinaries = @()
Expand Down Expand Up @@ -96,7 +96,7 @@ if (!$OnlyCurrentPlatform) {
if ($BuildTray) {
Write-Host ""
Write-Host "Building mcpproxy-tray binaries..." -ForegroundColor Magenta

# Build tray for current platform (Windows with CGO)
Write-Host "Building tray for Windows..." -ForegroundColor Cyan
$env:CGO_ENABLED = "1"
Expand All @@ -109,7 +109,7 @@ if ($BuildTray) {
Write-Host "✓ Windows tray binary built successfully" -ForegroundColor Green
$BuiltBinaries += "mcpproxy-tray.exe"
}

if (!$OnlyCurrentPlatform) {
# Build tray for macOS (requires macOS host for proper CGO compilation)
Write-Host "Attempting to build tray for macOS..." -ForegroundColor Cyan
Expand All @@ -123,7 +123,7 @@ if ($BuildTray) {
Write-Host "✓ macOS tray binary built successfully" -ForegroundColor Green
$BuiltBinaries += "mcpproxy-tray-darwin-amd64"
}

# Build tray for macOS ARM64
Write-Host "Attempting to build tray for macOS ARM64..." -ForegroundColor Cyan
$env:CGO_ENABLED = "1"
Expand All @@ -139,7 +139,7 @@ if ($BuildTray) {
} else {
Write-Host "Skipping cross-platform tray builds (OnlyCurrentPlatform flag is set)" -ForegroundColor Yellow
}

# Reset environment variables
Remove-Item Env:CGO_ENABLED -ErrorAction SilentlyContinue
Remove-Item Env:GOOS -ErrorAction SilentlyContinue
Expand Down Expand Up @@ -172,4 +172,3 @@ if ($BuiltFiles.Count -gt 0) {
Write-Host ""
Write-Host "Test version info:" -ForegroundColor Cyan
& .\mcpproxy.exe --version

Loading