-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
365 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
119 changes: 119 additions & 0 deletions
119
internal/extension-registry/installer/impl/zipinstaller/installer.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package zipinstaller | ||
|
||
import ( | ||
"net/url" | ||
"os" | ||
"path" | ||
"strings" | ||
|
||
"github.com/ImSingee/go-ex/ee" | ||
"github.com/ImSingee/go-ex/pp" | ||
|
||
"github.com/ImSingee/kitty/internal/extension-registry/binkey" | ||
"github.com/ImSingee/kitty/internal/extension-registry/installer" | ||
"github.com/ImSingee/kitty/internal/extension-registry/installer/tmpl" | ||
eroptions "github.com/ImSingee/kitty/internal/extension-registry/options" | ||
erutils "github.com/ImSingee/kitty/internal/extension-registry/utils" | ||
) | ||
|
||
type Factory struct{} | ||
|
||
func (Factory) Key() string { | ||
return "dist-zip" | ||
} | ||
|
||
func (Factory) GetInstaller(o eroptions.AnyOptions) (installer.Installer, error) { | ||
object := eroptions.Get(o, string(binkey.GetCurrentBinKey())) | ||
object = eroptions.RenameDollarKey(object, "url", false) | ||
|
||
if eroptions.Exists(o, "default") { | ||
defaultOptions := eroptions.Get(o, "default") | ||
defaultOptions = eroptions.RenameDollarKey(defaultOptions, "url", false) | ||
|
||
object = eroptions.Merge(object, defaultOptions, false) | ||
} | ||
|
||
url, _ := object["url"].Val().(string) | ||
bin, _ := object["bin"].Val().(string) | ||
|
||
if url == "" || bin == "" { | ||
return nil, installer.ErrInstallerNotApplicable | ||
} | ||
|
||
return &Installer{url, bin}, nil | ||
} | ||
|
||
type Installer struct { | ||
url string | ||
bin string | ||
} | ||
|
||
func (i *Installer) Install(o *installer.InstallOptions) error { | ||
url, err := tmpl.Render(i.url, o) | ||
if err != nil { | ||
return ee.Wrap(err, "invalid url") | ||
} | ||
bin, err := tmpl.Render(i.bin, o) | ||
if err != nil { | ||
return ee.Wrap(err, "invalid bin") | ||
} | ||
|
||
filenameWithoutExt, ext, err := getFilenamePartsOfUrl(url) | ||
if err != nil { | ||
return err | ||
} | ||
filename := filenameWithoutExt + ext | ||
|
||
zipDownloadedTo, err := erutils.DownloadFileToTemp(url, "kdl-*-"+filename, o.ShowProgress) | ||
if err != nil { | ||
return ee.Wrap(err, "cannot download zip file") | ||
} | ||
|
||
if o.ShowProgress { | ||
pp.Println("zip file downloaded to:", zipDownloadedTo) | ||
} | ||
|
||
unzipToDir, err := os.MkdirTemp("", "kunzip-*") | ||
if err != nil { | ||
return ee.Wrap(err, "cannot create temp dir") | ||
} | ||
|
||
if o.ShowProgress { | ||
pp.Println("Extract ...") | ||
} | ||
err = erutils.Unzip(zipDownloadedTo, unzipToDir) | ||
if err != nil { | ||
return ee.Wrap(err, "cannot unzip file") | ||
} | ||
|
||
distFrom := path.Join(unzipToDir, bin) | ||
if _, err := os.Lstat(distFrom); err != nil { | ||
return ee.Wrapf(err, "cannot find bin file %s in zip", bin) | ||
} | ||
|
||
_, _ = erutils.MkdirFor(o.To) | ||
err = erutils.Rename(distFrom, o.To) | ||
if err != nil { | ||
return ee.Wrapf(err, "failed to install bin file %s from zip", bin) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func getFilenamePartsOfUrl(u string) (name string, ext string, err error) { | ||
parsed, err := url.Parse(u) | ||
if err != nil { | ||
return "", "", ee.Wrapf(err, "url %s is invalid", u) | ||
} | ||
|
||
paths := strings.Split(parsed.Path, "/") | ||
pathLastPart := paths[len(paths)-1] | ||
|
||
dotIndex := strings.Index(pathLastPart, ".") | ||
|
||
if dotIndex == -1 { | ||
return pathLastPart, "", nil | ||
} else { | ||
return pathLastPart[:dotIndex], pathLastPart[dotIndex:], nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package tmpl | ||
|
||
import ( | ||
"strings" | ||
"text/template" | ||
|
||
"github.com/ImSingee/go-ex/ee" | ||
"github.com/ImSingee/semver" | ||
|
||
"github.com/ImSingee/kitty/internal/extension-registry/installer" | ||
) | ||
|
||
func Render(tmpl string, options *installer.InstallOptions) (string, error) { | ||
t, err := template.New("").Parse(tmpl) | ||
if err != nil { | ||
return "", ee.Wrap(err, "invalid template") | ||
} | ||
|
||
w := strings.Builder{} | ||
err = t.Execute(&w, toTemplateOptions(options)) | ||
if err != nil { | ||
return "", ee.Wrap(err, "cannot render template") | ||
} | ||
return w.String(), nil | ||
} | ||
|
||
func toTemplateOptions(options *installer.InstallOptions) map[string]interface{} { | ||
v := "" | ||
if sv, _ := semver.NewVersion(options.Version); sv != nil { | ||
v = sv.String() | ||
} | ||
|
||
return map[string]interface{}{ | ||
"version": options.Version, | ||
"semver": v, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package erutils | ||
|
||
import ( | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"syscall" | ||
|
||
"github.com/ImSingee/go-ex/ee" | ||
) | ||
|
||
func copyFile(from, to string, force bool) error { | ||
from = filepath.Clean(from) | ||
to = filepath.Clean(to) | ||
if from == to { | ||
return nil | ||
} | ||
|
||
fromF, err := os.Open(from) | ||
if err != nil { | ||
return ee.Wrapf(err, "cannot open source file %s", from) | ||
} | ||
defer fromF.Close() | ||
|
||
stat, err := fromF.Stat() | ||
if err != nil { | ||
return ee.Wrapf(err, "cannot stat source file %s", from) | ||
} | ||
|
||
var toF *os.File | ||
if force { // 存在时覆盖 | ||
toF, err = os.OpenFile(to, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, stat.Mode()) | ||
} else { // 存在时报错 | ||
toF, err = os.OpenFile(to, os.O_WRONLY|os.O_CREATE|os.O_EXCL, stat.Mode()) | ||
} | ||
|
||
if err != nil { | ||
return ee.Wrapf(err, "cannot open destination file %s", to) | ||
} | ||
defer toF.Close() | ||
|
||
_, err = io.Copy(toF, fromF) | ||
if err != nil { | ||
return ee.Wrap(err, "cannot copy data") | ||
} | ||
|
||
// change owner | ||
sysStat := stat.Sys().(*syscall.Stat_t) //nolint:forcetypeassert | ||
err = os.Chown(to, int(sysStat.Uid), int(sysStat.Gid)) | ||
if err != nil { | ||
return ee.Wrap(err, "cannot change destination file owner") | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package erutils | ||
|
||
import ( | ||
"os" | ||
"syscall" | ||
|
||
"github.com/ImSingee/go-ex/ee" | ||
) | ||
|
||
func Rename(oldpath, newpath string) error { | ||
err := os.Rename(oldpath, newpath) | ||
if err == nil { | ||
return nil | ||
} | ||
|
||
// os.Rename guarantees it returns an *os.LinkError | ||
if err.(*os.LinkError).Err != syscall.EXDEV { //nolint:forcetypeassert | ||
return err | ||
} | ||
|
||
// fallback to copy and remove | ||
return moveFile(oldpath, newpath) | ||
} | ||
|
||
func moveFile(oldpath, newpath string) error { | ||
err := copyFile(oldpath, newpath, true) | ||
if err != nil { | ||
return ee.Wrapf(err, "rename %s -> %s: cannot copy file", oldpath, newpath) | ||
} | ||
err = os.Remove(oldpath) | ||
if err != nil { | ||
return ee.Wrapf(err, "rename %s -> %s: cannot remove old file", oldpath, newpath) | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.