diff --git a/internal/consts/consts.go b/internal/consts/consts.go index c8043a0..0413d3f 100644 --- a/internal/consts/consts.go +++ b/internal/consts/consts.go @@ -2,6 +2,7 @@ package consts import ( "os" + "path/filepath" "strings" "github.com/gogf/gf/v2/errors/gcode" @@ -9,25 +10,47 @@ import ( ) // Version can be set at build time via -ldflags "-X github.com/qianfree/team-api/internal/consts.Version=x.y.z". -// Falls back to reading the VERSION file in the project root, or "dev" if unavailable. +// Falls back to reading the VERSION file, or "dev" if unavailable. var Version = "" func init() { if Version != "" { return } - data, err := os.ReadFile("VERSION") - if err != nil { - Version = "dev" - return - } - if v := strings.TrimSpace(string(data)); v != "" { + v := readVersionFile() + if v != "" { Version = v } else { Version = "dev" } } +// readVersionFile tries multiple locations to find the VERSION file: +// 1. Current working directory (development mode) +// 2. Same directory as the running binary (deployment mode) +func readVersionFile() string { + // Candidate paths to search + candidates := []string{"VERSION"} + + // Add binary's directory + if exe, err := os.Executable(); err == nil { + if exe, err = filepath.EvalSymlinks(exe); err == nil { + candidates = append(candidates, filepath.Join(filepath.Dir(exe), "VERSION")) + } + } + + for _, path := range candidates { + data, err := os.ReadFile(path) + if err != nil { + continue + } + if v := strings.TrimSpace(string(data)); v != "" { + return v + } + } + return "" +} + // Business error variables (using gerror for full stack traces) var ( ErrUnauthorized = gerror.NewCode(gcode.New(CodeUnauthorized, MsgUnauthorized, nil), MsgUnauthorized) diff --git a/internal/logic/update/checker.go b/internal/logic/update/checker.go index 6297150..c851088 100644 --- a/internal/logic/update/checker.go +++ b/internal/logic/update/checker.go @@ -33,17 +33,6 @@ func CheckForUpdate(ctx context.Context, force bool) (*CheckResult, error) { } } - // Dev builds skip update check - if consts.Version == "dev" { - return &CheckResult{ - CurrentVersion: consts.Version, - LatestVersion: consts.Version, - HasUpdate: false, - CheckedAt: gtime.Now(), - DeploymentMode: GetDeploymentMode(), - }, nil - } - // Call GitHub API release, etag, err := fetchLatestRelease(ctx) if err != nil { @@ -65,16 +54,23 @@ func CheckForUpdate(ctx context.Context, force bool) (*CheckResult, error) { }, nil } - // Parse versions - currentVer, err := semver.NewVersion(strings.TrimPrefix(consts.Version, "v")) - if err != nil { - return nil, fmt.Errorf("invalid current version %q: %w", consts.Version, err) - } - latestStr := strings.TrimPrefix(release.TagName, "v") - latestVer, err := semver.NewVersion(latestStr) - if err != nil { - return nil, fmt.Errorf("invalid latest version %q: %w", release.TagName, err) + + // Determine if update is available + var hasUpdate bool + if consts.Version == "dev" { + // Dev builds: always report an update if a remote release exists + hasUpdate = true + } else { + currentVer, err := semver.NewVersion(strings.TrimPrefix(consts.Version, "v")) + if err != nil { + return nil, fmt.Errorf("invalid current version %q: %w", consts.Version, err) + } + latestVer, err := semver.NewVersion(latestStr) + if err != nil { + return nil, fmt.Errorf("invalid latest version %q: %w", release.TagName, err) + } + hasUpdate = latestVer.GreaterThan(currentVer) } // Find matching asset for current platform @@ -95,7 +91,7 @@ func CheckForUpdate(ctx context.Context, force bool) (*CheckResult, error) { result := &CheckResult{ CurrentVersion: consts.Version, LatestVersion: latestStr, - HasUpdate: latestVer.GreaterThan(currentVer), + HasUpdate: hasUpdate, ReleaseNotes: release.Body, ReleaseURL: release.HTMLURL, PublishedAt: release.PublishedAt,