Skip to content
Draft
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
10 changes: 10 additions & 0 deletions help/cli-commands/sbom.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ Optional. Use for monorepos and directories with multiple projects or manifest f

Auto-detect all projects in the working directory (including Yarn workspaces) and generate a single SBOM based on their contents.

### `--fail-fast=true|false`

Use with `--all-projects` to control whether SBOM generation continues when errors occur.

**Default:** `true` (fail-fast behavior)

- `--fail-fast=true` or `--fail-fast` (default): When errors occur, SBOM generation is interrupted immediately. The exit code is 2 and the scan ends. No SBOM is generated for projects that had errors.

- `--fail-fast=false`: Continue processing all projects even when some fail. All successful projects will be included in the SBOM, and errors for failed projects will be reported at the end.

### `[--name=<NAME>]`

Use in combination with `--all-projects` to provide the name of the software which the SBOM describes. If not specified, this defaults to the name of the current working directory.
Expand Down
8 changes: 8 additions & 0 deletions src/cli/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,14 @@ export function args(rawArgv: string[]): Args {
}
}

if (argv['fail-fast'] !== undefined) {
if (argv['fail-fast'] === 'false' || argv['fail-fast'] === false) {
argv['fail-fast'] = false;
} else {
argv['fail-fast'] = true;
}
}

// Alias
const aliases = {
gradleSubProject: 'subProject',
Expand Down
54 changes: 54 additions & 0 deletions test/jest/acceptance/snyk-sbom/all-projects.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createProjectFromWorkspace } from '../../util/createProject';
import { createProject } from '../../util/createProject';
import { runSnykCLI } from '../../util/runSnykCLI';
import { fakeServer } from '../../../acceptance/fake-server';
import { getAvailableServerPort } from '../../util/getServerPort';
Expand Down Expand Up @@ -113,4 +114,57 @@ describe('snyk sbom --all-projects (mocked server only)', () => {
);
expect(bom.components).toHaveLength(37);
});

describe('--fail-fast option', () => {
test('`sbom --all-projects` with projects that have errors exits with code 2 (fail-fast by default)', async () => {
server.setFeatureFlag('enableMavenDverboseExhaustiveDeps', false);
const project = await createProject(
'snyk-test-all-projects-exit-codes/project-with-no-issues-and-project-with-error',
);

const { code, stderr } = await runSnykCLI(
`sbom --org aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee --format cyclonedx1.4+json --debug --all-projects`,
{
cwd: project.path(),
env,
},
);

// With default fail-fast behavior, should exit on first error
expect(code).toEqual(2);
// Should indicate that processing was interrupted
expect(stderr).toBeTruthy();
});

test('`sbom --all-projects --fail-fast=false` with projects that have errors continues processing', async () => {
server.setFeatureFlag('enableMavenDverboseExhaustiveDeps', false);
const project = await createProject(
'snyk-test-all-projects-exit-codes/project-with-no-issues-and-project-with-error',
);

const { code, stdout, stderr } = await runSnykCLI(
`sbom --org aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee --format cyclonedx1.4+json --debug --all-projects --fail-fast=false`,
{
cwd: project.path(),
env,
},
);

// With --fail-fast=false, should continue processing even if some projects fail
// If at least one project succeeds, should generate SBOM
if (code === 0) {
// Success case: at least one project succeeded, SBOM was generated
let bom;
expect(() => {
bom = JSON.parse(stdout);
}).not.toThrow();
expect(bom).toBeDefined();
} else {
// Error case: all projects failed, but we tried all of them
expect(code).toBeGreaterThan(0);
}
// Should indicate that errors occurred but processing continued
expect(stderr).toBeTruthy();
});
});
});