Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement self-update for nali #177

Merged
merged 8 commits into from
Sep 28, 2023
Merged

feat: implement self-update for nali #177

merged 8 commits into from
Sep 28, 2023

Conversation

DateBro
Copy link
Contributor

@DateBro DateBro commented Sep 25, 2023

implemented self-update for nali, used creativeprojects/go-selfupdate.

locally tested, this is the log:

~/Documents/code_projects/nali/nali   go install
 ~/Documents/code_projects/nali/nali  nali 1.2.3.4                
1.2.3.4 [澳大利亚 APNIC Debogon-prefix网络] 
~/Documents/code_projects/nali/nali  nali --version
nali version unknown version
~/Documents/code_projects/nali/nali  nali update --version v0.7.2
2023/09/24 20:58:34 Decompressing gzip file
2023/09/24 20:58:34 Executable file "nali-darwin-arm64" was found in gzip file
2023/09/24 20:58:34 Will update /Users/zhaozhiyong/go/bin/nali to v0.7.2 downloaded from https://github.com/zu1k/nali/releases/download/v0.7.2/nali-darwin-arm64-v0.7.2.gz
2023/09/24 20:58:34 Successfully updated to version 0.7.2
~/Documents/code_projects/nali/nali  nali 1.2.3.4                
1.2.3.4 [澳大利亚 APNIC Debogon-prefix网络] 
 ~/Documents/code_projects/nali/nali  nali --version
nali version v0.7.2

issue: #171

cmd/update.go Outdated Show resolved Hide resolved
cmd/update.go Outdated Show resolved Hide resolved
internal/repo/decompress.go Outdated Show resolved Hide resolved
internal/repo/update.go Outdated Show resolved Hide resolved
internal/repo/update.go Outdated Show resolved Hide resolved
@DateBro DateBro force-pushed the master branch 2 times, most recently from b38f115 to cc570ef Compare September 25, 2023 04:35
@zu1k
Copy link
Owner

zu1k commented Sep 25, 2023

Our binary increased by 40% due to the introduction of a lot of non-essential code, which could have been avoided. We can no longer use go-selfupdate to do this, but need to implement this in nali itself.

The logic here is simple:

  1. Access https://api.github.com/repos/zu1k/nali/releases/latest
  2. Compare version numbers
  3. Filtering assets by GOOS and GOARCH
  4. Download the new version nali and its sha256 via the given browser_download_url
  5. Verifying files with sha256
  6. Unzip and replace nali itself

@DateBro DateBro force-pushed the master branch 3 times, most recently from d300edc to 8999671 Compare September 26, 2023 13:51
internal/repo/github.go Outdated Show resolved Hide resolved
internal/repo/update.go Outdated Show resolved Hide resolved
internal/repo/update.go Outdated Show resolved Hide resolved
cmd/update.go Outdated Show resolved Hide resolved
internal/repo/github.go Outdated Show resolved Hide resolved
func download(ctx context.Context, assetId int64) (data []byte, err error) {
var rc io.ReadCloser

rc, _, err = client.Repositories.DownloadReleaseAsset(ctx, constant.Owner, constant.Repo, assetId, http.DefaultClient)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use the default http client, which has no timeout control and does not use proxies.

See

func GetHttpClient() *HttpClient {

internal/repo/update.go Outdated Show resolved Hide resolved
internal/repo/update.go Outdated Show resolved Hide resolved
internal/repo/update.go Fixed Show fixed Hide fixed
internal/repo/update.go Outdated Show resolved Hide resolved
cmd/update.go Outdated Show resolved Hide resolved
internal/repo/decompress.go Outdated Show resolved Hide resolved
internal/repo/github.go Outdated Show resolved Hide resolved
Comment on lines 72 to 77
hash := fmt.Sprintf("%s", vData[:sha256.BlockSize])
calculatedHash := fmt.Sprintf("%x", sha256.Sum256(data))

if calculatedHash != hash {
return fmt.Errorf("expected %q, found %q: sha256 validation failed", hash, calculatedHash)
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of converting bytes to a string (and you're not dealing with case issues), decode the hexadecimal sha256 and then use
https://pkg.go.dev/bytes#Equal

internal/repo/update.go Outdated Show resolved Hide resolved
internal/repo/update.go Outdated Show resolved Hide resolved
internal/repo/update.go Fixed Show fixed Hide fixed
internal/repo/update.go Fixed Show fixed Hide fixed

return latest.GreaterThan(cur)
}
return true
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
return true
return false

Comment on lines 101 to 107
// get the directory the executable exists in
updateDir := filepath.Dir(cmdPath)
filename := filepath.Base(cmdPath)

// Copy the contents of new binary to a new executable file
newPath := filepath.Join(updateDir, fmt.Sprintf(".%s.new", filename))
fp, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is necessary to check the permissions of folders and files before updating, because some of our users install nali through package management, which results in no write permissions, in which case we give a friendly reminder to the user to inform about the latest version and skip the automatic update.

}

if _, err = io.Copy(fp, bytes.NewReader(newBytes)); err != nil {
fp.Close()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fp.Close()
_ = fp.Close()

fp, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)

if err != nil {
fp.Close()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fp.Close()
_ = fp.Close()

}
// if we don't call fp.Close(), windows won't let us move the new executable
// because the file will still be "in use"
fp.Close()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should handle the error here, although the probability of the error here is extremely low, we need to make sure that we don't cause file corruption here, and if an error occurs we need to cancel the update process

func canWriteFile(path string) bool {
f, err := os.OpenFile(path, os.O_WRONLY, 0644)
if err == nil {
defer f.Close()

Check warning

Code scanning / CodeQL

Writable file handle closed without error handling Warning

File handle may be writable as a result of data flow from a
call to OpenFile
and closing it may result in data loss upon failure, which is not handled explicitly.
@zu1k
Copy link
Owner

zu1k commented Sep 28, 2023

LGTM

@zu1k
Copy link
Owner

zu1k commented Sep 28, 2023

Thanks

@zu1k zu1k merged commit 43a3080 into zu1k:master Sep 28, 2023
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants