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  }