diff --git a/internal/runners/packages/import.go b/internal/runners/packages/import.go index 797425da04..10250f4734 100644 --- a/internal/runners/packages/import.go +++ b/internal/runners/packages/import.go @@ -1,8 +1,10 @@ package packages import ( + "errors" "fmt" "os" + "strings" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" @@ -19,6 +21,7 @@ import ( "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/localcommit" "github.com/ActiveState/cli/pkg/platform/api" + "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" "github.com/ActiveState/cli/pkg/platform/api/reqsimport" "github.com/ActiveState/cli/pkg/platform/model" @@ -29,6 +32,8 @@ const ( defaultImportFile = "requirements.txt" ) +var errImportSbomSolve = errs.New("failed to solve SBOM") + // Confirmer describes the behavior required to prompt a user for confirmation. type Confirmer interface { Confirm(title, msg string, defaultOpt *bool) (bool, error) @@ -143,7 +148,24 @@ func (i *Import) Run(params *ImportRunParams) (rerr error) { // Solve the runtime. rtCommit, err := bp.FetchCommit(stagedCommitId, proj.Owner(), proj.Name(), nil) if err != nil { - return errs.Wrap(err, "Failed to fetch build result for previous commit") + var buildplannerErr *response.BuildPlannerError + if errors.As(err, &buildplannerErr) { + // When importing CycloneDX and SPDX SBOMs, we put all packages in the 'private/' + // namespace, which will fail to solve. That is expected. However, we still want to update the + // local commit so the user can see what was imported, even if the runtime is not viable. + for _, verr := range buildplannerErr.ValidationErrors { + if strings.Contains(verr, "non-existent namespace: private/") { + if err := localcommit.Set(proj.Dir(), stagedCommitId.String()); err != nil { + return locale.WrapError(err, "err_package_update_commit_id") + } + return errImportSbomSolve + } + } + } + + if err != nil { + return errs.Wrap(err, "Failed to fetch build result for staged commit") + } } // Output change summary. diff --git a/internal/runners/packages/rationalize.go b/internal/runners/packages/rationalize.go index b96ba8a285..e2364b4fea 100644 --- a/internal/runners/packages/rationalize.go +++ b/internal/runners/packages/rationalize.go @@ -71,5 +71,11 @@ func rationalizeError(auth *authentication.Auth, err *error) { locale.Tl("err_import_unauthenticated", "Could not import requirements into a private namespace because you are not authenticated. Please authenticate using '[ACTIONABLE]state auth[/RESET]' and try again."), errs.SetInput(), ) + + case errors.Is(*err, errImportSbomSolve): + *err = errs.WrapUserFacing(*err, + locale.Tl("err_import_sbom_solve", "Import finished, but your runtime could not be created because the SBOM's requirements do not exist on the Platform."), + errs.SetInput(), + ) } } diff --git a/test/integration/import_int_test.go b/test/integration/import_int_test.go index c267052808..5aded08886 100644 --- a/test/integration/import_int_test.go +++ b/test/integration/import_int_test.go @@ -158,6 +158,9 @@ func (suite *ImportIntegrationTestSuite) TestImportCycloneDx() { ts.PrepareEmptyProject() + cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") + cp.ExpectExitCode(0) + jsonSbom := filepath.Join(osutil.GetTestDataDir(), "import", "cyclonedx", "bom.json") xmlSbom := filepath.Join(osutil.GetTestDataDir(), "import", "cyclonedx", "bom.xml") @@ -193,9 +196,12 @@ func (suite *ImportIntegrationTestSuite) TestImportSpdx() { ts.PrepareEmptyProject() + cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") + cp.ExpectExitCode(0) + jsonSbom := filepath.Join(osutil.GetTestDataDir(), "import", "spdx", "appbomination.spdx.json") - cp := ts.Spawn("import", jsonSbom) + cp = ts.Spawn("import", jsonSbom) cp.Expect("Creating commit") cp.Expect("Done") cp.ExpectNotExitCode(0) // solve should fail due to private namespace