From 4704f704bf77cc86758f3d44cf6d45603dd88b0c Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sat, 29 Jul 2023 14:44:06 +0900 Subject: [PATCH] feat: support go_build package (#2065) * feat: support go_build package * fix: fix * fix: fix compile errors * fix: fix type * fix: fix go_build --- pkg/config/package.go | 25 +++++++++++- pkg/config/registry/package_info.go | 16 +++++++- pkg/controller/exec/exec_test.go | 4 +- pkg/controller/install/install_test.go | 2 +- pkg/controller/wire.go | 16 ++++++++ pkg/controller/wire_gen.go | 12 ++++-- pkg/download/util.go | 3 +- pkg/exec/exec.go | 4 ++ pkg/installpackage/aqua_test.go | 2 +- pkg/installpackage/go_build.go | 38 ++++++++++++++++++ pkg/installpackage/installer.go | 53 +++++++++++++++++++++----- pkg/installpackage/installer_test.go | 4 +- pkg/installpackage/proxy_test.go | 2 +- pkg/installpackage/public.go | 2 + tests/main/aqua-global.yaml | 2 + tests/main/registry.yaml | 8 ++++ 16 files changed, 169 insertions(+), 24 deletions(-) create mode 100644 pkg/installpackage/go_build.go diff --git a/pkg/config/package.go b/pkg/config/package.go index 019ce6aa7..4ad647b2f 100644 --- a/pkg/config/package.go +++ b/pkg/config/package.go @@ -22,6 +22,11 @@ type Package struct { } func (cpkg *Package) GetExePath(rootDir string, file *registry.File, rt *runtime.Runtime) (string, error) { + pkgInfo := cpkg.PackageInfo + if pkgInfo.Type == "go_build" { + return filepath.Join(rootDir, "pkgs", pkgInfo.GetType(), "github.com", pkgInfo.RepoOwner, pkgInfo.RepoName, cpkg.Package.Version, "bin", file.Name), nil + } + pkgPath, err := cpkg.GetPkgPath(rootDir, rt) if err != nil { return "", err @@ -75,6 +80,8 @@ func (cpkg *Package) GetPkgPath(rootDir string, rt *runtime.Runtime) (string, er switch pkgInfo.Type { case PkgInfoTypeGitHubArchive: return filepath.Join(rootDir, "pkgs", pkgInfo.GetType(), "github.com", pkgInfo.RepoOwner, pkgInfo.RepoName, pkg.Version), nil + case PkgInfoTypeGoBuild: + return filepath.Join(rootDir, "pkgs", pkgInfo.GetType(), "github.com", pkgInfo.RepoOwner, pkgInfo.RepoName, pkg.Version, "src"), nil case PkgInfoTypeGoInstall: p, err := cpkg.RenderPath() if err != nil { @@ -209,6 +216,7 @@ const ( PkgInfoTypeGitHubArchive = "github_archive" PkgInfoTypeHTTP = "http" PkgInfoTypeGoInstall = "go_install" + PkgInfoTypeGoBuild = "go_build" PkgInfoTypeCargo = "cargo" ) @@ -249,7 +257,7 @@ type Param struct { func (cpkg *Package) renderAsset(rt *runtime.Runtime) (string, error) { pkgInfo := cpkg.PackageInfo switch pkgInfo.Type { - case PkgInfoTypeGitHubArchive: + case PkgInfoTypeGitHubArchive, PkgInfoTypeGoBuild: return "", nil case PkgInfoTypeGoInstall: if pkgInfo.Asset != nil { @@ -328,3 +336,18 @@ func (cpkg *Package) semVer() string { } return strings.TrimPrefix(v, prefix) } + +func (cpkg *Package) RenderDir(file *registry.File, rt *runtime.Runtime) (string, error) { + pkgInfo := cpkg.PackageInfo + pkg := cpkg.Package + return template.Execute(file.Dir, map[string]interface{}{ //nolint:wrapcheck + "Version": pkg.Version, + "SemVer": cpkg.semVer(), + "GOOS": rt.GOOS, + "GOARCH": rt.GOARCH, + "OS": replace(rt.GOOS, pkgInfo.GetReplacements()), + "Arch": getArch(pkgInfo.GetRosetta2(), pkgInfo.GetReplacements(), rt), + "Format": pkgInfo.GetFormat(), + "FileName": file.Name, + }) +} diff --git a/pkg/config/registry/package_info.go b/pkg/config/registry/package_info.go index e0058772e..3bc439dec 100644 --- a/pkg/config/registry/package_info.go +++ b/pkg/config/registry/package_info.go @@ -16,6 +16,7 @@ const ( PkgInfoTypeGitHubArchive = "github_archive" PkgInfoTypeHTTP = "http" PkgInfoTypeGoInstall = "go_install" + PkgInfoTypeGoBuild = "go_build" PkgInfoTypeCargo = "cargo" ) @@ -173,6 +174,17 @@ func (pkgInfo *PackageInfo) resetByPkgType(typ string) { //nolint:funlen pkgInfo.SLSAProvenance = nil pkgInfo.Format = "" pkgInfo.Rosetta2 = nil + case PkgInfoTypeGoBuild: + pkgInfo.URL = nil + pkgInfo.Asset = nil + pkgInfo.Crate = nil + pkgInfo.Cargo = nil + pkgInfo.WindowsExt = "" + pkgInfo.CompleteWindowsExt = nil + pkgInfo.Cosign = nil + pkgInfo.SLSAProvenance = nil + pkgInfo.Format = "" + pkgInfo.Rosetta2 = nil case PkgInfoTypeCargo: pkgInfo.URL = nil pkgInfo.Asset = nil @@ -445,7 +457,7 @@ func (pkgInfo *PackageInfo) GetLink() string { } func (pkgInfo *PackageInfo) GetFormat() string { - if pkgInfo.Type == PkgInfoTypeGitHubArchive { + if pkgInfo.Type == PkgInfoTypeGitHubArchive || pkgInfo.Type == PkgInfoTypeGoBuild { return "tar.gz" } return pkgInfo.Format @@ -490,7 +502,7 @@ func (pkgInfo *PackageInfo) Validate() error { //nolint:cyclop return errPkgNameIsRequired } switch pkgInfo.Type { - case PkgInfoTypeGitHubArchive: + case PkgInfoTypeGitHubArchive, PkgInfoTypeGoBuild: if !pkgInfo.HasRepo() { return errRepoRequired } diff --git a/pkg/controller/exec/exec_test.go b/pkg/controller/exec/exec_test.go index 6a83f3518..f257df191 100644 --- a/pkg/controller/exec/exec_test.go +++ b/pkg/controller/exec/exec_test.go @@ -152,7 +152,7 @@ packages: whichCtrl := which.New(d.param, finder.NewConfigFinder(fs), reader.New(fs, d.param), registry.New(d.param, ghDownloader, fs, d.rt, &cosign.MockVerifier{}, &slsa.MockVerifier{}), d.rt, osEnv, fs, linker) downloader := download.NewDownloader(nil, download.NewHTTPDownloader(http.DefaultClient)) executor := &exec.Mock{} - pkgInstaller := installpackage.New(d.param, downloader, d.rt, fs, linker, nil, &checksum.Calculator{}, unarchive.New(executor, fs), &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockCargoPackageInstaller{}) + pkgInstaller := installpackage.New(d.param, downloader, d.rt, fs, linker, nil, &checksum.Calculator{}, unarchive.New(executor, fs), &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockGoBuildInstaller{}, &installpackage.MockCargoPackageInstaller{}) policyFinder := policy.NewConfigFinder(fs) ctrl := execCtrl.New(d.param, pkgInstaller, whichCtrl, executor, osEnv, fs, policy.NewReader(fs, policy.NewValidator(d.param, fs), policyFinder, policy.NewConfigReader(fs)), policyFinder) if err := ctrl.Exec(ctx, logE, d.param, d.exeName, d.args...); err != nil { @@ -248,7 +248,7 @@ packages: whichCtrl := which.New(d.param, finder.NewConfigFinder(fs), reader.New(fs, d.param), registry.New(d.param, ghDownloader, afero.NewOsFs(), d.rt, &cosign.MockVerifier{}, &slsa.MockVerifier{}), d.rt, osEnv, fs, linker) downloader := download.NewDownloader(nil, download.NewHTTPDownloader(http.DefaultClient)) executor := &exec.Mock{} - pkgInstaller := installpackage.New(d.param, downloader, d.rt, fs, linker, nil, &checksum.Calculator{}, unarchive.New(executor, fs), &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockCargoPackageInstaller{}) + pkgInstaller := installpackage.New(d.param, downloader, d.rt, fs, linker, nil, &checksum.Calculator{}, unarchive.New(executor, fs), &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockGoBuildInstaller{}, &installpackage.MockCargoPackageInstaller{}) ctrl := execCtrl.New(d.param, pkgInstaller, whichCtrl, executor, osEnv, fs, &policy.MockReader{}, policy.NewConfigFinder(fs)) b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/pkg/controller/install/install_test.go b/pkg/controller/install/install_test.go index f2cd324d8..0cb477569 100644 --- a/pkg/controller/install/install_test.go +++ b/pkg/controller/install/install_test.go @@ -103,7 +103,7 @@ packages: } downloader := download.NewDownloader(nil, download.NewHTTPDownloader(http.DefaultClient)) executor := &exec.Mock{} - pkgInstaller := installpackage.New(d.param, downloader, d.rt, fs, linker, nil, &checksum.Calculator{}, unarchive.New(executor, fs), &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockCargoPackageInstaller{}) + pkgInstaller := installpackage.New(d.param, downloader, d.rt, fs, linker, nil, &checksum.Calculator{}, unarchive.New(executor, fs), &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockGoBuildInstaller{}, &installpackage.MockCargoPackageInstaller{}) policyFinder := policy.NewConfigFinder(fs) policyReader := policy.NewReader(fs, &policy.MockValidator{}, policyFinder, policy.NewConfigReader(fs)) ctrl := install.New(d.param, finder.NewConfigFinder(fs), reader.New(fs, d.param), registry.New(d.param, registryDownloader, fs, d.rt, &cosign.MockVerifier{}, &slsa.MockVerifier{}), pkgInstaller, fs, d.rt, policyReader, policyFinder) diff --git a/pkg/controller/wire.go b/pkg/controller/wire.go index 3a0f62918..dc922996d 100644 --- a/pkg/controller/wire.go +++ b/pkg/controller/wire.go @@ -293,6 +293,10 @@ func InitializeInstallCommandController(ctx context.Context, param *config.Param installpackage.NewGoInstallInstallerImpl, wire.Bind(new(installpackage.GoInstallInstaller), new(*installpackage.GoInstallInstallerImpl)), ), + wire.NewSet( + installpackage.NewGoBuildInstallerImpl, + wire.Bind(new(installpackage.GoBuildInstaller), new(*installpackage.GoBuildInstallerImpl)), + ), wire.NewSet( installpackage.NewCargoPackageInstallerImpl, wire.Bind(new(installpackage.CargoPackageInstaller), new(*installpackage.CargoPackageInstallerImpl)), @@ -456,6 +460,10 @@ func InitializeExecCommandController(ctx context.Context, param *config.Param, h installpackage.NewGoInstallInstallerImpl, wire.Bind(new(installpackage.GoInstallInstaller), new(*installpackage.GoInstallInstallerImpl)), ), + wire.NewSet( + installpackage.NewGoBuildInstallerImpl, + wire.Bind(new(installpackage.GoBuildInstaller), new(*installpackage.GoBuildInstallerImpl)), + ), wire.NewSet( installpackage.NewCargoPackageInstallerImpl, wire.Bind(new(installpackage.CargoPackageInstaller), new(*installpackage.CargoPackageInstallerImpl)), @@ -524,6 +532,10 @@ func InitializeUpdateAquaCommandController(ctx context.Context, param *config.Pa installpackage.NewGoInstallInstallerImpl, wire.Bind(new(installpackage.GoInstallInstaller), new(*installpackage.GoInstallInstallerImpl)), ), + wire.NewSet( + installpackage.NewGoBuildInstallerImpl, + wire.Bind(new(installpackage.GoBuildInstaller), new(*installpackage.GoBuildInstallerImpl)), + ), wire.NewSet( installpackage.NewCargoPackageInstallerImpl, wire.Bind(new(installpackage.CargoPackageInstaller), new(*installpackage.CargoPackageInstallerImpl)), @@ -638,6 +650,10 @@ func InitializeCopyCommandController(ctx context.Context, param *config.Param, h installpackage.NewGoInstallInstallerImpl, wire.Bind(new(installpackage.GoInstallInstaller), new(*installpackage.GoInstallInstallerImpl)), ), + wire.NewSet( + installpackage.NewGoBuildInstallerImpl, + wire.Bind(new(installpackage.GoBuildInstaller), new(*installpackage.GoBuildInstallerImpl)), + ), wire.NewSet( installpackage.NewCargoPackageInstallerImpl, wire.Bind(new(installpackage.CargoPackageInstaller), new(*installpackage.CargoPackageInstallerImpl)), diff --git a/pkg/controller/wire_gen.go b/pkg/controller/wire_gen.go index c7f4cb1b8..18faa8e93 100644 --- a/pkg/controller/wire_gen.go +++ b/pkg/controller/wire_gen.go @@ -126,8 +126,9 @@ func InitializeInstallCommandController(ctx context.Context, param *config.Param unarchiverImpl := unarchive.New(executor, fs) checker := policy.NewChecker(param) goInstallInstallerImpl := installpackage.NewGoInstallInstallerImpl(executor) + goBuildInstallerImpl := installpackage.NewGoBuildInstallerImpl(executor) cargoPackageInstallerImpl := installpackage.NewCargoPackageInstallerImpl(executor, fs) - installpackageInstallerImpl := installpackage.New(param, downloader, rt, fs, linker, checksumDownloaderImpl, calculator, unarchiverImpl, checker, verifierImpl, slsaVerifierImpl, goInstallInstallerImpl, cargoPackageInstallerImpl) + installpackageInstallerImpl := installpackage.New(param, downloader, rt, fs, linker, checksumDownloaderImpl, calculator, unarchiverImpl, checker, verifierImpl, slsaVerifierImpl, goInstallInstallerImpl, goBuildInstallerImpl, cargoPackageInstallerImpl) validatorImpl := policy.NewValidator(param, fs) configFinderImpl := policy.NewConfigFinder(fs) policyConfigReaderImpl := policy.NewConfigReader(fs) @@ -170,8 +171,9 @@ func InitializeExecCommandController(ctx context.Context, param *config.Param, h executorImpl := slsa.NewExecutor(executor, param) slsaVerifierImpl := slsa.New(downloader, fs, executorImpl) goInstallInstallerImpl := installpackage.NewGoInstallInstallerImpl(executor) + goBuildInstallerImpl := installpackage.NewGoBuildInstallerImpl(executor) cargoPackageInstallerImpl := installpackage.NewCargoPackageInstallerImpl(executor, fs) - installerImpl := installpackage.New(param, downloader, rt, fs, linker, checksumDownloaderImpl, calculator, unarchiverImpl, checker, verifierImpl, slsaVerifierImpl, goInstallInstallerImpl, cargoPackageInstallerImpl) + installerImpl := installpackage.New(param, downloader, rt, fs, linker, checksumDownloaderImpl, calculator, unarchiverImpl, checker, verifierImpl, slsaVerifierImpl, goInstallInstallerImpl, goBuildInstallerImpl, cargoPackageInstallerImpl) configFinder := finder.NewConfigFinder(fs) configReaderImpl := reader.New(fs, param) gitHubContentFileDownloader := download.NewGitHubContentFileDownloader(repositoriesService, httpDownloader) @@ -201,8 +203,9 @@ func InitializeUpdateAquaCommandController(ctx context.Context, param *config.Pa executorImpl := slsa.NewExecutor(executor, param) slsaVerifierImpl := slsa.New(downloader, fs, executorImpl) goInstallInstallerImpl := installpackage.NewGoInstallInstallerImpl(executor) + goBuildInstallerImpl := installpackage.NewGoBuildInstallerImpl(executor) cargoPackageInstallerImpl := installpackage.NewCargoPackageInstallerImpl(executor, fs) - installerImpl := installpackage.New(param, downloader, rt, fs, linker, checksumDownloaderImpl, calculator, unarchiverImpl, checker, verifierImpl, slsaVerifierImpl, goInstallInstallerImpl, cargoPackageInstallerImpl) + installerImpl := installpackage.New(param, downloader, rt, fs, linker, checksumDownloaderImpl, calculator, unarchiverImpl, checker, verifierImpl, slsaVerifierImpl, goInstallInstallerImpl, goBuildInstallerImpl, cargoPackageInstallerImpl) controller := updateaqua.New(param, fs, rt, repositoriesService, installerImpl) return controller } @@ -222,8 +225,9 @@ func InitializeCopyCommandController(ctx context.Context, param *config.Param, h executorImpl := slsa.NewExecutor(executor, param) slsaVerifierImpl := slsa.New(downloader, fs, executorImpl) goInstallInstallerImpl := installpackage.NewGoInstallInstallerImpl(executor) + goBuildInstallerImpl := installpackage.NewGoBuildInstallerImpl(executor) cargoPackageInstallerImpl := installpackage.NewCargoPackageInstallerImpl(executor, fs) - installerImpl := installpackage.New(param, downloader, rt, fs, linker, checksumDownloaderImpl, calculator, unarchiverImpl, checker, verifierImpl, slsaVerifierImpl, goInstallInstallerImpl, cargoPackageInstallerImpl) + installerImpl := installpackage.New(param, downloader, rt, fs, linker, checksumDownloaderImpl, calculator, unarchiverImpl, checker, verifierImpl, slsaVerifierImpl, goInstallInstallerImpl, goBuildInstallerImpl, cargoPackageInstallerImpl) configFinder := finder.NewConfigFinder(fs) configReaderImpl := reader.New(fs, param) gitHubContentFileDownloader := download.NewGitHubContentFileDownloader(repositoriesService, httpDownloader) diff --git a/pkg/download/util.go b/pkg/download/util.go index bf50066d3..ba0500571 100644 --- a/pkg/download/util.go +++ b/pkg/download/util.go @@ -70,7 +70,8 @@ func ConvertPackageToFile(pkg *config.Package, assetName string, rt *runtime.Run case config.PkgInfoTypeGitHubContent: file.Path = assetName return file, nil - case config.PkgInfoTypeGitHubArchive: + case config.PkgInfoTypeGitHubArchive, config.PkgInfoTypeGoBuild: + file.Type = config.PkgInfoTypeGitHubArchive return file, nil case config.PkgInfoTypeHTTP: uS, err := pkg.RenderURL(rt) diff --git a/pkg/exec/exec.go b/pkg/exec/exec.go index 0fc066253..29b63ed92 100644 --- a/pkg/exec/exec.go +++ b/pkg/exec/exec.go @@ -24,6 +24,10 @@ func New() *Executor { } } +func (exe *Executor) ExecCommand(cmd *exec.Cmd) (int, error) { + return exe.exec(exe.command(cmd)) +} + func (exe *Executor) Exec(ctx context.Context, exePath string, args ...string) (int, error) { return exe.exec(exe.command(exec.CommandContext(ctx, exePath, args...))) } diff --git a/pkg/installpackage/aqua_test.go b/pkg/installpackage/aqua_test.go index 504dc00d2..c682e925c 100644 --- a/pkg/installpackage/aqua_test.go +++ b/pkg/installpackage/aqua_test.go @@ -69,7 +69,7 @@ e922723678f493216c2398f3f23fb027c9a98808b49f6fce401ef82ee2c22b03 aqua_linux_arm } ctrl := installpackage.New(d.param, &download.Mock{ RC: io.NopCloser(strings.NewReader("xxx")), - }, d.rt, fs, domain.NewMockLinker(fs), d.checksumDownloader, d.checksumCalculator, &unarchive.MockUnarchiver{}, &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockCargoPackageInstaller{}) + }, d.rt, fs, domain.NewMockLinker(fs), d.checksumDownloader, d.checksumCalculator, &unarchive.MockUnarchiver{}, &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockGoBuildInstaller{}, &installpackage.MockCargoPackageInstaller{}) if err := ctrl.InstallAqua(ctx, logE, d.version); err != nil { if d.isErr { return diff --git a/pkg/installpackage/go_build.go b/pkg/installpackage/go_build.go new file mode 100644 index 000000000..00f46d07a --- /dev/null +++ b/pkg/installpackage/go_build.go @@ -0,0 +1,38 @@ +package installpackage + +import ( + "context" + "fmt" + "os/exec" +) + +type GoBuildInstaller interface { + Install(ctx context.Context, exePath, exeDir, src string) error +} + +type MockGoBuildInstaller struct { + Err error +} + +func (mock *MockGoBuildInstaller) Install(ctx context.Context, exePath, exeDir, src string) error { + return mock.Err +} + +type GoBuildInstallerImpl struct { + exec Executor +} + +func NewGoBuildInstallerImpl(exec Executor) *GoBuildInstallerImpl { + return &GoBuildInstallerImpl{ + exec: exec, + } +} + +func (inst *GoBuildInstallerImpl) Install(ctx context.Context, exePath, exeDir, src string) error { + cmd := exec.CommandContext(ctx, "go", "build", "-o", exePath, src) + cmd.Dir = exeDir + if _, err := inst.exec.ExecCommand(cmd); err != nil { + return fmt.Errorf("build a go package: %w", err) + } + return nil +} diff --git a/pkg/installpackage/installer.go b/pkg/installpackage/installer.go index a15ad6c98..38d355dcb 100644 --- a/pkg/installpackage/installer.go +++ b/pkg/installpackage/installer.go @@ -41,6 +41,7 @@ type InstallerImpl struct { cosignInstaller *Cosign slsaVerifierInstaller *SLSAVerifier goInstallInstaller GoInstallInstaller + goBuildInstaller GoBuildInstaller cargoPackageInstaller CargoPackageInstaller runtime *runtime.Runtime fs afero.Fs @@ -51,20 +52,20 @@ type InstallerImpl struct { onlyLink bool } -func New(param *config.Param, downloader download.ClientAPI, rt *runtime.Runtime, fs afero.Fs, linker domain.Linker, chkDL download.ChecksumDownloader, chkCalc ChecksumCalculator, unarchiver unarchive.Unarchiver, policyChecker *policy.Checker, cosignVerifier cosign.Verifier, slsaVerifier slsa.Verifier, goInstallInstaller GoInstallInstaller, cargoPackageInstaller CargoPackageInstaller) *InstallerImpl { - installer := newInstaller(param, downloader, rt, fs, linker, chkDL, chkCalc, unarchiver, policyChecker, cosignVerifier, slsaVerifier, goInstallInstaller, cargoPackageInstaller) +func New(param *config.Param, downloader download.ClientAPI, rt *runtime.Runtime, fs afero.Fs, linker domain.Linker, chkDL download.ChecksumDownloader, chkCalc ChecksumCalculator, unarchiver unarchive.Unarchiver, policyChecker *policy.Checker, cosignVerifier cosign.Verifier, slsaVerifier slsa.Verifier, goInstallInstaller GoInstallInstaller, goBuildInstaller GoBuildInstaller, cargoPackageInstaller CargoPackageInstaller) *InstallerImpl { + installer := newInstaller(param, downloader, rt, fs, linker, chkDL, chkCalc, unarchiver, policyChecker, cosignVerifier, slsaVerifier, goInstallInstaller, goBuildInstaller, cargoPackageInstaller) installer.cosignInstaller = &Cosign{ - installer: newInstaller(param, downloader, runtime.NewR(), fs, linker, chkDL, chkCalc, unarchiver, policyChecker, cosignVerifier, slsaVerifier, goInstallInstaller, cargoPackageInstaller), + installer: newInstaller(param, downloader, runtime.NewR(), fs, linker, chkDL, chkCalc, unarchiver, policyChecker, cosignVerifier, slsaVerifier, goInstallInstaller, goBuildInstaller, cargoPackageInstaller), mutex: &sync.Mutex{}, } installer.slsaVerifierInstaller = &SLSAVerifier{ - installer: newInstaller(param, downloader, runtime.NewR(), fs, linker, chkDL, chkCalc, unarchiver, policyChecker, cosignVerifier, slsaVerifier, goInstallInstaller, cargoPackageInstaller), + installer: newInstaller(param, downloader, runtime.NewR(), fs, linker, chkDL, chkCalc, unarchiver, policyChecker, cosignVerifier, slsaVerifier, goInstallInstaller, goBuildInstaller, cargoPackageInstaller), mutex: &sync.Mutex{}, } return installer } -func newInstaller(param *config.Param, downloader download.ClientAPI, rt *runtime.Runtime, fs afero.Fs, linker domain.Linker, chkDL download.ChecksumDownloader, chkCalc ChecksumCalculator, unarchiver unarchive.Unarchiver, policyChecker *policy.Checker, cosignVerifier cosign.Verifier, slsaVerifier slsa.Verifier, goInstallInstaller GoInstallInstaller, cargoPackageInstaller CargoPackageInstaller) *InstallerImpl { +func newInstaller(param *config.Param, downloader download.ClientAPI, rt *runtime.Runtime, fs afero.Fs, linker domain.Linker, chkDL download.ChecksumDownloader, chkCalc ChecksumCalculator, unarchiver unarchive.Unarchiver, policyChecker *policy.Checker, cosignVerifier cosign.Verifier, slsaVerifier slsa.Verifier, goInstallInstaller GoInstallInstaller, goBuildInstaller GoBuildInstaller, cargoPackageInstaller CargoPackageInstaller) *InstallerImpl { return &InstallerImpl{ rootDir: param.RootDir, maxParallelism: param.MaxParallelism, @@ -82,6 +83,7 @@ func newInstaller(param *config.Param, downloader download.ClientAPI, rt *runtim cosign: cosignVerifier, slsaVerifier: slsaVerifier, goInstallInstaller: goInstallInstaller, + goBuildInstaller: goBuildInstaller, cargoPackageInstaller: cargoPackageInstaller, } } @@ -257,7 +259,7 @@ func (inst *InstallerImpl) InstallPackage(ctx context.Context, logE *logrus.Entr for _, file := range pkgInfo.GetFiles() { logE := logE.WithField("file_name", file.Name) var errFileNotFound *config.FileNotFoundError - if err := inst.checkAndCopyFile(pkg, file, logE); err != nil { + if err := inst.checkAndCopyFile(ctx, pkg, file, logE); err != nil { if errors.As(err, &errFileNotFound) { notFound = true } @@ -317,8 +319,8 @@ type DownloadParam struct { RequireChecksum bool } -func (inst *InstallerImpl) checkAndCopyFile(pkg *config.Package, file *registry.File, logE *logrus.Entry) error { - exePath, err := inst.checkFileSrc(pkg, file, logE) +func (inst *InstallerImpl) checkAndCopyFile(ctx context.Context, pkg *config.Package, file *registry.File, logE *logrus.Entry) error { + exePath, err := inst.checkFileSrc(ctx, pkg, file, logE) if err != nil { return fmt.Errorf("check file_src is correct: %w", err) } @@ -333,7 +335,40 @@ func (inst *InstallerImpl) checkAndCopyFile(pkg *config.Package, file *registry. return nil } -func (inst *InstallerImpl) checkFileSrc(pkg *config.Package, file *registry.File, logE *logrus.Entry) (string, error) { +func (inst *InstallerImpl) checkFileSrcGo(ctx context.Context, pkg *config.Package, file *registry.File, logE *logrus.Entry) (string, error) { + pkgInfo := pkg.PackageInfo + exePath := filepath.Join(inst.rootDir, "pkgs", pkgInfo.GetType(), "github.com", pkgInfo.RepoOwner, pkgInfo.RepoName, pkg.Package.Version, "bin", file.Name) + if isWindows(inst.runtime.GOOS) { + exePath += ".exe" + } + dir, err := pkg.RenderDir(file, inst.runtime) + if err != nil { + return "", fmt.Errorf("render file dir: %w", err) + } + exeDir := filepath.Join(inst.rootDir, "pkgs", pkgInfo.GetType(), "github.com", pkgInfo.RepoOwner, pkgInfo.RepoName, pkg.Package.Version, "src", dir) + if _, err := inst.fs.Stat(exePath); err == nil { + return exePath, nil + } + src := file.Src + if src == "" { + src = "." + } + logE.WithFields(logrus.Fields{ + "exe_path": exePath, + "go_src": src, + "go_build_dir": exeDir, + }).Info("building Go tool") + if err := inst.goBuildInstaller.Install(ctx, exePath, exeDir, src); err != nil { + return "", fmt.Errorf("build Go tool: %w", err) + } + return exePath, nil +} + +func (inst *InstallerImpl) checkFileSrc(ctx context.Context, pkg *config.Package, file *registry.File, logE *logrus.Entry) (string, error) { + if pkg.PackageInfo.Type == "go_build" { + return inst.checkFileSrcGo(ctx, pkg, file, logE) + } + pkgPath, err := pkg.GetPkgPath(inst.rootDir, inst.runtime) if err != nil { return "", fmt.Errorf("get the package install path: %w", err) diff --git a/pkg/installpackage/installer_test.go b/pkg/installpackage/installer_test.go index 2ebd679bf..8277f8c20 100644 --- a/pkg/installpackage/installer_test.go +++ b/pkg/installpackage/installer_test.go @@ -193,7 +193,7 @@ func Test_installer_InstallPackages(t *testing.T) { //nolint:funlen } } downloader := download.NewDownloader(nil, download.NewHTTPDownloader(http.DefaultClient)) - ctrl := installpackage.New(d.param, downloader, d.rt, fs, linker, nil, &checksum.Calculator{}, unarchive.New(d.executor, fs), &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockCargoPackageInstaller{}) + ctrl := installpackage.New(d.param, downloader, d.rt, fs, linker, nil, &checksum.Calculator{}, unarchive.New(d.executor, fs), &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockGoBuildInstaller{}, &installpackage.MockCargoPackageInstaller{}) if err := ctrl.InstallPackages(ctx, logE, &installpackage.ParamInstallPackages{ Config: d.cfg, Registries: d.registries, @@ -269,7 +269,7 @@ func Test_installer_InstallPackage(t *testing.T) { //nolint:funlen t.Fatal(err) } downloader := download.NewDownloader(nil, download.NewHTTPDownloader(http.DefaultClient)) - ctrl := installpackage.New(d.param, downloader, d.rt, fs, nil, nil, &checksum.Calculator{}, unarchive.New(d.executor, fs), &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockCargoPackageInstaller{}) + ctrl := installpackage.New(d.param, downloader, d.rt, fs, nil, nil, &checksum.Calculator{}, unarchive.New(d.executor, fs), &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockGoBuildInstaller{}, &installpackage.MockCargoPackageInstaller{}) if err := ctrl.InstallPackage(ctx, logE, &installpackage.ParamInstallPackage{ Pkg: d.pkg, }); err != nil { diff --git a/pkg/installpackage/proxy_test.go b/pkg/installpackage/proxy_test.go index c3f281d8f..afb3bdb21 100644 --- a/pkg/installpackage/proxy_test.go +++ b/pkg/installpackage/proxy_test.go @@ -65,7 +65,7 @@ func Test_installer_InstallProxy(t *testing.T) { } } downloader := download.NewDownloader(nil, download.NewHTTPDownloader(http.DefaultClient)) - ctrl := installpackage.New(d.param, downloader, d.rt, fs, linker, nil, &checksum.Calculator{}, unarchive.New(d.executor, fs), &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockCargoPackageInstaller{}) + ctrl := installpackage.New(d.param, downloader, d.rt, fs, linker, nil, &checksum.Calculator{}, unarchive.New(d.executor, fs), &policy.Checker{}, &cosign.MockVerifier{}, &slsa.MockVerifier{}, &installpackage.MockGoInstallInstaller{}, &installpackage.MockGoBuildInstaller{}, &installpackage.MockCargoPackageInstaller{}) if err := ctrl.InstallProxy(ctx, logE); err != nil { if d.isErr { return diff --git a/pkg/installpackage/public.go b/pkg/installpackage/public.go index 3fe545324..e05ff85da 100644 --- a/pkg/installpackage/public.go +++ b/pkg/installpackage/public.go @@ -2,6 +2,7 @@ package installpackage import ( "context" + "os/exec" ) type Executor interface { @@ -9,5 +10,6 @@ type Executor interface { HdiutilDetach(ctx context.Context, mountPath string) (int, error) UnarchivePkg(ctx context.Context, pkgFilePath, dest string) (int, error) Exec(ctx context.Context, exePath string, args ...string) (int, error) + ExecCommand(cmd *exec.Cmd) (int, error) ExecWithEnvs(ctx context.Context, exePath string, args, envs []string) (int, error) } diff --git a/tests/main/aqua-global.yaml b/tests/main/aqua-global.yaml index 76db4367f..ecc091fd6 100644 --- a/tests/main/aqua-global.yaml +++ b/tests/main/aqua-global.yaml @@ -45,3 +45,5 @@ packages: registry: local - name: crates.io/skim@0.10.4 registry: local +- name: suzuki-shunsuke/github-comment@v4.0.0 + registry: local diff --git a/tests/main/registry.yaml b/tests/main/registry.yaml index 1a3f5502f..4cd77a5f4 100644 --- a/tests/main/registry.yaml +++ b/tests/main/registry.yaml @@ -144,3 +144,11 @@ packages: crate: skim files: - name: sk + + - type: go_build + repo_owner: suzuki-shunsuke + repo_name: github-comment + files: + - name: github-comment + src: ./cmd/github-comment + dir: github-comment-{{trimV .Version}}