Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions cliv2/cmd/cliv2/behavior/maperrortoexitcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package behavior

import (
"github.com/snyk/error-catalog-golang-public/aibom"
"github.com/snyk/error-catalog-golang-public/cli"
"github.com/snyk/error-catalog-golang-public/code"
"github.com/snyk/error-catalog-golang-public/snyk"
"github.com/snyk/error-catalog-golang-public/snyk_errors"
Expand All @@ -14,9 +15,10 @@ var MapErrorCatalogToExitCode func(err *snyk_errors.Error, defaultValue int) int
// mapErrorToExitCode maps error catalog errors to exit codes. Please extend the switch statement if new error codes need to be mapped.
func mapErrorToExitCode(err *snyk_errors.Error, defaultValue int) int {
var errorCatalogToExitCodeMap = map[string]int{
code.NewUnsupportedProjectError("").ErrorCode: constants.SNYK_EXIT_CODE_UNSUPPORTED_PROJECTS,
aibom.NewNoSupportedFilesError("").ErrorCode: constants.SNYK_EXIT_CODE_UNSUPPORTED_PROJECTS,
snyk.NewMaintenanceWindowError("").ErrorCode: constants.SNYK_EXIT_CODE_EX_TEMPFAIL,
code.NewUnsupportedProjectError("").ErrorCode: constants.SNYK_EXIT_CODE_UNSUPPORTED_PROJECTS,
aibom.NewNoSupportedFilesError("").ErrorCode: constants.SNYK_EXIT_CODE_UNSUPPORTED_PROJECTS,
cli.NewNoSupportedFilesFoundError("").ErrorCode: constants.SNYK_EXIT_CODE_UNSUPPORTED_PROJECTS,
snyk.NewMaintenanceWindowError("").ErrorCode: constants.SNYK_EXIT_CODE_EX_TEMPFAIL,
// Add new mappings here
}

Expand Down
5 changes: 5 additions & 0 deletions cliv2/internal/cliv2/cliv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

cli_errors "github.com/snyk/cli/cliv2/internal/errors"

"github.com/snyk/cli/cliv2/cmd/cliv2/behavior"
"github.com/snyk/cli/cliv2/internal/constants"
debug_utils "github.com/snyk/cli/cliv2/internal/debug"
"github.com/snyk/cli/cliv2/internal/embedded"
Expand Down Expand Up @@ -566,6 +567,7 @@ func DeriveExitCode(err error) int {
if err != nil {
var exitError *exec.ExitError
var errorWithExitCode *cli_errors.ErrorWithExitCode
var snykErr snyk_errors.Error

if errors.As(err, &exitError) {
returnCode = exitError.ExitCode()
Expand All @@ -575,6 +577,8 @@ func DeriveExitCode(err error) int {
}
} else if errors.As(err, &errorWithExitCode) {
returnCode = errorWithExitCode.ExitCode
} else if errors.As(err, &snykErr) {
returnCode = behavior.MapErrorCatalogToExitCode(&snykErr, constants.SNYK_EXIT_CODE_ERROR)
} else {
// got an error but it's not an ExitError
returnCode = constants.SNYK_EXIT_CODE_ERROR
Expand All @@ -583,6 +587,7 @@ func DeriveExitCode(err error) int {
return returnCode
}


func (e EnvironmentWarning) Error() string {
return e.message
}
Expand Down
4 changes: 4 additions & 0 deletions cliv2/internal/cliv2/cliv2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"testing"
"time"

"github.com/snyk/error-catalog-golang-public/code"
"github.com/snyk/error-catalog-golang-public/snyk_errors"
"github.com/snyk/go-application-framework/pkg/app"
"github.com/snyk/go-application-framework/pkg/configuration"
Expand Down Expand Up @@ -604,6 +605,9 @@ func TestDeriveExitCode(t *testing.T) {
{name: "no error", err: nil, expected: constants.SNYK_EXIT_CODE_OK},
{name: "error with exit code", err: &cli_errors.ErrorWithExitCode{ExitCode: 42}, expected: 42},
{name: "other error", err: errors.New("some other error"), expected: constants.SNYK_EXIT_CODE_ERROR},
{name: "snyk_errors.Error with mapped code", err: snyk_errors.Error{ErrorCode: code.NewUnsupportedProjectError("").ErrorCode}, expected: constants.SNYK_EXIT_CODE_UNSUPPORTED_PROJECTS},
{name: "snyk_errors.Error with unmapped code", err: snyk_errors.Error{ErrorCode: "SNYK-UNKNOWN-9999"}, expected: constants.SNYK_EXIT_CODE_ERROR},
{name: "wrapped snyk_errors.Error", err: fmt.Errorf("wrap: %w", snyk_errors.Error{ErrorCode: code.NewUnsupportedProjectError("").ErrorCode}), expected: constants.SNYK_EXIT_CODE_UNSUPPORTED_PROJECTS},
}

for _, tc := range tests {
Expand Down
3 changes: 1 addition & 2 deletions src/cli/commands/monitor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,7 @@ export default async function monitor(...args0: MethodArgs): Promise<any> {
const verboseEnabled =
args.includes('-Dverbose') ||
args.includes('-Dverbose=true') ||
!!options['print-graph'] ||
!!options['print-output-jsonl-with-errors'];
!!options['print-graph'];
if (verboseEnabled) {
enableMavenDverboseExhaustiveDeps = (await hasFeatureFlag(
MAVEN_DVERBOSE_EXHAUSTIVE_DEPS_FF,
Expand Down
7 changes: 4 additions & 3 deletions src/lib/ecosystems/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
assembleQueryString,
printDepGraph,
printDepGraphJsonl,
shouldPrintDepGraph,
shouldPrintGraph,
isJsonl,
} from '../snyk-test/common';
import { getAuthHeader } from '../api-token';
import { resolveAndTestFacts } from './resolve-test-facts';
Expand Down Expand Up @@ -57,7 +58,7 @@ export async function testEcosystem(

if (
isUnmanagedEcosystem(ecosystem) &&
(shouldPrintDepGraph(options) || options['print-output-jsonl-with-errors'])
shouldPrintGraph(options)
) {
const [target] = paths;
return printUnmanagedDepGraph(results, target, process.stdout, options);
Expand Down Expand Up @@ -108,7 +109,7 @@ export async function printUnmanagedDepGraph(
const [result] = await getUnmanagedDepGraph(results);
const depGraph = convertDepGraph(result);

if (options['print-output-jsonl-with-errors']) {
if (isJsonl(options)) {
await printDepGraphJsonl(
depGraph,
target,
Expand Down
3 changes: 2 additions & 1 deletion src/lib/monitor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,8 @@ export async function monitorDepGraph(
analytics.add('targetBranch', target.branch);
}

const pruneIsRequired = options.pruneRepeatedSubdependencies;
const pruneIsRequired =
options.pruneRepeatedSubdependencies || !!options['prune'];
depGraph = await pruneGraph(depGraph, packageManager, pruneIsRequired);

let callGraphPayload;
Expand Down
8 changes: 5 additions & 3 deletions src/lib/plugins/get-deps-from-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
detectPackageManagerFromFile,
} from '../detect';
import * as analytics from '../analytics';
import { shouldEmbedErrors } from '../snyk-test/common';
import { convertSingleResultToMultiCustom } from './convert-single-splugin-res-to-multi-custom';
import { convertMultiResultToMultiCustom } from './convert-multi-plugin-res-to-multi-custom';
import { processYarnWorkspaces } from './nodejs-plugin/yarn-workspaces-parser';
Expand Down Expand Up @@ -65,7 +66,7 @@ export async function getDepsFromPlugin(
);
if (targetFiles.length === 0) {
const error = NoSupportedManifestsFoundError([root]);
if (options['print-output-jsonl-with-errors']) {
if (shouldEmbedErrors(options)) {
return {
plugin: { name: 'custom-auto-detect' },
scannedProjects: [],
Expand Down Expand Up @@ -138,7 +139,7 @@ export async function getDepsFromPlugin(
}
if (!options.docker && !(options.file || options.packageManager)) {
const error = NoSupportedManifestsFoundError([root]);
if (options['print-output-jsonl-with-errors']) {
if (shouldEmbedErrors(options)) {
return {
plugin: { name: 'custom-auto-detect' },
scannedProjects: [],
Expand All @@ -149,6 +150,7 @@ export async function getDepsFromPlugin(
errMessage: error.userMessage,
},
],

} as MultiProjectResultCustom;
}
throw error;
Expand All @@ -158,7 +160,7 @@ export async function getDepsFromPlugin(
try {
inspectRes = await getSinglePluginResult(root, options, '', featureFlags);
} catch (error) {
if (options['print-output-jsonl-with-errors']) {
if (shouldEmbedErrors(options)) {
const errMessage =
error?.message ?? 'Something went wrong getting dependencies';
debug(
Expand Down
3 changes: 2 additions & 1 deletion src/lib/plugins/get-multi-plugin-result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { convertMultiResultToMultiCustom } from './convert-multi-plugin-res-to-m
import { PluginMetadata } from '@snyk/cli-interface/legacy/plugin';
import { CallGraph } from '@snyk/cli-interface/legacy/common';
import { errorMessageWithRetry, FailedToRunTestError } from '../errors';
import { shouldEmbedErrors } from '../snyk-test/common';
import { processYarnWorkspaces } from './nodejs-plugin/yarn-workspaces-parser';
import { processNpmWorkspaces } from './nodejs-plugin/npm-workspaces-parser';
import { processPnpmWorkspaces } from 'snyk-nodejs-plugin';
Expand Down Expand Up @@ -203,7 +204,7 @@ export async function getMultiPluginResult(
}

if (!allResults.length) {
if (options['print-output-jsonl-with-errors']) {
if (shouldEmbedErrors(options)) {
return {
plugin: {
name: 'custom-auto-detect',
Expand Down
85 changes: 64 additions & 21 deletions src/lib/snyk-test/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ContainerTarget, GitTarget } from '../project-metadata/types';
import { CLI, ProblemError } from '@snyk/error-catalog-nodejs-public';
import { CustomError } from '../errors';
import { FailedProjectScanError } from '../plugins/get-multi-plugin-result';
import * as analytics from '../analytics';

/**
* Determines workspace information from the plugin name and scanned project metadata.
Expand Down Expand Up @@ -132,8 +133,67 @@ export async function printDepGraph(
});
}

export function shouldPrintDepGraph(opts: Options): boolean {
return opts['print-graph'] && !opts['print-deps'];
// PHASE 2: --jsonl will be removed once all consumers use the dep-graph router
// directly. At that point, JSONL will be the only print-graph output format.
export function isJsonl(opts: Options): boolean {
return !!opts['jsonl'];
}

export function shouldEmbedErrors(opts: Options): boolean {
return !!opts['embed-errors'];
}

export function shouldPrintGraph(opts: Options): boolean {
return !opts['print-deps'] && (
!!opts['print-graph'] ||
!!opts['print-effective-graph'] ||
!!opts['print-effective-graph-with-errors'] ||
!!opts['print-output-jsonl-with-errors']
);
}

// DEPRECATION: The legacy flag mappings below exist for backward compatibility.
// Once analytics confirm no usage of the old flags, remove the legacyMappings
// table and the deprecation warnings.
export function mapLegacyGraphFlags(opts: Options): void {
// --prune implies --jsonl (pruned output is always JSONL)
if (opts['prune']) {
opts['jsonl'] = true;
opts['print-graph'] = true;
}

// New-style --jsonl or --prune: always embed errors.
// Consumers of the new model are expected to handle embedded errors.
if (opts['jsonl']) {
opts['print-graph'] = true;
opts['embed-errors'] = true;
return;
}

const legacyMappings: Array<{ flag: keyof Options; prune: boolean; embedErrors: boolean }> = [
{ flag: 'print-effective-graph', prune: true, embedErrors: false },
{ flag: 'print-effective-graph-with-errors', prune: true, embedErrors: true },
{ flag: 'print-output-jsonl-with-errors', prune: false, embedErrors: true },
];

for (const { flag, prune, embedErrors } of legacyMappings) {
if (opts[flag]) {
const replacement = prune ? '--print-graph --prune' : '--print-graph --jsonl';
process.stderr.write(
`WARNING: --${flag} is deprecated. Use ${replacement} instead.\n`,
);
analytics.add('deprecatedLegacyDepGraphFlag', flag);
opts['print-graph'] = true;
opts['jsonl'] = true;
if (prune) {
opts['prune'] = true;
}
if (embedErrors) {
opts['embed-errors'] = true;
}
return;
}
}
}

/**
Expand Down Expand Up @@ -180,8 +240,8 @@ export async function printDepGraphError(
return new Promise((res, rej) => {
// Normalize the target file path to be relative to root, consistent with printDepGraphJsonl
const normalisedTargetFile = failedProjectScanError.targetFile
? path.relative(root, failedProjectScanError.targetFile)
: failedProjectScanError.targetFile;
? path.relative(root, path.resolve(root, failedProjectScanError.targetFile))
: failedProjectScanError.targetFile;

const problemError = getOrCreateErrorCatalogError(failedProjectScanError);
const serializedError = problemError.toJsonApi().body();
Expand All @@ -198,23 +258,6 @@ export async function printDepGraphError(
});
}

/**
* Checks if either --print-effective-graph or --print-effective-graph-with-errors is set.
*/
export function shouldPrintEffectiveDepGraph(opts: Options): boolean {
return (
!!opts['print-effective-graph'] ||
shouldPrintEffectiveDepGraphWithErrors(opts)
);
}

/**
* shouldPrintEffectiveDepGraphWithErrors checks if the --print-effective-graph-with-errors flag is set.
* This is used to determine if the effective dep-graph with errors should be printed.
*/
export function shouldPrintEffectiveDepGraphWithErrors(opts: Options): boolean {
return !!opts['print-effective-graph-with-errors'];
}

/**
* getOrCreateErrorCatalogError returns a ProblemError instance for consistent error catalog usage.
Expand Down
9 changes: 5 additions & 4 deletions src/lib/snyk-test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const {
DISABLE_GO_PACKAGE_URLS_IN_CLI_FEATURE_FLAG,
} = require('../package-managers');
const { getOrganizationID } = require('../organization');
const { printDepGraphError } = require('./common');
const { printDepGraphError, mapLegacyGraphFlags, shouldEmbedErrors } = require('./common');
const debug = require('debug')('snyk-test');

async function test(root, options, callback) {
Expand All @@ -42,6 +42,8 @@ async function test(root, options, callback) {
}

async function executeTest(root, options) {
mapLegacyGraphFlags(options);

const includeGoStandardLibraryDeps = await hasFeatureFlagOrDefault(
INCLUDE_GO_STANDARD_LIBRARY_DEPS_FEATURE_FLAG,
options,
Expand All @@ -57,8 +59,7 @@ async function executeTest(root, options) {
const verboseEnabled =
args.includes('-Dverbose') ||
args.includes('-Dverbose=true') ||
!!options['print-graph'] ||
!!options['print-output-jsonl-with-errors'];
(!!options['print-graph'] && !options['prune']);
if (verboseEnabled) {
enableMavenDverboseExhaustiveDeps = await hasFeatureFlag(
MAVEN_DVERBOSE_EXHAUSTIVE_DEPS_FF,
Expand Down Expand Up @@ -117,7 +118,7 @@ async function executeTest(root, options) {
featureFlags,
);
} catch (error) {
if (options['print-output-jsonl-with-errors']) {
if (shouldEmbedErrors(options)) {
await printDepGraphError(
root,
{
Expand Down
Loading
Loading