Skip to content

Conversation

@rsenden
Copy link
Contributor

@rsenden rsenden commented Nov 12, 2025

HIGH-LEVEL GOALS

  • Maximum re-use of fcli functionality across CI integrations; CI integrations become even thinner wrappers
  • Support pre-installed tools, in addition to dynamically downloading tools like ScanCentral Client during pipeline execution
  • Future support for sourceanalyzer as pre-installed tool, allowing local translation/scan to be triggered from fcli ci action

PURPOSE / RATIONALE

Centralize Setup Logic in Fcli

  • Standardized tool management across all CI/CD platforms - New fcli tool env init command provides unified API that GitHub Actions, Azure DevOps, GitLab, Jenkins, and shell scripts can consume, eliminating platform-specific tool management code duplication
  • Single source of truth for tool operations - Tool definitions, installation, and registration logic centralized in fcli reduces maintenance burden for platform integrations by ~60%
  • Thin platform wrappers - Platform integrations become lightweight layers that bootstrap fcli and delegate to standard commands

Pre-installed Tool Detection & Registration

  • Support air-gapped/enterprise environments - New fcli tool <tool> register command auto-detects pre-installed tools via environment variables (SC_CLIENT_HOME), PATH scanning, and fcli metadata, enabling offline/restricted environments without downloads
  • Future support for non-downloadable tools - Registration framework enables tools without public download URLs like sourceanalyzer to be registered and used with fcli, for example in the fcli ci action

Unified Environment Variable Generation

  • Platform-specific output formats - New fcli tool env command family (shell, powershell, github, gitlab, ado) generates platform-appropriate environment configurations, eliminating manual PATH/HOME variable management
  • Automatic tool cache integration - Platform-specific cache directories (GitHub Actions $RUNNER_TOOL_CACHE, Azure DevOps $AGENT_TOOLSDIRECTORY, etc.) automatically detected and used for persistent tool installations across pipeline runs

Reusable Bootstrap Components

  • @fortify/setup npm module - Zero-dependency TypeScript library provides fcli bootstrap logic (download, verify, cache) for JavaScript-based integrations, enabling standardized fcli setup across GitHub Actions, Azure DevOps tasks, and custom scripts
  • Clean separation of concerns - Fcli cannot install itself (circular dependency), so bootstrap responsibility handled by platform tools, which then delegate all other tool management to fcli
  • Semantic version support - Fcli release tags (v3, v3.6, v3.6.1) enable flexible version pinning without hardcoding patch versions; by default, CI/CD integrations like GitHub Action will always bootstrap latest fcli v3.x.y for running fcli tool env * commands

ALSO SEE

TODO

  • General:
    • Review all code in this PR and new @fortify/setup module (no code duplication, re-use code from fcli-common where possible, short methods, any weird Copilot-generated code, ...)
    • Fix any TODO comments in code
    • Review & update documentation for both fcli and @fortify/setup on bootstrapping and usage; provide samples, instructions for various use cases (setting up fcli & other tools in custom CI integrations/pipelines, Dockerfile, ...)
  • fcli
    • Review Copilot-generated functional tests in fcli-functional-test; do these tests make sense, are we missing any tests, ...? Ideally, we should have (Copilot-generated?) functional tests for the fcli tool env * commands as well
    • Include final fix/feat commit messages in PR description, to be applied upon squash merge
  • @fortify/setup
    • Add tests for @fortify/setup (both library and CLI?) and run those tests in its build process
    • Review all code in this new NPM module (no code duplication, use existing fcli code where possible, short methods, any weird Copilot-generated code, ...)
    • Review and update documentation
    • Review/remove examples; these are likely out of date
  • Release:
    • Release new fcli version -> release @fortify/setup 1.0.0 -> finalize GitHub Action v3 -> create/update other CI integrations (GitLab, Azure Devops, ...)

TBD

  • Should any of the new fcli commands be marked as PREVIEW and/or hidden? We can't really implement breaking changes anyway, as that would potentially break @fortify/setup, new GitHub Action, ...
  • Any better way of doing any of these things? Better command/option names in fcli, @fortify/setup, ...?

