package mining import ( "encoding/json" "io" "net/http" "strconv" ) var ( version = "dev" commit = "none" date = "unknown" ) // versionString := mining.GetVersion() // "1.2.3" or "dev" when unset func GetVersion() string { return version } // commitHash := mining.GetCommit() // "a1b2c3d" or "none" when unset func GetCommit() string { return commit } // buildDate := mining.GetBuildDate() // "2026-04-02T00:00:00Z" or "unknown" when unset func GetBuildDate() string { return date } // release := &GitHubRelease{TagName: "v6.21.0", Name: "XMRig v6.21.0"} type GitHubRelease struct { TagName string `json:"tag_name"` Name string `json:"name"` } // tag, err := FetchLatestGitHubVersion("xmrig", "xmrig") // "v6.21.0" func FetchLatestGitHubVersion(owner, repo string) (string, error) { circuitBreaker := getGitHubCircuitBreaker() result, err := circuitBreaker.Execute(func() (interface{}, error) { return fetchGitHubVersionDirect(owner, repo) }) if err != nil { // If circuit is open, try to return cached value with warning if err == ErrCircuitOpen { if cached, ok := circuitBreaker.GetCached(); ok { if tagName, ok := cached.(string); ok { return tagName, nil } } return "", ErrInternal("github API unavailable (circuit breaker open)").WithCause(err) } return "", err } tagName, ok := result.(string) if !ok { return "", ErrInternal("unexpected result type from circuit breaker") } return tagName, nil } // tag, err := fetchGitHubVersionDirect("xmrig", "xmrig") // "v6.21.0"; called via circuit breaker func fetchGitHubVersionDirect(owner, repo string) (string, error) { url := "https://api.github.com/repos/" + owner + "/" + repo + "/releases/latest" resp, err := getHTTPClient().Get(url) if err != nil { return "", ErrInternal("failed to fetch version").WithCause(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { io.Copy(io.Discard, resp.Body) // Drain body to allow connection reuse return "", ErrInternal("failed to get latest release: unexpected status code " + strconv.Itoa(resp.StatusCode)) } var release GitHubRelease if err := json.NewDecoder(resp.Body).Decode(&release); err != nil { return "", ErrInternal("failed to decode release").WithCause(err) } return release.TagName, nil }