CM-67195: Add Bun package manager support to SCA local scans#478
CM-67195: Add Bun package manager support to SCA local scans#478arad-traud-cycode wants to merge 4 commits into
Conversation
Add a dedicated Bun restore handler so `cycode scan -t sca` correctly restores Bun project dependencies before scanning, mirroring the existing pnpm/yarn pattern. - New RestoreBunDependencies: detects Bun via bun.lock or a packageManager/engines bun signal; reads an existing bun.lock directly, otherwise runs `bun install --ignore-scripts` to generate it. - Enforce Bun >=1.2 (text bun.lock) before generating a lockfile; skip with a warning on older or missing Bun. - Register the handler ahead of the npm fallback and add bun.lock to npm's alternative-lockfile guard so npm does not claim Bun projects. - Add bun.lock to the SCA supported files and npm ecosystem map. - Unit tests for detection, version gating, restore, and cleanup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dencies Move the Bun-specific version check into the base class as a reusable, opt-in capability so the logic lives in one place instead of a single handler. - BaseRestoreDependencies gains parse_tool_version() and is_supported_tool_version(), enforced automatically right before the install command runs. - Handlers opt in by overriding get_version_command() and get_minimum_supported_version() (both default to None = no check), so every existing handler is unchanged. - RestoreBunDependencies now just declares ['bun', '--version'] and (1, 2); its bespoke _parse_bun_version/_is_supported_bun_version are removed. - Version-parsing and gate tests moved to the base-class test module. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ee443f1 to
e41b00e
Compare
Some tools print versions with a leading 'v' (e.g. `node --version` -> v20.1.0). Since parse_tool_version is now a shared base-class helper, make it strip an optional leading 'v' so a future opt-in handler isn't misparsed as "could not determine version". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| def is_supported_tool_version(self) -> bool: | ||
| """Verify the installed package manager meets get_minimum_supported_version(). | ||
|
|
||
| Returns True when no minimum is declared (the default for all handlers), so existing | ||
| handlers are unaffected. Only runs the version command for handlers that opt in. | ||
| """ | ||
| minimum_version = self.get_minimum_supported_version() | ||
| version_command = self.get_version_command() | ||
| if minimum_version is None or version_command is None: | ||
| return True | ||
|
|
||
| tool_name = version_command[0] | ||
| minimum_str = '.'.join(str(part) for part in minimum_version) | ||
|
|
||
| raw_version = shell(command=version_command, timeout=self.command_timeout, silent_exc_info=True) | ||
| version = parse_tool_version(raw_version) | ||
| if version is None: | ||
| logger.warning( | ||
| 'Could not determine %s version; %s+ is required to restore dependencies, %s', | ||
| tool_name, | ||
| minimum_str, | ||
| {'raw_version': raw_version}, | ||
| ) | ||
| return False | ||
| if version < minimum_version: | ||
| logger.warning( | ||
| 'Unsupported %s version; %s+ is required to restore dependencies, %s', | ||
| tool_name, | ||
| minimum_str, | ||
| {'detected_version': '.'.join(str(part) for part in version)}, | ||
| ) | ||
| return False | ||
| return True |
There was a problem hiding this comment.
Is this function relevant for anything other than Bun?
If not, I'd move to logic to _indicates_bun
There was a problem hiding this comment.
Good call — it's only used by Bun. Moved the version check back into restore_bun_dependencies.py (_parse_bun_version + _is_supported_bun_version) and reverted BaseRestoreDependencies to its original form. (f4ad542)
| # The minimum-version gate lives in the base class; patch shell/execute_commands there. | ||
| _BASE_MODULE = 'cycode.cli.files_collector.sca.base_restore_dependencies' |
There was a problem hiding this comment.
Assuming we're moving the minimum-version gate to bun, is this still relevant?
There was a problem hiding this comment.
Right — reverted. The gate now lives in the Bun module again, so the version-parse/gate tests moved back to test_restore_bun_dependencies.py and patch the Bun module's shell; the base test module is unchanged from main. (f4ad542)
Per review: the minimum-version check is only used by Bun, so keep it co-located in the Bun handler rather than as generic machinery on the base class. - Revert BaseRestoreDependencies to its original form (no version hooks, no parse_tool_version). - Restore _parse_bun_version + _is_supported_bun_version in restore_bun_dependencies.py; the generate path verifies Bun >=1.2 before running `bun install`. - Move the version-parse/gate tests back to the Bun test module. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What & why
Enables
cycode scan -t scato support Bun projects during local SCA scans, so CLI users get the same coverage as platform scans. Closes the CLI portion of Bun Phase 1 (CM-67195).Bun is now a first-class package manager alongside npm, Yarn, and Pnpm — the implementation mirrors the existing Pnpm/Yarn restore handlers.
Changes
RestoreBunDependencieshandler — detects a Bun project viabun.lockor apackageManager/enginesbun signal inpackage.json. Ifbun.lockalready exists it's read directly; otherwisebun install --ignore-scriptsis run to generate it (then cleaned up).bun.lockwe parse (older Bun writes binarybun.lockb). Before generating a lockfile the handler verifiesbun --version; on older/missing Bun it logs a warning and skips the restore rather than producing an unparseable lockfile. An already-presentbun.lockskips the check (its existence implies Bun ≥ 1.2).bun.lockadded to npm's alternative-lockfile guard so npm doesn't claim Bun projects, and the Bun handler is registered ahead of the npm fallback.consts.py—bun.lockadded to the SCA supported files and the npm ecosystem map.Acceptance criteria
bun.lock→bun installrestores deps before scanningTesting
ruff checkandruff formatclean.bun.lockproject reads the lockfile directly (npm skipped); no-lockfile project runs the version gate +bun install --ignore-scriptsand generates/parsesbun.lock.Note
There is a documented NOTE in
restore_npm_dependencies.py::is_project: the npm guard is lockfile-on-disk based, so a project declaringpackageManager: "bun@..."with no lockfile is claimed by both handlers (pre-existing behavior shared by pnpm/yarn). Left as-is for consistency; tracked on CM-67195.🤖 Generated with Claude Code