Changelog Entries

After careful review, upon squash merge of this PR, use these commit message lines for changelog generation:

feat: `fcli tool definitions update`: Add `--max-age` option to only update definitions if older than specified period (e.g., `1h`, `4h`, `1d`)

feat: `fcli tool definitions update`: Add `--force` option to update definitions even if they are up-to-date

feat: `fcli tool * register`: New commands for registering existing tool installations with automatic version detection (supports binary path, bin directory, or installation directory)

feat: `fcli tool * get`: New commands for displaying detailed information about a specific installed tool version

feat: `fcli tool env init`: New command for automatically setting up one or more Fortify tools (auto-detects pre-installed tools, downloads if necessary, supports air-gapped environments, and platform-specific tool caching)

feat: `fcli tool env shell|powershell|github|gitlab|ado|expr`: New commands for generating environment variable exports for installed or registered tools in various shell or CI/CD-specific formats

feat: `fcli tool * install`: Tool installations now show action status (installed/copied/registered/skipped) for better visibility

feat: `fcli tool * install`: Add hidden `--copy-if-matching` option (internal use by `fcli tool env init`) to copy from existing installation instead of downloading

feat: `fcli tool sc-client install`: Add `--jre` option to specify custom JRE home directory for use with `fcli tool sc-client run` command

feat: `fcli tool sc-client install`: Improve JRE handling with automatic detection from environment variables (`SC_CLIENT_JRE_HOME`, `SCANCENTRAL_JRE_HOME`)

chore: `fcli tool * install`: Remove deprecation warning for `--install-dir` option

feat: fcli `ci` action: Add support for pre-installed tools via `SC_CLIENT_HOME` and `DEBRICKED_HOME` environment variables, complementing existing dynamic installation

feat: fcli `ci` action: Add support for `PREINSTALLED` environment variable to require all tools to be pre-installed, preventing automatic downloads

fix: fcli `ci` action: Post-scan tasks (check-policy, release-summary, pr-comment, export) are now properly skipped if no scans were run

feat: fcli `detect-env` action: Renamed from `ci-vars` and updated to provide general environment detection capabilities (CI platform, Git repository, ...)

feat: fcli `package` action: Simplify tool setup by using `fcli tool env init` instead of separate update/install steps

feat: fcli `package` action: Change default `--sc-client-version` from `latest` to `auto` for smarter version resolution

feat: fcli `debricked-scan` action: Simplify tool setup by using `fcli tool env init` instead of separate update/install steps

feat: fcli `debricked-scan` action: Change default `--cli-version` from `latest` to `auto` for smarter version resolution

feat: Action SpEL functions: Add `#opt(name, value)` function for conditionally formatting command-line options

chore: `fcli action run ci`: Remove "PREVIEW" labels - now considered production-ready

chore: `fcli action run package`: Remove "PREVIEW" label - now considered production-ready

chore: Various fcli actions: Improve command-line argument quoting for better shell compatibility (use `"--option=value"` format)

fix: Improve error handling and error output

chore: Add `--skip-if-exists` and `--auto-required-attrs` options as reusable mixins for FoD and SSC create commands

chore: FileUtils: Add new glob pattern matching methods (`processMatchingFileStream`, `processMatchingDirStream`, `processMatchingStream`) with support for Ant-style patterns

chore: Add `JreHelper` utility class for JRE-related operations

chore: Improve exception handling by using `FcliSimpleException` instead of generic exceptions in DateTimePeriodHelper and FcliVariableHelper

chore: Hide generic fcli options (`--help`, `--env-prefix`) from wrapper tool help output like `@fortify/setup`

docs: Add comprehensive Developer Integration Guide (`developer-integration-guide.md`) for CI/CD platform integrations

docs: Document fcli bootstrap strategy and semantic versioning support for platform integration tools

docs: Document `--self` parameter for `fcli tool env init` to enable efficient tool copying from bootstrapped fcli

chore: Add FileUtils glob pattern matching tests

chore: Add functional tests for tool register commands

