From f2bec9468105cdbf76847d5ff9d12d424427b76f Mon Sep 17 00:00:00 2001 From: "Zhiyong(Johnny) Zhao" Date: Thu, 28 Sep 2023 16:09:40 -0400 Subject: [PATCH 1/3] fix: fix self-update bugs 1. added -v for update command, only update binary when -v is assigned; 2. removed the reference to semver library, implemented repo version compare functions; 3. edited release-aur-git rule in go.yml, won't replace the hyphen to dot in release version; --- .github/workflows/go.yml | 2 +- cmd/update.go | 16 +++++++---- go.mod | 1 - go.sum | 2 -- internal/repo/update.go | 25 +++++++++++----- internal/repo/version.go | 62 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 internal/repo/version.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 624b7cf..0c442c4 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -38,7 +38,7 @@ jobs: - name: Get version id: version - run: echo ::set-output name=version::$(git describe --long --tags | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g') + run: echo ::set-output name=version::$(git describe --long --tags | sed 's/^v//;s/\([^-]*-g\)/r\1/;') - name: Publish AUR package nali-go-git uses: zu1k/aur-publish-action@master diff --git a/cmd/update.go b/cmd/update.go index b0811db..9bd0532 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -12,15 +12,18 @@ import ( // updateCmd represents the update command var updateCmd = &cobra.Command{ - Use: "update [--db dbs]", - Short: "update qqwry, zxipv6wry, ip2region ip database and cdn", - Long: `update qqwry, zxipv6wry, ip2region ip database and cdn. Use commas to separate`, - Example: "nali update --db qqwry,cdn", + Use: "update [--db dbs -v]", + Short: "update qqwry, zxipv6wry, ip2region ip database and cdn, update nali to latest version if -v", + Long: `update qqwry, zxipv6wry, ip2region ip database and cdn. Use commas to separate. update nali to latest version if -v`, + Example: "nali update --db qqwry,cdn -v", Run: func(cmd *cobra.Command, args []string) { DBs, _ := cmd.Flags().GetString("db") - if err := repo.UpdateRepo(); err != nil { - log.Printf("update nali to latest version failed: %v \n", err) + version, _ := cmd.Flags().GetBool("v") + if version { + if err := repo.UpdateRepo(); err != nil { + log.Printf("update nali to latest version failed: %v \n", err) + } } var DBNameArray []string @@ -33,5 +36,6 @@ var updateCmd = &cobra.Command{ func init() { updateCmd.PersistentFlags().String("db", "", "choose db you want to update") + updateCmd.PersistentFlags().Bool("v", false, "decide whether to update the nali version") rootCmd.AddCommand(updateCmd) } diff --git a/go.mod b/go.mod index 6ba0c06..d4c55a7 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/zu1k/nali go 1.19 require ( - github.com/Masterminds/semver/v3 v3.2.1 github.com/adrg/xdg v0.4.0 github.com/fatih/color v1.15.0 github.com/google/go-github/v55 v55.0.0 diff --git a/go.sum b/go.sum index c334f06..1b6614f 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,6 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= diff --git a/internal/repo/update.go b/internal/repo/update.go index e71c71a..e14e1f3 100644 --- a/internal/repo/update.go +++ b/internal/repo/update.go @@ -13,7 +13,6 @@ import ( "github.com/zu1k/nali/internal/constant" - "github.com/Masterminds/semver/v3" "github.com/google/go-github/v55/github" ) @@ -153,13 +152,25 @@ func update(asset io.Reader, cmdPath string) error { } func canUpdate(rel *github.RepositoryRelease) bool { - if constant.Version != "unknown version" { - latest, _ := semver.NewVersion(rel.GetTagName()) - cur, _ := semver.NewVersion(constant.Version) - - return latest.GreaterThan(cur) + // unknown version means that the user compiled it manually instead of downloading it from the release, + // in which case we don't take the liberty of updating it to a potentially older version. + if constant.Version == "unknown version" { + return false } - return false + + latest, err := parseVersion(rel.GetTagName()) + if err != nil { + log.Printf("failed to parse latest version: %v, err: %v \n", rel.GetTagName(), err) + return false + } + + cur, err := parseVersion(constant.Version) + if err != nil { + log.Printf("failed to parse current version: %v, err: %v \n", constant.Version, err) + return false + } + + return latest.GreaterThan(cur) } func canWriteDir(path string) bool { diff --git a/internal/repo/version.go b/internal/repo/version.go new file mode 100644 index 0000000..8c65bc4 --- /dev/null +++ b/internal/repo/version.go @@ -0,0 +1,62 @@ +package repo + +import ( + "fmt" + "strconv" + "strings" +) + +type Version struct { + Major int + Minor int + Patch int +} + +func parseVersion(vStr string) (*Version, error) { + vStr = strings.TrimPrefix(vStr, "v") + + // split by hyphen to remove pre-release info, then split by dot to get version info + parts := strings.Split(strings.Split(vStr, "-")[0], ".") + if len(parts) < 3 { + return nil, fmt.Errorf("invalid version format: %s", vStr) + } + + major, err := strconv.Atoi(parts[0]) + if err != nil { + return nil, err + } + + minor, err := strconv.Atoi(parts[1]) + if err != nil { + return nil, err + } + + patch, err := strconv.Atoi(parts[2]) + if err != nil { + return nil, err + } + + return &Version{Major: major, Minor: minor, Patch: patch}, nil +} + +func (v *Version) Equal(other *Version) bool { + return v.compare(other) == 0 +} + +func (v *Version) GreaterThan(other *Version) bool { + return v.compare(other) > 0 +} + +func (v *Version) LessThan(other *Version) bool { + return v.compare(other) < 0 +} + +func (v *Version) compare(other *Version) int { + if v.Major != other.Major { + return v.Major - other.Major + } + if v.Minor != other.Minor { + return v.Minor - other.Minor + } + return v.Patch - other.Patch +} From 4123411ecbeff9622326cdf324de7dff2e8328fc Mon Sep 17 00:00:00 2001 From: "Zhiyong(Johnny) Zhao" Date: Fri, 29 Sep 2023 13:01:48 -0400 Subject: [PATCH 2/3] fix: delete write permission validation for files when self-update --- internal/repo/update.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/internal/repo/update.go b/internal/repo/update.go index e14e1f3..2c0fcd6 100644 --- a/internal/repo/update.go +++ b/internal/repo/update.go @@ -95,9 +95,6 @@ func update(asset io.Reader, cmdPath string) error { if !canWriteDir(updateDir) { return fmt.Errorf("no write permissions on the directory, consider updating nali manually") } - if !canWriteFile(cmdPath) { - return fmt.Errorf("no write permissions on the executable, consider updating nali manually") - } // Copy the contents of new binary to a new executable file newPath := filepath.Join(updateDir, fmt.Sprintf(".%s.new", filename)) @@ -184,12 +181,3 @@ func canWriteDir(path string) bool { return err == nil } - -func canWriteFile(path string) bool { - f, err := os.OpenFile(path, os.O_WRONLY, 0644) - if err == nil { - defer f.Close() - } - - return err == nil -} From 44522726f8907089e73f19081b0c181a7a3358ef Mon Sep 17 00:00:00 2001 From: "Zhiyong(Johnny) Zhao" Date: Mon, 2 Oct 2023 13:30:57 -0400 Subject: [PATCH 3/3] test: added test for version.go --- internal/repo/version_test.go | 111 ++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 internal/repo/version_test.go diff --git a/internal/repo/version_test.go b/internal/repo/version_test.go new file mode 100644 index 0000000..7d50191 --- /dev/null +++ b/internal/repo/version_test.go @@ -0,0 +1,111 @@ +package repo + +import ( + "reflect" + "testing" +) + +func Test_parseVersion(t *testing.T) { + tests := []struct { + name string + vStr string + want *Version + wantErr bool + }{ + { + name: "happy path for normal release version name", + vStr: "v0.7.1", + want: &Version{Major: 0, Minor: 7, Patch: 1}, + wantErr: false, + }, + { + name: "happy path for old aur-git release version name", + vStr: "0.7.3.r15.g43a3080", + want: &Version{Major: 0, Minor: 7, Patch: 3}, + wantErr: false, + }, + { + name: "happy path for new aur-git release version name", + vStr: "0.7.3-r15-g43a3080", + want: &Version{Major: 0, Minor: 7, Patch: 3}, + wantErr: false, + }, + { + name: "empty version name", + vStr: "", + want: nil, + wantErr: true, + }, + { + name: "only major and minor version", + vStr: "0.7", + want: nil, + wantErr: true, + }, + { + name: "invalid format version name, major/minor/patch version is not a number", + vStr: "xx.xx.xx", + want: nil, + wantErr: true, + }, + { + name: "user customized version name", + vStr: "test version", + want: nil, + wantErr: true, + }, + { + name: "only dots, no version number", + vStr: "...", + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseVersion(tt.vStr) + if (err != nil) != tt.wantErr { + t.Errorf("parseVersion() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("parseVersion() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestVersion_GreaterThan(t *testing.T) { + tests := []struct { + name string + v *Version + other *Version + want bool + }{ + { + name: "happy path", + v: &Version{Major: 0, Minor: 7, Patch: 1}, + other: &Version{Major: 0, Minor: 7, Patch: 0}, + want: true, + }, + { + name: "same version number", + v: &Version{Major: 0, Minor: 7, Patch: 1}, + other: &Version{Major: 0, Minor: 7, Patch: 1}, + want: false, + }, + { + name: "less than other version number", + v: &Version{Major: 0, Minor: 7, Patch: 1}, + other: &Version{Major: 0, Minor: 7, Patch: 3}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.v.GreaterThan(tt.other); got != tt.want { + t.Errorf("GreaterThan() = %v, want %v", got, tt.want) + } + }) + } +}