Skip to content

Commit

Permalink
Merge pull request nezhahq#3 from uubulb/gitee-support
Browse files Browse the repository at this point in the history
  • Loading branch information
naiba authored Jul 13, 2024
2 parents 9d84a13 + 249af3d commit d560a87
Show file tree
Hide file tree
Showing 6 changed files with 403 additions and 33 deletions.
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/nezhahq/go-github-selfupdate
go 1.19

require (
gitee.com/naibahq/go-gitee v0.0.0-20240713052758-bc992e4c5b2c
github.com/blang/semver v3.5.1+incompatible
github.com/google/go-github v17.0.0+incompatible
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf
Expand All @@ -12,10 +13,11 @@ require (
)

require (
github.com/antihax/optional v1.0.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/onsi/gomega v1.18.1 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/net v0.21.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)
14 changes: 12 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
gitee.com/naibahq/go-gitee v0.0.0-20240713052758-bc992e4c5b2c h1:rFMPP1jR4CIOcxU2MHTFHKtXLPqV+qD4VPjRnpA6Inw=
gitee.com/naibahq/go-gitee v0.0.0-20240713052758-bc992e4c5b2c/go.mod h1:9gFPAuMAO9HJv5W73eoLV1NX71Ko5MhzGe+NwOJkm24=
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
Expand Down Expand Up @@ -58,18 +63,22 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -98,6 +107,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
Expand Down
200 changes: 200 additions & 0 deletions selfupdate/detect_gitee.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package selfupdate

import (
"context"
"fmt"
"regexp"
"runtime"
"strings"

"gitee.com/naibahq/go-gitee/gitee"
"github.com/blang/semver"
)

func findAssetFromReleaseGitee(rel gitee.Release,
suffixes []string, targetVersion string, filters []*regexp.Regexp) (*gitee.Asset, semver.Version, bool) {

if targetVersion != "" && targetVersion != rel.TagName {
log.Println("Skip", rel.TagName, "not matching to specified version", targetVersion)
return nil, semver.Version{}, false
}

if targetVersion == "" && rel.Prerelease {
log.Println("Skip pre-release version", rel.TagName)
return nil, semver.Version{}, false
}

verText := rel.TagName
indices := reVersion.FindStringIndex(verText)
if indices == nil {
log.Println("Skip version not adopting semver", verText)
return nil, semver.Version{}, false
}
if indices[0] > 0 {
log.Println("Strip prefix of version", verText[:indices[0]], "from", verText)
verText = verText[indices[0]:]
}

// If semver cannot parse the version text, it means that the text is not adopting
// the semantic versioning. So it should be skipped.
ver, err := semver.Make(verText)
if err != nil {
log.Println("Failed to parse a semantic version", verText)
return nil, semver.Version{}, false
}

for _, asset := range rel.Assets {
name := asset.Name
if len(filters) > 0 {
// if some filters are defined, match them: if any one matches, the asset is selected
matched := false
for _, filter := range filters {
if filter.MatchString(name) {
log.Println("Selected filtered asset", name)
matched = true
break
}
log.Printf("Skipping asset %q not matching filter %v\n", name, filter)
}
if !matched {
continue
}
}

for _, s := range suffixes {
if strings.HasSuffix(name, s) { // require version, arch etc
// default: assume single artifact
return &asset, ver, true
}
}
}

log.Println("No suitable asset was found in release", rel.TagName)
return nil, semver.Version{}, false
}

func findValidationAssetGitee(rel *gitee.Release, validationName string) (*gitee.Asset, bool) {
for _, asset := range rel.Assets {
if asset.Name == validationName {
return &asset, true
}
}
return nil, false
}

func findReleaseAndAssetGitee(rels []gitee.Release,
targetVersion string,
filters []*regexp.Regexp) (*gitee.Release, *gitee.Asset, semver.Version, bool) {
// Generate candidates
suffixes := make([]string, 0, 2*7*2)
for _, sep := range []rune{'_', '-'} {
for _, ext := range []string{".zip", ".tar.gz", ".tgz", ".gzip", ".gz", ".tar.xz", ".xz", ""} {
suffix := fmt.Sprintf("%s%c%s%s", runtime.GOOS, sep, runtime.GOARCH, ext)
suffixes = append(suffixes, suffix)
if runtime.GOOS == "windows" {
suffix = fmt.Sprintf("%s%c%s.exe%s", runtime.GOOS, sep, runtime.GOARCH, ext)
suffixes = append(suffixes, suffix)
}
}
}

var ver semver.Version
var asset *gitee.Asset
var release *gitee.Release

// Find the latest version from the list of releases.
// Returned list from GitHub API is in the order of the date when created.
// ref: https://github.com/nezhahq/go-github-selfupdate/issues/11
for _, rel := range rels {
if a, v, ok := findAssetFromReleaseGitee(rel, suffixes, targetVersion, filters); ok {
// Note: any version with suffix is less than any version without suffix.
// e.g. 0.0.1 > 0.0.1-beta
if release == nil || v.GTE(ver) {
ver = v
asset = a
release = &rel
}
}
}

if release == nil {
log.Println("Could not find any release for", runtime.GOOS, "and", runtime.GOARCH)
return nil, nil, semver.Version{}, false
}

return release, asset, ver, true
}

// DetectLatest tries to get the latest version of the repository on GitHub. 'slug' means 'owner/name' formatted string.
// It fetches releases information from GitHub API and find out the latest release with matching the tag names and asset names.
// Drafts and pre-releases are ignored. Assets would be suffixed by the OS name and the arch name such as 'foo_linux_amd64'
// where 'foo' is a command name. '-' can also be used as a separator. File can be compressed with zip, gzip, zxip, tar&zip or tar&zxip.
// So the asset can have a file extension for the corresponding compression format such as '.zip'.
// On Windows, '.exe' also can be contained such as 'foo_windows_amd64.exe.zip'.
func (up *GiteeUpdater) DetectLatest(slug string) (release *Release, found bool, err error) {
return up.DetectVersion(slug, "")
}

// DetectVersion tries to get the given version of the repository on Github. `slug` means `owner/name` formatted string.
// And version indicates the required version.
func (up *GiteeUpdater) DetectVersion(slug string, version string) (release *Release, found bool, err error) {
repo := strings.Split(slug, "/")
if len(repo) != 2 || repo[0] == "" || repo[1] == "" {
return nil, false, fmt.Errorf("Invalid slug format. It should be 'owner/name': %s", slug)
}

rels, res, err := up.api.RepositoriesApi.GetV5ReposOwnerRepoReleases(context.Background(), repo[0], repo[1], &gitee.GetV5ReposOwnerRepoReleasesOpts{})
if err != nil {
log.Println("API returned an error response:", err)
if res != nil && res.StatusCode == 404 {
// 404 means repository not found or release not found. It's not an error here.
err = nil
log.Println("API returned 404. Repository or release not found")
}
return nil, false, err
}

rel, asset, ver, found := findReleaseAndAssetGitee(rels, version, up.filters)
if !found {
return nil, false, nil
}

url := asset.BrowserDownloadUrl
log.Println("Successfully fetched the latest release. tag:", rel.TagName, ", name:", rel.Name, ", Asset:", url)

publishedAt := rel.CreatedAt
release = &Release{
ver,
url,
-1,
-1,
-1,
"",
rel.Body,
rel.Name,
&publishedAt,
repo[0],
repo[1],
}

if up.validator != nil {
validationName := asset.Name + up.validator.Suffix()
_, ok := findValidationAssetGitee(rel, validationName)
if !ok {
return nil, false, fmt.Errorf("Failed finding validation file %q", validationName)
}
}

return release, true, nil
}

// DetectLatestGitee detects the latest release of the slug (owner/repo).
// This function is a shortcut version of updater.DetectLatest() method.
func DetectLatestGitee(slug string) (*Release, bool, error) {
return DefaultGiteeUpdater().DetectLatest(slug)
}

// DetectVersionGitee detects the given release of the slug (owner/repo) from its version.
func DetectVersionGitee(slug string, version string) (*Release, bool, error) {
return DefaultGiteeUpdater().DetectVersion(slug, version)
}
30 changes: 0 additions & 30 deletions selfupdate/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,40 +51,10 @@ func (up *Updater) downloadDirectlyFromURL(assetURL string) (io.ReadCloser, erro
return res.Body, nil
}

func useGitHubMirrorIfIpInChina(rel *Release) {
resp, err := http.Get("https://api.myip.la/json")
if err != nil {
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return
}
if !strings.Contains("China", string(body)) {
return
}
resp, err = http.Get("https://dn-dao-github-mirror.daocloud.io")
if err != nil {
return
}
defer resp.Body.Close()
body, err = io.ReadAll(resp.Body)
if err != nil {
return
}
if !strings.Contains("GitHub", string(body)) {
return
}
log.Println("China IP detected, using kkgithub mirror")
rel.AssetURL = strings.Replace(rel.AssetURL, "github.com", "dn-dao-github-mirror.daocloud.io", 1)
}

// UpdateTo downloads an executable from GitHub Releases API and replace current binary with the downloaded one.
// It downloads a release asset via GitHub Releases API so this function is available for update releases on private repository.
// If a redirect occurs, it fallbacks into directly downloading from the redirect URL.
func (up *Updater) UpdateTo(rel *Release, cmdPath string) error {
// useGitHubMirrorIfIpInChina(rel)
src, err := up.downloadDirectlyFromURL(rel.AssetURL)
if err != nil {
return err
Expand Down
Loading

0 comments on commit d560a87

Please sign in to comment.