ci: Add concurrency control to cancel redundant workflow runs

ci: Improve pull request handling in functional test triggering

ci: Fix branch name handling for development releases

ci: Prevent duplicate workflow runs on push to PR branch


@rsenden rsenden marked this pull request as draft November 12, 2025 13:52
@rsenden
Copy link
Contributor Author

rsenden commented Nov 14, 2025

@copilot please check code in this PR; are there any inconsistencies, does the code follow fcli code style and conventions (also considering information in the copilot instructions file), is there anything that could be improved?

Copy link
Contributor

Copilot AI commented Nov 14, 2025

@rsenden I've opened a new pull request, #869, to work on those changes. Once the pull request is ready, I'll request review from you.

@rsenden
Copy link
Contributor Author

rsenden commented Nov 14, 2025

@copilot Please have a look at existing commit messages containing lines with feat: and fix: prefixes in the dev/v3.x branch, then generate a literal text block that proposes new features and fixes (compared to the dev/v3.x branch) using similar conventions for generating change log entries for this PR

Copilot AI mentioned this pull request Nov 14, 2025
Copy link
Contributor

Copilot AI commented Nov 14, 2025

@rsenden I've opened a new pull request, #870, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Contributor

Copilot AI commented Nov 26, 2025

@rsenden I've opened a new pull request, #879, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This substantial PR introduces centralized tool management capabilities to fcli, enabling standardized tool setup across CI/CD platforms. The key architectural shift moves tool management logic into fcli itself, allowing external integrations (GitHub Actions, Azure DevOps, etc.) to become thin wrappers.

Key changes:

  • New fcli tool setup command for unified tool installation/registration with auto-detection
  • New fcli tool env commands for platform-specific environment configuration (shell, PowerShell, GitHub, GitLab, ADO)
  • New fcli tool * register commands to register pre-installed tools
  • New fcli tool * get commands for version metadata retrieval
  • Enhanced install commands with --copy-if-matching for bootstrap optimization
  • Action schema version bump to 2.5.0

Reviewed changes

Copilot reviewed 131 out of 131 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
gradle.properties Bumps action schema version to 2.5.0 for structural changes
ToolRegisterSpec.groovy New comprehensive functional tests for tool registration (457 lines)
ToolFoDUploaderSpec.groovy Updated tests to accept SKIPPED_EXISTING alongside INSTALLED
ToolFcliSpec.groovy New tests for --copy-from functionality across scenarios
MCPToolFcliRunnerRecordsTest.java Replaced wildcard import with explicit imports
ToolMessages.properties Extensive new documentation for setup/env/register commands
Tool*.java (various) Refactored from String toolName to Tool enum pattern across all tools
ToolSetupCommand.java New high-level command orchestrating tool detection and installation
ToolEnv*.java (various) New commands generating platform-specific environment configs
Tool.java New enum centralizing tool metadata (names, binaries, env prefixes)
ToolVersionDetector.java New utility for detecting versions from binaries/manifests/filenames
ToolRegistrationHelper.java New helper managing tool registration with path resolution
ToolInstaller.java Enhanced with copy-if-matching support and action tracking
ToolInstallationsResolver.java New resolver consolidating installation+definition lookups
AbstractTool*.java (various) Updated abstract commands to use Tool enum instead of String
ToolDefinitionRootDescriptor.java Added version normalization logic
debricked-scan.yaml Updated to use new fcli tool setup command
ci.yaml Updated action invocations and environment detection

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

* @param maxDepth Maximum directory depth to search
* @param streamProcessor Function to process the stream of matching paths
* @return Result from the stream processor function
* @throws IOException if directory traversal fails
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc for processMatchingFileStream claims to throw IOException but this is impossible.

Suggested change
* @throws IOException if directory traversal fails

Copilot uses AI. Check for mistakes.
* @param maxDepth Maximum directory depth to search
* @param streamProcessor Function to process the stream of matching paths
* @return Result from the stream processor function
* @throws IOException if directory traversal fails
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc for processMatchingFileStream claims to throw IOException but this is impossible.

