github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/internal/runners/manifest/manifest.go (about) 1 package manifest 2 3 import ( 4 "os" 5 "strings" 6 7 "github.com/ActiveState/cli/internal/analytics" 8 "github.com/ActiveState/cli/internal/config" 9 "github.com/ActiveState/cli/internal/constants" 10 "github.com/ActiveState/cli/internal/errs" 11 "github.com/ActiveState/cli/internal/locale" 12 "github.com/ActiveState/cli/internal/output" 13 "github.com/ActiveState/cli/internal/runbits/rationalize" 14 "github.com/ActiveState/cli/pkg/buildplan" 15 "github.com/ActiveState/cli/pkg/localcommit" 16 "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" 17 "github.com/ActiveState/cli/pkg/platform/api/vulnerabilities/request" 18 "github.com/ActiveState/cli/pkg/platform/authentication" 19 "github.com/ActiveState/cli/pkg/platform/model" 20 "github.com/ActiveState/cli/pkg/platform/model/buildplanner" 21 "github.com/ActiveState/cli/pkg/platform/runtime" 22 "github.com/ActiveState/cli/pkg/platform/runtime/target" 23 "github.com/ActiveState/cli/pkg/project" 24 ) 25 26 type primeable interface { 27 Output() output.Outputer 28 Project() *project.Project 29 Auth() *authentication.Auth 30 Analytics() analytics.Dispatcher 31 SvcModel() *model.SvcModel 32 Config() *config.Instance 33 } 34 35 type Manifest struct { 36 out output.Outputer 37 project *project.Project 38 auth *authentication.Auth 39 analytics analytics.Dispatcher 40 svcModel *model.SvcModel 41 cfg *config.Instance 42 } 43 44 func NewManifest(prime primeable) *Manifest { 45 return &Manifest{ 46 out: prime.Output(), 47 project: prime.Project(), 48 auth: prime.Auth(), 49 analytics: prime.Analytics(), 50 svcModel: prime.SvcModel(), 51 cfg: prime.Config(), 52 } 53 } 54 55 func (m *Manifest) Run() (rerr error) { 56 defer rationalizeError(&rerr) 57 58 if m.project == nil { 59 return rationalize.ErrNoProject 60 } 61 62 m.out.Notice(locale.Tl("manifest_operating_on_project", "Operating on project: [ACTIONABLE]{{.V0}}[/RESET], located at [ACTIONABLE]{{.V1}}[/RESET]\n", m.project.Namespace().String(), m.project.Dir())) 63 64 reqs, err := m.fetchRequirements() 65 if err != nil { 66 return errs.Wrap(err, "Could not fetch requirements") 67 } 68 69 bpReqs, err := m.fetchBuildplanRequirements() 70 if err != nil { 71 return errs.Wrap(err, "Could not fetch artifacts") 72 } 73 74 vulns, err := m.fetchVulnerabilities(reqs) 75 if err != nil { 76 return errs.Wrap(err, "Could not fetch vulnerabilities") 77 } 78 79 m.out.Print(newRequirements(reqs, bpReqs, vulns)) 80 81 if len(vulns) > 0 { 82 m.out.Notice(locale.Tl("manifest_vulnerabilities_info", "\nFor CVE info run '[ACTIONABLE]state security[/RESET]'")) 83 } 84 85 return nil 86 } 87 88 func (m *Manifest) fetchRequirements() ([]types.Requirement, error) { 89 commitID, err := localcommit.Get(m.project.Dir()) 90 if err != nil { 91 return nil, errs.Wrap(err, "Could not get commit ID") 92 } 93 94 bp := buildplanner.NewBuildPlannerModel(m.auth) 95 expr, _, err := bp.GetBuildExpressionAndTime(commitID.String()) 96 if err != nil { 97 return nil, errs.Wrap(err, "Could not get remote build expr and time") 98 } 99 100 reqs, err := expr.Requirements() 101 if err != nil { 102 return nil, errs.Wrap(err, "Could not get requirements") 103 } 104 105 return reqs, nil 106 } 107 108 func (m *Manifest) fetchBuildplanRequirements() (buildplan.Ingredients, error) { 109 if strings.EqualFold(os.Getenv(constants.DisableRuntime), "true") { 110 return nil, nil 111 } 112 113 target := target.NewProjectTarget(m.project, nil, target.TriggerManifest) 114 rt, err := runtime.New(target, m.analytics, m.svcModel, m.auth, m.cfg, m.out) 115 if err != nil { 116 return nil, locale.WrapError(err, "err_packages_update_runtime_init", "Could not initialize runtime.") 117 } 118 119 if rt.NeedsUpdate() { 120 m.out.Notice(locale.T("manifest_runtime_needs_update")) 121 } 122 123 bp, err := rt.BuildPlan() 124 if err != nil { 125 return nil, errs.Wrap(err, "could not get build plan") 126 } 127 128 return bp.RequestedIngredients(), nil 129 } 130 131 func (m *Manifest) fetchVulnerabilities(reqs []types.Requirement) (vulnerabilities, error) { 132 vulns := make(vulnerabilities) 133 134 if !m.auth.Authenticated() { 135 for _, req := range reqs { 136 vulns.addVulnerability(req.Name, req.Namespace, &requirementVulnerabilities{ 137 authenticated: false, 138 }) 139 } 140 return vulns, nil 141 } 142 143 var ingredients []*request.Ingredient 144 for _, req := range reqs { 145 var version string 146 if req.VersionRequirement != nil { 147 version = model.BuildPlannerVersionConstraintsToString(req.VersionRequirement) 148 } 149 150 ingredients = append(ingredients, &request.Ingredient{ 151 Name: req.Name, 152 Namespace: req.Namespace, 153 Version: version, 154 }) 155 } 156 157 ingredientVulnerabilities, err := model.FetchVulnerabilitiesForIngredients(m.auth, ingredients) 158 if err != nil { 159 return nil, errs.Wrap(err, "Failed to fetch ingredient vulnerabilities") 160 } 161 162 for _, vuln := range ingredientVulnerabilities { 163 vulns.addVulnerability(vuln.Name, vuln.PrimaryNamespace, &requirementVulnerabilities{ 164 Count: vuln.Vulnerabilities.Count(), 165 authenticated: true, 166 }) 167 } 168 169 return vulns, nil 170 }