Files
GoTunnel/pkg/version/version.go
Flik 0a932211f1
Some checks failed
Build Multi-Platform Binaries / build-android-apk (push) Failing after 6s
Build Multi-Platform Binaries / build-frontend (push) Successful in 33s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 1m37s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m46s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 1m28s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 2m0s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 1m21s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 1m52s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 2m2s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 2m18s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 1m21s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 1m58s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m33s
fix: Update references from Gitea to GitHub for versioning and self-update
2026-03-22 22:37:27 +08:00

250 lines
5.6 KiB
Go

package version
import (
"encoding/json"
"fmt"
"io"
"net/http"
"runtime"
"strconv"
"strings"
"time"
)
var Version = "1.0.0"
var GitCommit = ""
var BuildTime = ""
const (
RepoURL = "https://github.com/Flikify/Gotunnel"
APIBaseURL = "https://api.github.com"
RepoOwner = "Flikify"
RepoName = "Gotunnel"
GitHubAPIVersion = "2022-11-28"
GitHubUserAgent = "GoTunnel-Updater"
)
type Info struct {
Version string `json:"version"`
GitCommit string `json:"git_commit"`
BuildTime string `json:"build_time"`
GoVersion string `json:"go_version"`
OS string `json:"os"`
Arch string `json:"arch"`
}
type ReleaseInfo struct {
TagName string `json:"tag_name"`
Name string `json:"name"`
Body string `json:"body"`
PublishedAt string `json:"published_at"`
Assets []ReleaseAsset `json:"assets"`
}
type ReleaseAsset struct {
Name string `json:"name"`
Size int64 `json:"size"`
BrowserDownloadURL string `json:"browser_download_url"`
}
type UpdateInfo struct {
Latest string `json:"latest"`
ReleaseNote string `json:"release_note"`
DownloadURL string `json:"download_url"`
AssetName string `json:"asset_name"`
AssetSize int64 `json:"asset_size"`
}
func SetVersion(v string) {
if v != "" {
Version = v
}
}
func SetBuildInfo(gitCommit, buildTime string) {
if gitCommit != "" {
GitCommit = gitCommit
}
if buildTime != "" {
BuildTime = buildTime
}
}
func GetInfo() Info {
return Info{
Version: Version,
GitCommit: GitCommit,
BuildTime: BuildTime,
GoVersion: runtime.Version(),
OS: runtime.GOOS,
Arch: runtime.GOARCH,
}
}
func newGitHubRequest(url string) (*http.Request, error) {
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("X-GitHub-Api-Version", GitHubAPIVersion)
req.Header.Set("User-Agent", GitHubUserAgent)
return req, nil
}
func GetLatestRelease() (*ReleaseInfo, error) {
client := &http.Client{Timeout: 30 * time.Second}
latestURL := fmt.Sprintf("%s/repos/%s/%s/releases/latest", APIBaseURL, RepoOwner, RepoName)
req, err := newGitHubRequest(latestURL)
if err != nil {
return nil, fmt.Errorf("build request: %w", err)
}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
var release ReleaseInfo
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
return nil, fmt.Errorf("decode response: %w", err)
}
return &release, nil
}
resp.Body.Close()
listURL := fmt.Sprintf("%s/repos/%s/%s/releases?per_page=1", APIBaseURL, RepoOwner, RepoName)
req, err = newGitHubRequest(listURL)
if err != nil {
return nil, fmt.Errorf("build request: %w", err)
}
resp, err = client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("API error: %s - %s", resp.Status, string(body))
}
var releases []ReleaseInfo
if err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {
return nil, fmt.Errorf("decode response: %w", err)
}
if len(releases) == 0 {
return nil, fmt.Errorf("no releases found in repository")
}
return &releases[0], nil
}
func CheckUpdate(component string) (*UpdateInfo, error) {
release, err := GetLatestRelease()
if err != nil {
return nil, fmt.Errorf("get latest release: %w", err)
}
var downloadURL string
var assetName string
var assetSize int64
if asset := findAssetForPlatform(release.Assets, component, runtime.GOOS, runtime.GOARCH); asset != nil {
downloadURL = asset.BrowserDownloadURL
assetName = asset.Name
assetSize = asset.Size
}
return &UpdateInfo{
Latest: release.TagName,
ReleaseNote: release.Body,
DownloadURL: downloadURL,
AssetName: assetName,
AssetSize: assetSize,
}, nil
}
func CheckUpdateForPlatform(component, osName, arch string) (*UpdateInfo, error) {
release, err := GetLatestRelease()
if err != nil {
return nil, fmt.Errorf("get latest release: %w", err)
}
var downloadURL string
var assetName string
var assetSize int64
if asset := findAssetForPlatform(release.Assets, component, osName, arch); asset != nil {
downloadURL = asset.BrowserDownloadURL
assetName = asset.Name
assetSize = asset.Size
}
return &UpdateInfo{
Latest: release.TagName,
ReleaseNote: release.Body,
DownloadURL: downloadURL,
AssetName: assetName,
AssetSize: assetSize,
}, nil
}
func findAssetForPlatform(assets []ReleaseAsset, component, osName, arch string) *ReleaseAsset {
prefix := fmt.Sprintf("gotunnel-%s-", component)
suffix := fmt.Sprintf("-%s-%s", osName, arch)
for i := range assets {
name := assets[i].Name
if strings.HasPrefix(name, prefix) && strings.Contains(name, suffix) {
return &assets[i]
}
}
return nil
}
func CompareVersions(v1, v2 string) int {
parts1 := parseVersionParts(v1)
parts2 := parseVersionParts(v2)
maxLen := len(parts1)
if len(parts2) > maxLen {
maxLen = len(parts2)
}
for i := 0; i < maxLen; i++ {
var p1, p2 int
if i < len(parts1) {
p1 = parts1[i]
}
if i < len(parts2) {
p2 = parts2[i]
}
if p1 < p2 {
return -1
}
if p1 > p2 {
return 1
}
}
return 0
}
func parseVersionParts(v string) []int {
v = strings.TrimPrefix(v, "v")
parts := strings.Split(v, ".")
result := make([]int, len(parts))
for i, p := range parts {
n, _ := strconv.Atoi(p)
result[i] = n
}
return result
}