Copilot uses AI. Check for mistakes.
* @param maxDepth Maximum directory depth to search
* @param streamProcessor Function to process the stream of matching paths
* @return Result from the stream processor function
* @throws IOException if directory traversal fails
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc for processMatchingDirStream claims to throw IOException but this is impossible.

Suggested change
* @throws IOException if directory traversal fails

Copilot uses AI. Check for mistakes.
* @param maxDepth Maximum directory depth to search
* @param streamProcessor Function to process the stream of matching paths
* @return Result from the stream processor function
* @throws IOException if directory traversal fails
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc for processMatchingDirStream claims to throw IOException but this is impossible.

Copilot uses AI. Check for mistakes.
* @param pathFilter Predicate to filter paths (e.g., Files::isRegularFile, Files::isDirectory)
* @param streamProcessor Function to process the stream of matching paths
* @return Result from the stream processor function
* @throws IOException if directory traversal fails
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc for processMatchingStream claims to throw IOException but this is impossible.

Copilot uses AI. Check for mistakes.

@Command(name = OutputHelperMixins.Get.CMD_NAME)
public class ToolDebrickedCliGetCommand extends AbstractToolGetCommand {
@Getter @Mixin private OutputHelperMixins.Get outputHelper;
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method overrides AbstractOutputCommand.getOutputHelper; it is advisable to add an Override annotation.

Copilot uses AI. Check for mistakes.

@Command(name = OutputHelperMixins.Get.CMD_NAME)
public class ToolFoDUploaderGetCommand extends AbstractToolGetCommand {
@Getter @Mixin private OutputHelperMixins.Get outputHelper;
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method overrides AbstractOutputCommand.getOutputHelper; it is advisable to add an Override annotation.

Copilot uses AI. Check for mistakes.

@Command(name = OutputHelperMixins.Get.CMD_NAME)
public class ToolFcliGetCommand extends AbstractToolGetCommand {
@Getter @Mixin private OutputHelperMixins.Get outputHelper;
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method overrides AbstractOutputCommand.getOutputHelper; it is advisable to add an Override annotation.

Copilot uses AI. Check for mistakes.

@Command(name = OutputHelperMixins.Get.CMD_NAME)
public class ToolSCClientGetCommand extends AbstractToolGetCommand {
@Getter @Mixin private OutputHelperMixins.Get outputHelper;
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method overrides AbstractOutputCommand.getOutputHelper; it is advisable to add an Override annotation.

Copilot uses AI. Check for mistakes.

@Command(name = OutputHelperMixins.Get.CMD_NAME)
public class ToolVulnExporterGetCommand extends AbstractToolGetCommand {
@Getter @Mixin private OutputHelperMixins.Get outputHelper;
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method overrides AbstractOutputCommand.getOutputHelper; it is advisable to add an Override annotation.

Copilot uses AI. Check for mistakes.
Copilot AI and others added 4 commits November 26, 2025 03:56
Co-authored-by: rsenden <8635138+rsenden@users.noreply.github.com>
…bjectMapper()

Co-authored-by: rsenden <8635138+rsenden@users.noreply.github.com>
Fix ToolInstallationDescriptor.getGlobalBinPath() and standardize ObjectMapper usage
private static final InputStream getToolDefinitionsInputStream() throws IOException {
return Files.exists(DEFINITIONS_STATE_ZIP)
? Files.newInputStream(DEFINITIONS_STATE_ZIP)
return Files.exists(DEFINITIONS_STATE_ZIP) ? Files.newInputStream(DEFINITIONS_STATE_ZIP)

Check failure

Code scanning / Fortify on Demand

Unreleased Resource: Streams High

The function getToolDefinitionsInputStream() in ToolDefinitionsHelper.java sometimes fails to release a system resource allocated by newInputStream() on line 363. More information
@rsenden rsenden marked this pull request as ready for review December 16, 2025 09:25
@rsenden rsenden merged commit 737f39a into dev/v3.x Dec 16, 2025
29 of 41 checks passed
@rsenden rsenden deleted the feat/ci-updates branch December 16, 2025 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants