diff --git a/pkg/controller/install/install.go b/pkg/controller/install/install.go index fba292b21..d9d157aa6 100644 --- a/pkg/controller/install/install.go +++ b/pkg/controller/install/install.go @@ -12,6 +12,7 @@ import ( "github.com/aquaproj/aqua/pkg/config/aqua" registry "github.com/aquaproj/aqua/pkg/install-registry" "github.com/aquaproj/aqua/pkg/installpackage" + "github.com/aquaproj/aqua/pkg/runtime" "github.com/sirupsen/logrus" "github.com/spf13/afero" ) @@ -27,9 +28,10 @@ type Controller struct { configReader reader.ConfigReader registryInstaller registry.Installer fs afero.Fs + runtime *runtime.Runtime } -func New(param *config.Param, configFinder finder.ConfigFinder, configReader reader.ConfigReader, registInstaller registry.Installer, pkgInstaller installpackage.Installer, fs afero.Fs) *Controller { +func New(param *config.Param, configFinder finder.ConfigFinder, configReader reader.ConfigReader, registInstaller registry.Installer, pkgInstaller installpackage.Installer, fs afero.Fs, rt *runtime.Runtime) *Controller { return &Controller{ rootDir: param.RootDir, configFinder: configFinder, @@ -37,6 +39,7 @@ func New(param *config.Param, configFinder finder.ConfigFinder, configReader rea registryInstaller: registInstaller, packageInstaller: pkgInstaller, fs: fs, + runtime: rt, } } @@ -46,6 +49,11 @@ func (ctrl *Controller) Install(ctx context.Context, param *config.Param, logE * if err := ctrl.fs.MkdirAll(rootBin, dirPermission); err != nil { return fmt.Errorf("create the directory: %w", err) } + if ctrl.runtime.GOOS == "windows" { + if err := ctrl.fs.MkdirAll(filepath.Join(ctrl.rootDir, "bat"), dirPermission); err != nil { + return fmt.Errorf("create the directory: %w", err) + } + } if err := ctrl.packageInstaller.InstallProxy(ctx, logE); err != nil { return err //nolint:wrapcheck diff --git a/pkg/controller/install/install_test.go b/pkg/controller/install/install_test.go index 3dbc20fd6..8e1922758 100644 --- a/pkg/controller/install/install_test.go +++ b/pkg/controller/install/install_test.go @@ -90,7 +90,7 @@ packages: downloader := download.NewPackageDownloader(nil, d.rt, download.NewHTTPDownloader(http.DefaultClient)) executor := exec.NewMock(0, nil) pkgInstaller := installpackage.New(d.param, downloader, d.rt, fs, linker, executor) - ctrl := install.New(d.param, finder.NewConfigFinder(fs), reader.New(fs), registry.New(d.param, registryDownloader, fs), pkgInstaller, fs) + ctrl := install.New(d.param, finder.NewConfigFinder(fs), reader.New(fs), registry.New(d.param, registryDownloader, fs), pkgInstaller, fs, d.rt) if err := ctrl.Install(ctx, d.param, logE); err != nil { if d.isErr { return diff --git a/pkg/controller/wire_gen.go b/pkg/controller/wire_gen.go index c797d30c0..beacc4c1e 100644 --- a/pkg/controller/wire_gen.go +++ b/pkg/controller/wire_gen.go @@ -75,7 +75,7 @@ func InitializeInstallCommandController(ctx context.Context, param *config.Param linker := link.New() executor := exec.New() installpackageInstaller := installpackage.New(param, packageDownloader, rt, fs, linker, executor) - controller := install.New(param, configFinder, configReader, installer, installpackageInstaller, fs) + controller := install.New(param, configFinder, configReader, installer, installpackageInstaller, fs, rt) return controller } diff --git a/pkg/installpackage/installer.go b/pkg/installpackage/installer.go index e4d1e202b..62ca16f5a 100644 --- a/pkg/installpackage/installer.go +++ b/pkg/installpackage/installer.go @@ -32,6 +32,10 @@ type installer struct { executor exec.Executor } +func isWindows(goos string) bool { + return goos == "windows" +} + func (inst *installer) InstallPackages(ctx context.Context, cfg *aqua.Config, registries map[string]*registry.Config, binDir string, onlyLink, isTest bool, logE *logrus.Entry) error { pkgs, failed := inst.createLinks(cfg, registries, binDir, logE) if onlyLink { @@ -186,6 +190,13 @@ func (inst *installer) createLinks(cfg *aqua.Config, registries map[string]*regi PackageInfo: pkgInfo, }) for _, file := range pkgInfo.GetFiles() { + if isWindows(inst.runtime.GOOS) { + if err := inst.createProxyWindows(file.Name, logE); err != nil { + logerr.WithError(logE, err).Error("create the proxy file") + failed = true + } + continue + } if err := inst.createLink(filepath.Join(binDir, file.Name), proxyName, logE); err != nil { logerr.WithError(logE, err).Error("create the symbolic link") failed = true diff --git a/pkg/installpackage/link.go b/pkg/installpackage/link.go index 70fe73b70..aaac3435a 100644 --- a/pkg/installpackage/link.go +++ b/pkg/installpackage/link.go @@ -3,8 +3,11 @@ package installpackage import ( "fmt" "os" + "path/filepath" + "strings" "github.com/sirupsen/logrus" + "github.com/spf13/afero" ) func (inst *installer) createLink(linkPath, linkDest string, logE *logrus.Entry) error { @@ -58,3 +61,58 @@ func (inst *installer) recreateLink(linkPath, linkDest string, logE *logrus.Entr } return nil } + +const ( + batTemplate = `@echo off +aqua exec -- %* +` + scrTemplate = `#!/usr/bin/env bash +exec aqua exec -- $0 $@ +` + proxyPermission os.FileMode = 0o755 +) + +func (inst *installer) createProxyWindows(binName string, logE *logrus.Entry) error { + if err := inst.createBinWindows(filepath.Join(inst.rootDir, "bin", binName), scrTemplate, logE); err != nil { + return err + } + if err := inst.createBinWindows(filepath.Join(inst.rootDir, "bat", binName+".bat"), strings.Replace(batTemplate, "", binName, 1), logE); err != nil { + return err + } + return nil +} + +func (inst *installer) createBinWindows(binPath, binTxt string, logE *logrus.Entry) error { + if fileInfo, err := inst.linker.Lstat(binPath); err == nil { + switch mode := fileInfo.Mode(); { + case mode.IsDir(): + // if file is a directory, raise error + return fmt.Errorf("%s has already existed and is a directory", binPath) + case mode&os.ModeNamedPipe != 0: + // if file is a pipe, raise error + return fmt.Errorf("%s has already existed and is a named pipe", binPath) + case mode.IsRegular(): + // TODO check content + return nil + case mode&os.ModeSymlink != 0: + if err := inst.fs.Remove(binPath); err != nil { + return fmt.Errorf("remove a symbolic link (%s): %w", binPath, err) + } + return inst.writeBinWindows(binPath, binTxt, logE) + default: + return fmt.Errorf("unexpected file mode %s: %s", binPath, mode.String()) + } + } + + return inst.writeBinWindows(binPath, binTxt, logE) +} + +func (inst *installer) writeBinWindows(proxyPath, binTxt string, logE *logrus.Entry) error { + logE.WithFields(logrus.Fields{ + "proxy_path": proxyPath, + }).Info("create a proxy file") + if err := afero.WriteFile(inst.fs, proxyPath, []byte(binTxt), proxyPermission); err != nil { + return fmt.Errorf("create a proxy file (%s): %w", proxyPath, err) + } + return nil +} diff --git a/pkg/installpackage/proxy.go b/pkg/installpackage/proxy.go index 27875a61f..1dcf9ec10 100644 --- a/pkg/installpackage/proxy.go +++ b/pkg/installpackage/proxy.go @@ -14,6 +14,9 @@ import ( const ProxyVersion = "v1.1.2" // renovate: depName=aquaproj/aqua-proxy func (inst *installer) InstallProxy(ctx context.Context, logE *logrus.Entry) error { + if isWindows(inst.runtime.GOOS) { + return nil + } proxyAssetTemplate := `aqua-proxy_{{.OS}}_{{.Arch}}.tar.gz` pkg := &config.Package{ Package: &aqua.Package{ @@ -63,9 +66,6 @@ func (inst *installer) InstallProxy(ctx context.Context, logE *logrus.Entry) err // create a symbolic link binName := proxyName - if inst.runtime.GOOS == "windows" { - binName += ".exe" - } a, err := filepath.Rel(filepath.Join(inst.rootDir, "bin"), filepath.Join(pkgPath, binName)) if err != nil { return fmt.Errorf("get a relative path: %w", err)