Skip to content

Commit f94e74e

Browse files
committed
add binary file name in new release option
1 parent eb6d9af commit f94e74e

7 files changed

Lines changed: 116 additions & 85 deletions

File tree

README.md

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,16 @@ func update(version string) error {
8585
return nil
8686
}
8787

88-
exe, err := selfupdate.ExecutablePath()
88+
cmdPath, err := selfupdate.ExecutablePath()
8989
if err != nil {
90-
return errors.New("could not locate executable path")
90+
return fmt.Errorf("could not locate executable path: %w", err)
9191
}
92-
if err := selfupdate.UpdateTo(context.Background(), latest.AssetURL, latest.AssetName, exe); err != nil {
92+
93+
// In the new release, the binary file can have any name — this is set by the relExe.
94+
// If it has the same name as the old one, you can get it like this:
95+
_, relExe := filepath.Split(cmdPath)
96+
97+
if err := selfupdate.UpdateTo(context.Background(), latest.AssetURL, relExe, cmdPath); err != nil {
9398
return fmt.Errorf("error occurred while updating binary: %w", err)
9499
}
95100
log.Printf("Successfully updated to version %s", latest.Version())
@@ -346,7 +351,7 @@ See [goreleaser documentation](https://goreleaser.com/scm/gitlab/#generic-packag
346351
## Example:
347352

348353
```go
349-
func update() {
354+
func update() error {
350355
source, err := selfupdate.NewGitLabSource(selfupdate.GitLabConfig{
351356
BaseURL: "https://private.instance.on.gitlab.com/",
352357
})
@@ -365,19 +370,25 @@ func update() {
365370
log.Fatal(err)
366371
}
367372
if !found {
368-
log.Print("Release not found")
369-
return
373+
return errors.New("release not found")
370374
}
371375
fmt.Printf("found release %s\n", release.Version())
372376

373-
exe, err := selfupdate.ExecutablePath()
377+
cmdPath, err := selfupdate.ExecutablePath()
374378
if err != nil {
375379
return errors.New("could not locate executable path")
376380
}
377-
err = updater.UpdateTo(context.Background(), release, exe)
381+
382+
// In the new release, the binary file can have any name — this is set by the relExe.
383+
// If it has the same name as the old one, you can get it like this:
384+
_, relExe := filepath.Split(cmdPath)
385+
386+
err = updater.UpdateTo(context.Background(), release, relExe, cmdPath)
378387
if err != nil {
379388
log.Fatal(err)
380389
}
390+
391+
return nil
381392
}
382393
```
383394

@@ -392,7 +403,7 @@ The HttpSource is designed to work with repositories built using [goreleaser-htt
392403
If your repository is at example.com/repo/project, then you'd use the following example.
393404

394405
```go
395-
func update() {
406+
func update() error {
396407
source, err := selfupdate.NewHttpSource(selfupdate.HttpConfig{
397408
BaseURL: "https://example.com/",
398409
})
@@ -411,19 +422,25 @@ func update() {
411422
log.Fatal(err)
412423
}
413424
if !found {
414-
log.Print("Release not found")
415-
return
425+
return errors.New("release not found")
416426
}
417427
fmt.Printf("found release %s\n", release.Version())
418428

419-
exe, err := selfupdate.ExecutablePath()
429+
cmdPath, err := selfupdate.ExecutablePath()
420430
if err != nil {
421431
return errors.New("could not locate executable path")
422432
}
423-
err = updater.UpdateTo(context.Background(), release, exe)
433+
434+
// In the new release, the binary file can have any name — this is set by the relExe.
435+
// If it has the same name as the old one, you can get it like this:
436+
_, relExe := filepath.Split(cmdPath)
437+
438+
err = updater.UpdateTo(context.Background(), release, relExe, cmdPath)
424439
if err != nil {
425440
log.Fatal(err)
426441
}
442+
443+
return nil
427444
}
428445
```
429446

cmd/detect-latest-release/update.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"log"
7+
"path/filepath"
78
"runtime"
89

910
"github.com/creativeprojects/go-selfupdate"
@@ -26,11 +27,16 @@ func update(version string) error {
2627
return nil
2728
}
2829

29-
exe, err := selfupdate.ExecutablePath()
30+
cmdPath, err := selfupdate.ExecutablePath()
3031
if err != nil {
3132
return fmt.Errorf("could not locate executable path: %w", err)
3233
}
33-
if err := selfupdate.UpdateTo(context.Background(), latest.AssetURL, latest.AssetName, exe); err != nil {
34+
35+
// In the new release, the binary file can have any name — this is set by the relExe.
36+
// If it has the same name as the old one, you can get it like this:
37+
_, relExe := filepath.Split(cmdPath)
38+
39+
if err := selfupdate.UpdateTo(context.Background(), latest.AssetURL, relExe, cmdPath); err != nil {
3440
return fmt.Errorf("error occurred while updating binary: %w", err)
3541
}
3642
log.Printf("Successfully updated to version %s", latest.Version())

cmd/get-release/main.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func main() {
2828
flag.Usage = usage
2929
flag.Parse()
3030

31-
if help || flag.NArg() != 1 {
31+
if help || flag.NArg() != 2 {
3232
usage()
3333
return
3434
}
@@ -38,6 +38,7 @@ func main() {
3838
}
3939

4040
repo := flag.Arg(0)
41+
relExe := flag.Arg(1)
4142

4243
domain, slug, err := cmd.SplitDomainSlug(repo)
4344
if err != nil {
@@ -77,16 +78,16 @@ func main() {
7778
os.Exit(1)
7879
}
7980

80-
cmd := getCommand(flag.Arg(0))
81+
cmd := getCommand(repo)
8182
cmdPath := filepath.Join(build.Default.GOPATH, "bin", cmd)
8283
if _, err := os.Stat(cmdPath); err != nil {
8384
// When executable is not existing yet
84-
if err := installFrom(ctx, latest.AssetURL, cmd, cmdPath); err != nil {
85+
if err := installFrom(ctx, latest.AssetURL, relExe, cmdPath); err != nil {
8586
fmt.Fprintf(os.Stderr, "Error while installing the release binary from %s: %s\n", latest.AssetURL, err)
8687
os.Exit(1)
8788
}
8889
} else {
89-
if err := updater.UpdateTo(ctx, latest, cmdPath); err != nil {
90+
if err := updater.UpdateTo(ctx, latest, relExe, cmdPath); err != nil {
9091
fmt.Fprintf(os.Stderr, "Error while replacing the binary with %s: %s\n", latest.AssetURL, err)
9192
os.Exit(1)
9293
}
@@ -101,10 +102,11 @@ Release Notes:
101102

102103
func usage() {
103104
fmt.Fprintln(os.Stderr, `
104-
Usage: get-release [flags] {package}
105+
Usage: get-release [flags] {package} {relExe}
105106
106107
get-release is like "go get github.com/owner/repo@latest".
107108
{package} is using the same format: "github.com/owner/repo".
109+
{relExe} is the name of the new executable file (usually inside a release archive).
108110
109111
Flags:`)
110112
flag.PrintDefaults()
@@ -116,7 +118,7 @@ func getCommand(pkg string) string {
116118
return cmd
117119
}
118120

119-
func installFrom(ctx context.Context, url, cmd, path string) error {
121+
func installFrom(ctx context.Context, url, relExe, path string) error {
120122
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
121123
if err != nil {
122124
return fmt.Errorf("failed to create request to download release binary from %s: %s", url, err)
@@ -129,7 +131,7 @@ func installFrom(ctx context.Context, url, cmd, path string) error {
129131
if res.StatusCode != http.StatusOK {
130132
return fmt.Errorf("failed to download release binary from %s: Invalid response ", url)
131133
}
132-
executable, err := selfupdate.DecompressCommand(res.Body, url, cmd, runtime.GOOS, runtime.GOARCH)
134+
executable, err := selfupdate.DecompressCommand(res.Body, url, relExe, runtime.GOOS, runtime.GOARCH)
133135
if err != nil {
134136
return fmt.Errorf("failed to decompress downloaded asset from %s: %s", url, err)
135137
}

decompress.go

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
var (
2020
fileTypes = []struct {
2121
ext string
22-
decompress func(src io.Reader, cmd, os, arch string) (io.Reader, error)
22+
decompress func(src io.Reader, relExe, os, arch string) (io.Reader, error)
2323
}{
2424
{".zip", unzip},
2525
{".tar.gz", untar},
@@ -37,23 +37,23 @@ var (
3737
// DecompressCommand decompresses the given source. Archive and compression format is
3838
// automatically detected from 'url' parameter, which represents the URL of asset,
3939
// or simply a filename (with an extension).
40-
// This returns a reader for the decompressed command given by 'cmd'. '.zip',
40+
// This returns a reader for the decompressed command given by 'url'. '.zip',
4141
// '.tar.gz', '.tar.xz', '.tgz', '.gz', '.bz2' and '.xz' are supported.
4242
//
4343
// These wrapped errors can be returned:
4444
// - ErrCannotDecompressFile
4545
// - ErrExecutableNotFoundInArchive
46-
func DecompressCommand(src io.Reader, url, cmd, os, arch string) (io.Reader, error) {
46+
func DecompressCommand(src io.Reader, url, relExe, os, arch string) (io.Reader, error) {
4747
for _, fileType := range fileTypes {
4848
if strings.HasSuffix(url, fileType.ext) {
49-
return fileType.decompress(src, cmd, os, arch)
49+
return fileType.decompress(src, relExe, os, arch)
5050
}
5151
}
5252
log.Print("File is not compressed")
5353
return src, nil
5454
}
5555

56-
func unzip(src io.Reader, cmd, os, arch string) (io.Reader, error) {
56+
func unzip(src io.Reader, relExe, os, arch string) (io.Reader, error) {
5757
log.Print("Decompressing zip file")
5858

5959
// Zip format requires its file size for Decompressing.
@@ -70,82 +70,82 @@ func unzip(src io.Reader, cmd, os, arch string) (io.Reader, error) {
7070
}
7171

7272
for _, file := range z.File {
73-
_, name := filepath.Split(file.Name)
74-
if !file.FileInfo().IsDir() && matchExecutableName(cmd, os, arch, name) {
73+
_, target := filepath.Split(file.Name)
74+
if !file.FileInfo().IsDir() && matchExecutableName(relExe, os, arch, target) {
7575
log.Printf("Executable file %q was found in zip archive", file.Name)
7676
return file.Open()
7777
}
7878
}
7979

80-
return nil, fmt.Errorf("%w in zip file: %q", ErrExecutableNotFoundInArchive, cmd)
80+
return nil, fmt.Errorf("%w in zip file: %q", ErrExecutableNotFoundInArchive, relExe)
8181
}
8282

83-
func untar(src io.Reader, cmd, os, arch string) (io.Reader, error) {
83+
func untar(src io.Reader, relExe, os, arch string) (io.Reader, error) {
8484
log.Print("Decompressing tar.gz file")
8585

8686
gz, err := gzip.NewReader(src)
8787
if err != nil {
8888
return nil, fmt.Errorf("%w tar.gz file: %s", ErrCannotDecompressFile, err)
8989
}
9090

91-
return unarchiveTar(gz, cmd, os, arch)
91+
return unarchiveTar(gz, relExe, os, arch)
9292
}
9393

94-
func gunzip(src io.Reader, cmd, os, arch string) (io.Reader, error) {
94+
func gunzip(src io.Reader, relExe, os, arch string) (io.Reader, error) {
9595
log.Print("Decompressing gzip file")
9696

9797
r, err := gzip.NewReader(src)
9898
if err != nil {
9999
return nil, fmt.Errorf("%w gzip file: %s", ErrCannotDecompressFile, err)
100100
}
101101

102-
name := r.Name
103-
if !matchExecutableName(cmd, os, arch, name) {
104-
return nil, fmt.Errorf("%w: expected %q but found %q", ErrExecutableNotFoundInArchive, cmd, name)
102+
target := r.Name
103+
if !matchExecutableName(relExe, os, arch, target) {
104+
return nil, fmt.Errorf("%w: expected %q but found %q", ErrExecutableNotFoundInArchive, relExe, target)
105105
}
106106

107-
log.Printf("Executable file %q was found in gzip file", name)
107+
log.Printf("Executable file %q was found in gzip file", target)
108108
return r, nil
109109
}
110110

111-
func untarxz(src io.Reader, cmd, os, arch string) (io.Reader, error) {
111+
func untarxz(src io.Reader, relExe, os, arch string) (io.Reader, error) {
112112
log.Print("Decompressing tar.xz file")
113113

114114
xzip, err := xz.NewReader(src)
115115
if err != nil {
116116
return nil, fmt.Errorf("%w tar.xz file: %s", ErrCannotDecompressFile, err)
117117
}
118118

119-
return unarchiveTar(xzip, cmd, os, arch)
119+
return unarchiveTar(xzip, relExe, os, arch)
120120
}
121121

122-
func unxz(src io.Reader, cmd, os, arch string) (io.Reader, error) {
122+
func unxz(src io.Reader, relExe, os, arch string) (io.Reader, error) {
123123
log.Print("Decompressing xzip file")
124124

125125
xzip, err := xz.NewReader(src)
126126
if err != nil {
127127
return nil, fmt.Errorf("%w xzip file: %s", ErrCannotDecompressFile, err)
128128
}
129129

130-
log.Printf("Decompressed file from xzip is assumed to be an executable: %s", cmd)
130+
log.Printf("Decompressed file from xzip is assumed to be an executable: %s", relExe)
131131
return xzip, nil
132132
}
133133

134-
func unbz2(src io.Reader, cmd, os, arch string) (io.Reader, error) {
134+
func unbz2(src io.Reader, relExe, os, arch string) (io.Reader, error) {
135135
log.Print("Decompressing bzip2 file")
136136

137137
bz2 := bzip2.NewReader(src)
138138

139-
log.Printf("Decompressed file from bzip2 is assumed to be an executable: %s", cmd)
139+
log.Printf("Decompressed file from bzip2 is assumed to be an executable: %s", relExe)
140140
return bz2, nil
141141
}
142142

143-
func matchExecutableName(cmd, os, arch, target string) bool {
144-
cmd = strings.TrimSuffix(cmd, ".exe")
143+
func matchExecutableName(relExe, os, arch, target string) bool {
144+
relExe = strings.TrimSuffix(relExe, ".exe")
145145
pattern := regexp.MustCompile(
146146
fmt.Sprintf(
147147
`^%s([_-]v?%s)?([_-]%s[_-]%s)?(\.exe)?$`,
148-
regexp.QuoteMeta(cmd),
148+
regexp.QuoteMeta(relExe),
149149
semverPattern,
150150
regexp.QuoteMeta(os),
151151
regexp.QuoteMeta(arch),
@@ -154,7 +154,7 @@ func matchExecutableName(cmd, os, arch, target string) bool {
154154
return pattern.MatchString(target)
155155
}
156156

157-
func unarchiveTar(src io.Reader, cmd, os, arch string) (io.Reader, error) {
157+
func unarchiveTar(src io.Reader, relExe, os, arch string) (io.Reader, error) {
158158
t := tar.NewReader(src)
159159
for {
160160
h, err := t.Next()
@@ -164,11 +164,11 @@ func unarchiveTar(src io.Reader, cmd, os, arch string) (io.Reader, error) {
164164
if err != nil {
165165
return nil, fmt.Errorf("%w tar file: %s", ErrCannotDecompressFile, err)
166166
}
167-
_, name := filepath.Split(h.Name)
168-
if matchExecutableName(cmd, os, arch, name) {
167+
_, target := filepath.Split(h.Name)
168+
if matchExecutableName(relExe, os, arch, target) {
169169
log.Printf("Executable file %q was found in tar archive", h.Name)
170170
return t, nil
171171
}
172172
}
173-
return nil, fmt.Errorf("%w in tar: %q", ErrExecutableNotFoundInArchive, cmd)
173+
return nil, fmt.Errorf("%w in tar: %q", ErrExecutableNotFoundInArchive, relExe)
174174
}

package.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,29 +24,29 @@ func DetectVersion(ctx context.Context, repository Repository, version string) (
2424
// This function is low-level API to update the binary. Because it does not use a source provider and downloads asset directly from the URL via HTTP,
2525
// this function is not available to update a release for private repositories.
2626
// cmdPath is a file path to command executable.
27-
func UpdateTo(ctx context.Context, assetURL, assetFileName, cmdPath string) error {
27+
func UpdateTo(ctx context.Context, assetURL, relExe, cmdPath string) error {
2828
//nolint:contextcheck
2929
up := DefaultUpdater()
3030
src, err := downloadReleaseAssetFromURL(ctx, assetURL)
3131
if err != nil {
3232
return err
3333
}
3434
defer src.Close()
35-
return up.decompressAndUpdate(src, assetFileName, assetURL, cmdPath)
35+
return up.decompressAndUpdate(src, assetURL, relExe, cmdPath)
3636
}
3737

3838
// UpdateCommand updates a given command binary to the latest version.
3939
// This function is a shortcut version of updater.UpdateCommand using a DefaultUpdater()
40-
func UpdateCommand(ctx context.Context, cmdPath string, current string, repository Repository) (*Release, error) {
40+
func UpdateCommand(ctx context.Context, relExe, cmdPath string, current string, repository Repository) (*Release, error) {
4141
//nolint:contextcheck
42-
return DefaultUpdater().UpdateCommand(ctx, cmdPath, current, repository)
42+
return DefaultUpdater().UpdateCommand(ctx, relExe, cmdPath, current, repository)
4343
}
4444

4545
// UpdateSelf updates the running executable itself to the latest version.
4646
// This function is a shortcut version of updater.UpdateSelf using a DefaultUpdater()
47-
func UpdateSelf(ctx context.Context, current string, repository Repository) (*Release, error) {
47+
func UpdateSelf(ctx context.Context, relExe, current string, repository Repository) (*Release, error) {
4848
//nolint:contextcheck
49-
return DefaultUpdater().UpdateSelf(ctx, current, repository)
49+
return DefaultUpdater().UpdateSelf(ctx, relExe, current, repository)
5050
}
5151

5252
func downloadReleaseAssetFromURL(ctx context.Context, url string) (rc io.ReadCloser, err error) {

0 commit comments

Comments
 (0)