diff --git a/json-schema/aqua-yaml.json b/json-schema/aqua-yaml.json index f00e55917..7145b0dc5 100644 --- a/json-schema/aqua-yaml.json +++ b/json-schema/aqua-yaml.json @@ -77,6 +77,9 @@ }, "go_version_file": { "type": "string" + }, + "vars": { + "type": "object" } }, "additionalProperties": false, diff --git a/json-schema/registry.json b/json-schema/registry.json index 4225edc52..e3020d284 100644 --- a/json-schema/registry.json +++ b/json-schema/registry.json @@ -371,6 +371,12 @@ "minisign": { "$ref": "#/$defs/Minisign" }, + "vars": { + "items": { + "$ref": "#/$defs/Var" + }, + "type": "array" + }, "envs": { "$ref": "#/$defs/SupportedEnvs" } @@ -522,6 +528,12 @@ "minisign": { "$ref": "#/$defs/Minisign" }, + "vars": { + "items": { + "$ref": "#/$defs/Var" + }, + "type": "array" + }, "version_constraint": { "type": "string" }, @@ -615,6 +627,22 @@ }, "type": "array" }, + "Var": { + "properties": { + "name": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "default": true + }, + "additionalProperties": false, + "type": "object", + "required": [ + "name" + ] + }, "VersionOverride": { "properties": { "version_constraint": { @@ -719,6 +747,12 @@ "build": { "$ref": "#/$defs/Build" }, + "vars": { + "items": { + "$ref": "#/$defs/Var" + }, + "type": "array" + }, "overrides": { "$ref": "#/$defs/Overrides" }, diff --git a/pkg/config/aqua/config.go b/pkg/config/aqua/config.go index 6598208ab..b0be57e81 100644 --- a/pkg/config/aqua/config.go +++ b/pkg/config/aqua/config.go @@ -7,16 +7,17 @@ import ( ) type Package struct { - Name string `validate:"required" json:"name,omitempty"` - Registry string `validate:"required" yaml:",omitempty" json:"registry,omitempty" jsonschema:"description=Registry name,example=foo,example=local,default=standard"` - Version string `validate:"required" yaml:",omitempty" json:"version,omitempty"` - Import string `yaml:",omitempty" json:"import,omitempty"` - Tags []string `yaml:",omitempty" json:"tags,omitempty"` - Description string `yaml:",omitempty" json:"description,omitempty"` - Link string `yaml:",omitempty" json:"link,omitempty"` - Update *Update `yaml:",omitempty" json:"update,omitempty"` - FilePath string `json:"-" yaml:"-"` - GoVersionFile string `json:"go_version_file,omitempty" yaml:"go_version_file,omitempty"` + Name string `validate:"required" json:"name,omitempty"` + Registry string `validate:"required" yaml:",omitempty" json:"registry,omitempty" jsonschema:"description=Registry name,example=foo,example=local,default=standard"` + Version string `validate:"required" yaml:",omitempty" json:"version,omitempty"` + Import string `yaml:",omitempty" json:"import,omitempty"` + Tags []string `yaml:",omitempty" json:"tags,omitempty"` + Description string `yaml:",omitempty" json:"description,omitempty"` + Link string `yaml:",omitempty" json:"link,omitempty"` + Update *Update `yaml:",omitempty" json:"update,omitempty"` + FilePath string `json:"-" yaml:"-"` + GoVersionFile string `json:"go_version_file,omitempty" yaml:"go_version_file,omitempty"` + Vars map[string]any `json:"vars,omitempty" yaml:",omitempty"` } type Update struct { diff --git a/pkg/config/extract.go b/pkg/config/extract.go index e6ddc59a3..4c7e962e4 100644 --- a/pkg/config/extract.go +++ b/pkg/config/extract.go @@ -84,44 +84,55 @@ func ListPackages(logE *logrus.Entry, cfg *aqua.Config, rt *runtime.Runtime, reg "package_version": pkg.Version, "registry": pkg.Registry, }) - rgst, ok := cfg.Registries[pkg.Registry] - if ok { - if rgst.Ref != "" { - logE = logE.WithField("registry_ref", rgst.Ref) - } - } - pkgInfo, err := getPkgInfoFromRegistries(logE, registries, pkg, m) - if err != nil { - logerr.WithError(logE, err).Error("install the package") - failed = true - continue - } - - pkgInfo, err = pkgInfo.Override(logE, pkg.Version, rt) + p, err := listPackage(logE, cfg, rt, registries, pkg, m, env) if err != nil { - logerr.WithError(logE, err).Error("evaluate version constraints") + logerr.WithError(logE, err).Error("ignore a package because the package version is empty") failed = true continue } - supported, err := pkgInfo.CheckSupported(rt, env) - if err != nil { - logerr.WithError(logE, err).Error("check if the package is supported") - failed = true + if p == nil { continue } - if !supported { - logE.Debug("the package isn't supported on this environment") - continue - } - pkgs = append(pkgs, &Package{ - Package: pkg, - PackageInfo: pkgInfo, - Registry: rgst, - }) + pkgs = append(pkgs, p) } return pkgs, failed } +func listPackage(logE *logrus.Entry, cfg *aqua.Config, rt *runtime.Runtime, registries map[string]*registry.Config, pkg *aqua.Package, m map[string]map[string]*registry.PackageInfo, env string) (*Package, error) { + rgst, ok := cfg.Registries[pkg.Registry] + if ok { + if rgst.Ref != "" { + logE = logE.WithField("registry_ref", rgst.Ref) + } + } + pkgInfo, err := getPkgInfoFromRegistries(logE, registries, pkg, m) + if err != nil { + return nil, errors.New("install the package") + } + + pkgInfo, err = pkgInfo.Override(logE, pkg.Version, rt) + if err != nil { + return nil, errors.New("evaluate version constraints") + } + supported, err := pkgInfo.CheckSupported(rt, env) + if err != nil { + return nil, errors.New("check if the package is supported") + } + if !supported { + logE.Debug("the package isn't supported on this environment") + return nil, nil //nolint:nilnil + } + p := &Package{ + Package: pkg, + PackageInfo: pkgInfo, + Registry: rgst, + } + if err := p.ApplyVars(); err != nil { + return nil, errors.New("apply the package variable") + } + return p, nil +} + func getPkgInfoFromRegistries(logE *logrus.Entry, registries map[string]*registry.Config, pkg *aqua.Package, m map[string]map[string]*registry.PackageInfo) (*registry.PackageInfo, error) { pkgInfoMap, ok := m[pkg.Registry] if !ok { diff --git a/pkg/config/package.go b/pkg/config/package.go index 6cccbbca3..68aae007b 100644 --- a/pkg/config/package.go +++ b/pkg/config/package.go @@ -1,6 +1,7 @@ package config import ( + "errors" "fmt" "net/url" "path" @@ -14,6 +15,8 @@ import ( "github.com/aquaproj/aqua/v2/pkg/runtime" "github.com/aquaproj/aqua/v2/pkg/template" "github.com/aquaproj/aqua/v2/pkg/unarchive" + "github.com/sirupsen/logrus" + "github.com/suzuki-shunsuke/logrus-error/logerr" ) type Package struct { @@ -68,6 +71,7 @@ func (p *Package) TemplateArtifact(rt *runtime.Runtime, asset string) *template. Arch: getArch(pkgInfo.Rosetta2, pkgInfo.WindowsARMEmulation, pkgInfo.Replacements, rt), Format: pkgInfo.GetFormat(), Asset: asset, + Vars: pkg.Vars, } } @@ -344,6 +348,7 @@ func (p *Package) renderChecksumFile(asset string, rt *runtime.Runtime) (string, "Arch": getArch(pkgInfo.Rosetta2, pkgInfo.WindowsARMEmulation, replacements, rt), "Format": pkgInfo.GetFormat(), "Asset": asset, + "Vars": pkg.Vars, }) if err != nil { return "", fmt.Errorf("render a template: %w", err) @@ -362,6 +367,7 @@ func (p *Package) renderTemplate(tpl *texttemplate.Template, rt *runtime.Runtime "OS": replace(rt.GOOS, pkgInfo.Replacements), "Arch": getArch(pkgInfo.Rosetta2, pkgInfo.WindowsARMEmulation, pkgInfo.Replacements, rt), "Format": pkgInfo.GetFormat(), + "Vars": pkg.Vars, }) if err != nil { return "", fmt.Errorf("render a template: %w", err) @@ -390,5 +396,43 @@ func (p *Package) RenderDir(file *registry.File, rt *runtime.Runtime) (string, e "Arch": getArch(pkgInfo.Rosetta2, pkgInfo.WindowsARMEmulation, pkgInfo.Replacements, rt), "Format": pkgInfo.GetFormat(), "FileName": file.Name, + "Vars": pkg.Vars, }) } + +func (p *Package) ApplyVars() error { + if p.PackageInfo.Vars == nil { + return nil + } + for _, v := range p.PackageInfo.Vars { + if v.Name == "" { + return errors.New("a variable name is empty") + } + if err := p.applyVar(v); err != nil { + return fmt.Errorf("apply a variable: %w", logerr.WithFields(err, logrus.Fields{ + "var_name": v.Name, + })) + } + } + return nil +} + +func (p *Package) applyVar(v *registry.Var) error { + if _, ok := p.Package.Vars[v.Name]; ok { + return nil + } + if v.Default != nil { + if p.Package.Vars == nil { + p.Package.Vars = map[string]any{ + v.Name: v.Default, + } + return nil + } + p.Package.Vars[v.Name] = v.Default + return nil + } + if !v.Required { + return nil + } + return errors.New("a variable is required") +} diff --git a/pkg/config/registry/package_info.go b/pkg/config/registry/package_info.go index 93dc55194..71e534ca6 100644 --- a/pkg/config/registry/package_info.go +++ b/pkg/config/registry/package_info.go @@ -57,10 +57,17 @@ type PackageInfo struct { Cosign *Cosign `json:"cosign,omitempty"` SLSAProvenance *SLSAProvenance `json:"slsa_provenance,omitempty" yaml:"slsa_provenance,omitempty"` Minisign *Minisign `json:"minisign,omitempty" yaml:",omitempty"` + Vars []*Var `json:"vars,omitempty" yaml:",omitempty"` VersionConstraints string `yaml:"version_constraint,omitempty" json:"version_constraint,omitempty"` VersionOverrides []*VersionOverride `yaml:"version_overrides,omitempty" json:"version_overrides,omitempty"` } +type Var struct { + Name string `json:"name"` + Required bool `json:"required,omitempty"` + Default any `json:"default,omitempty"` +} + type Build struct { Enabled *bool `json:"enabled,omitempty" yaml:",omitempty"` Type string `json:"type,omitempty" yaml:",omitempty" jsonschema:"enum=go_install,enum=go_build"` @@ -115,6 +122,7 @@ type VersionOverride struct { SLSAProvenance *SLSAProvenance `json:"slsa_provenance,omitempty" yaml:"slsa_provenance,omitempty"` Minisign *Minisign `json:"minisign,omitempty" yaml:",omitempty"` Build *Build `json:"build,omitempty" yaml:",omitempty"` + Vars []*Var `json:"vars,omitempty" yaml:",omitempty"` Overrides Overrides `yaml:",omitempty" json:"overrides,omitempty"` SupportedEnvs SupportedEnvs `yaml:"supported_envs,omitempty" json:"supported_envs,omitempty"` } @@ -138,6 +146,7 @@ type Override struct { Cosign *Cosign `json:"cosign,omitempty"` SLSAProvenance *SLSAProvenance `json:"slsa_provenance,omitempty" yaml:"slsa_provenance,omitempty"` Minisign *Minisign `json:"minisign,omitempty" yaml:",omitempty"` + Vars []*Var `json:"vars,omitempty" yaml:",omitempty"` Envs SupportedEnvs `yaml:",omitempty" json:"envs,omitempty"` } @@ -179,6 +188,7 @@ func (p *PackageInfo) Copy() *PackageInfo { NoAsset: p.NoAsset, AppendExt: p.AppendExt, Build: p.Build, + Vars: p.Vars, } return pkg } @@ -249,7 +259,7 @@ func (p *PackageInfo) resetByPkgType(typ string) { //nolint:funlen } } -func (p *PackageInfo) overrideVersion(child *VersionOverride) *PackageInfo { //nolint:cyclop,funlen +func (p *PackageInfo) overrideVersion(child *VersionOverride) *PackageInfo { //nolint:cyclop,funlen,gocyclo pkg := p.Copy() if child.Type != "" { pkg.resetByPkgType(child.Type) @@ -339,6 +349,9 @@ func (p *PackageInfo) overrideVersion(child *VersionOverride) *PackageInfo { //n if child.Build != nil { pkg.Build = child.Build } + if child.Vars != nil { + pkg.Vars = child.Vars + } return pkg } @@ -428,6 +441,10 @@ func (p *PackageInfo) OverrideByRuntime(rt *runtime.Runtime) { //nolint:cyclop,f if ov.AppendExt != nil { p.AppendExt = ov.AppendExt } + + if ov.Vars != nil { + p.Vars = ov.Vars + } } func (p *PackageInfo) OverrideByBuild() { diff --git a/pkg/controller/which/which.go b/pkg/controller/which/which.go index 6a1521a44..8f169f5a7 100644 --- a/pkg/controller/which/which.go +++ b/pkg/controller/which/which.go @@ -95,7 +95,11 @@ func (c *Controller) findExecFile(ctx context.Context, logE *logrus.Entry, param return nil, err //nolint:wrapcheck } for _, pkg := range cfg.Packages { - if findResult := c.findExecFileFromPkg(registryContents, exeName, pkg, logE); findResult != nil { + findResult, err := c.findExecFileFromPkg(registryContents, exeName, pkg, logE) + if err != nil { + return nil, err + } + if findResult != nil { findResult.Config = cfg findResult.ConfigFilePath = cfgFilePath findResult.Package.Registry = cfg.Registries[pkg.Registry] @@ -105,10 +109,10 @@ func (c *Controller) findExecFile(ctx context.Context, logE *logrus.Entry, param return nil, nil //nolint:nilnil } -func (c *Controller) findExecFileFromPkg(registries map[string]*registry.Config, exeName string, pkg *aqua.Package, logE *logrus.Entry) *FindResult { //nolint:cyclop +func (c *Controller) findExecFileFromPkg(registries map[string]*registry.Config, exeName string, pkg *aqua.Package, logE *logrus.Entry) (*FindResult, error) { //nolint:cyclop if pkg.Registry == "" || pkg.Name == "" { logE.Debug("ignore a package because the package name or package registry name is empty") - return nil + return nil, nil //nolint:nilnil } logE = logE.WithFields(logrus.Fields{ "registry_name": pkg.Registry, @@ -117,7 +121,7 @@ func (c *Controller) findExecFileFromPkg(registries map[string]*registry.Config, registry, ok := registries[pkg.Registry] if !ok { logE.Warn("registry isn't found") - return nil + return nil, nil //nolint:nilnil } m := registry.PackageInfos.ToMap(logE) @@ -125,23 +129,23 @@ func (c *Controller) findExecFileFromPkg(registries map[string]*registry.Config, pkgInfo, ok := m[pkg.Name] if !ok { logE.Warn("package isn't found") - return nil + return nil, nil //nolint:nilnil } pkgInfo, err := pkgInfo.Override(logE, pkg.Version, c.runtime) if err != nil { logerr.WithError(logE, err).Warn("version constraint is invalid") - return nil + return nil, nil //nolint:nilnil } supported, err := pkgInfo.CheckSupported(c.runtime, c.runtime.GOOS+"/"+c.runtime.GOARCH) if err != nil { logerr.WithError(logE, err).Error("check if the package is supported") - return nil + return nil, nil //nolint:nilnil } if !supported { logE.Debug("the package isn't supported on this environment") - return nil + return nil, nil //nolint:nilnil } for _, file := range pkgInfo.GetFiles() { @@ -153,14 +157,17 @@ func (c *Controller) findExecFileFromPkg(registries map[string]*registry.Config, }, File: file, } + if err := findResult.Package.ApplyVars(); err != nil { + return nil, fmt.Errorf("apply package variables: %w", err) + } exePath, err := c.getExePath(findResult) if err != nil { logE.WithError(err).Error("get the execution file path") - return nil + return nil, nil //nolint:nilnil } findResult.ExePath = exePath - return findResult + return findResult, nil } } - return nil + return nil, nil //nolint:nilnil } diff --git a/pkg/controller/which/which_internal_test.go b/pkg/controller/which/which_internal_test.go index 41eecefa1..041015b32 100644 --- a/pkg/controller/which/which_internal_test.go +++ b/pkg/controller/which/which_internal_test.go @@ -81,7 +81,10 @@ func TestController_findExecFileFromPkg(t *testing.T) { //nolint:funlen for _, d := range data { t.Run(d.title, func(t *testing.T) { t.Parallel() - which := ctrl.findExecFileFromPkg(d.registries, d.exeName, d.pkg, logE) + which, err := ctrl.findExecFileFromPkg(d.registries, d.exeName, d.pkg, logE) + if err != nil { + t.Fatal(err) + } if diff := cmp.Diff(d.expWhich, which); diff != "" { t.Fatal(diff) } diff --git a/pkg/template/render.go b/pkg/template/render.go index dbc7d2880..1e9b74d19 100644 --- a/pkg/template/render.go +++ b/pkg/template/render.go @@ -14,6 +14,7 @@ type Artifact struct { Format string Asset string AssetWithoutExt string + Vars map[string]any } func renderParam(art *Artifact, rt *runtime.Runtime) map[string]interface{} { @@ -27,6 +28,7 @@ func renderParam(art *Artifact, rt *runtime.Runtime) map[string]interface{} { "Format": art.Format, "Asset": art.Asset, "AssetWithoutExt": art.AssetWithoutExt, + "Vars": art.Vars, } }