Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ usage:
description: |
This action collects data about the current environment in which fcli is being run,
like the current CI/CD platform (GitHub, GitLab, Azure DevOps, ...), Git data from
current source code directory, ... Collected data is stored in global.ci.* action
current source code directory, ... Collected data is stored in global.ci.* action
variables for use by other actions, and printed to the output for user reference.
This includes both environment-specific data, and any data derived from that data,
like default FoD release name and SSC application version name.

config:
output: immediate
mcp: exclude

steps:
# Only run if not run before; global.isCiInitialized is set at the end of this action.
# Callers may also check this variable to avoid re-running this action.
Expand All @@ -26,9 +26,9 @@ steps:
global.ci.fcliVersion: ${fcliBuildProps.fcliVersion} # Fcli version
global.ci.fcliBuildInfo: ${fcliBuildProps.fcliBuildInfo} # Fcli build information
global.ci.name: # Name of current CI system
global.ci.id: # Id of current CI system, used to look up <id>-* actions
global.ci.id: # Id of current CI system, used to look up <id>-* actions
global.ci.qualifiedRepoName: # Fully qualified repository name
global.ci.sourceBranch: # The current branch being processed/scanned
global.ci.sourceBranch: # The current branch being processed/scanned
global.ci.commitHeadSHA: # Head commit SHA (actual commit on branch)
global.ci.commitMergeSHA: # Merge commit SHA (for PRs on GitHub, same as head otherwise)
global.ci.workspaceDir: "." # Workspace/repository root directory (default to current dir)
Expand All @@ -38,6 +38,7 @@ steps:
global.ci.prId: # Pull/merge request numeric identifier (null if not active)
global.ci.prTarget: # Pull/merge request target branch (null if not active)
global.ci.prTerminology: "Pull Request" # Pull/merge request terminology for this CI system (default)
global.ci.prCommentActionSuffix: pr-comment # Comment action suffix, for example pr-comment or mr-comment
# The following are set by default at the end, but may be overridden by individual CI configurations
global.fod.prCommentAction: # FoD PR comment action
global.ssc.prCommentAction: # SSC PR comment action
Expand All @@ -52,7 +53,7 @@ steps:
ci.detected: ${#_ci.detect()}
ci.type: ${ci.detected.type}
ci.env: ${ci.detected.env}

# For recognized CI systems (not unknown), extract properties from detected environment
# Using conditional navigation operator ?. to safely access properties even if ci.env is empty
- if: ${ci.type!='unknown'}
Expand All @@ -69,20 +70,33 @@ steps:
global.ci.prId: ${ci.env?.pullRequest?.id}
global.ci.prTarget: ${ci.env?.pullRequest?.target}
global.ci.prTerminology: ${ci.env?.prTerminology?:global.ci.prTerminology}

global.ci.prCommentActionSuffix: ${ci.env?.prCommentActionSuffix?:global.ci.prCommentActionSuffix}

# GitHub-specific properties
- if: ${ci.type=='github'}
var.set:
global.ci.jobSummaryFile: ${#ifBlank(global.ci.jobSummaryFile,ci.env?.jobSummaryFile)}

# Jenkins
- if: ${#isNotBlank(#env('JENKINS_HOME'))||#isNotBlank(#env('JENKINS_URL'))}
var.set:
global.ci.name: Jenkins
global.ci.id: jenkins
global.ci.workspaceDir: ${#env('WORKSPACE')}
global.ci.sourceDir: ${#env('WORKSPACE')}


# Azure DevOps - detect PR context from @fortify/setup env ado
- if: ${#isNotBlank(#env('SYSTEM_TEAMFOUNDATIONSERVERURI'))}
var.set:
global.ci.name: Azure DevOps
global.ci.id: ado
global.ci.workspaceDir: ${#env('BUILD_SOURCESDIRECTORY')}
global.ci.sourceDir: ${#env('BUILD_SOURCESDIRECTORY')}
global.ci.prActive: ${#isNotBlank(#env('SYSTEM_PULLREQUEST_PULLREQUESTID'))}
global.ci.prId: ${#env('SYSTEM_PULLREQUEST_PULLREQUESTID')}
global.ci.prTarget: ${#env('SYSTEM_PULLREQUEST_TARGETBRANCH')}
global.ci.prCommentActionSuffix: pr-comment

# Override sourceDir with SOURCE_DIR if specified (custom user variable)
# NOTE: workspaceDir is NOT overridden by SOURCE_DIR, as it should always be the workspace root
- if: ${#isNotBlank(#env('SOURCE_DIR'))}
Expand All @@ -94,7 +108,7 @@ steps:
global.ci.sourceDir: ${#ifBlank(global.ci.sourceDir,'.')}
- var.set:
global.ci.localRepo: ${#localRepo(global.ci.sourceDir)}

# Generic local repository fallback (run if previous CI-specific steps didn't set these)
- if: ${#isBlank(global.ci.id) && global.ci.localRepo!=null}
var.set:
Expand All @@ -104,14 +118,14 @@ steps:
global.ci.sourceBranch: ${global.ci.localRepo.branch?.short}
global.ci.commitHeadSHA: ${global.ci.localRepo.commit?.headId?.full}
global.ci.commitMergeSHA: ${global.ci.localRepo.commit?.mergeId?.full}

# Additional generic variables based on the output of the CI-specific sections above
- var.set:
global.ci.defaultFortifyRepo: ${#joinOrNull(':', global.ci.qualifiedRepoName, global.ci.sourceBranch)}
# Set default reporting actions based on ci identifier. Note that FoD/SSC CI actions should check existence of these actions
# Set default reporting actions based on ci identifier. Note that FoD/SSC CI actions should check existence of these actions
# TODO Only use default values if not explicitly defined in CI-specific sections above.
global.ci.fod_prCommentAction: ${#actionOrNull('fod',#joinOrNull('-', global.ci.id, 'pr-comment'))}
global.ci.ssc_prCommentAction: ${#actionOrNull('ssc',#joinOrNull('-', global.ci.id, 'pr-comment'))}
global.ci.fod_prCommentAction: ${#actionOrNull('fod',#joinOrNull('-', global.ci.id, global.ci.prCommentActionSuffix))}
global.ci.ssc_prCommentAction: ${#actionOrNull('ssc',#joinOrNull('-', global.ci.id, global.ci.prCommentActionSuffix))}
global.ci.fod_sastExportAction: ${#actionOrNull('fod',#joinOrNull('-', global.ci.id, 'sast-report'))}
global.ci.ssc_sastExportAction: ${#actionOrNull('ssc',#joinOrNull('-', global.ci.id, 'sast-report'))}
global.ci.fod_dastExportAction: ${#actionOrNull('fod',#joinOrNull('-', global.ci.id, 'dast-report'))}
Expand All @@ -122,13 +136,13 @@ steps:
- if: "${global.ci.prActive!=true}"
var.set:
global.ci.prNotActiveSkipReason: "Not a ${global.ci.prTerminology}"
- log.info: "${global.ci.name!=null ? 'Detected '+global.ci.name : 'No CI system detected'}"
- log.info: {msg: "${global.ci.name!=null ? 'Detected '+global.ci.name : 'No CI system detected'}"}
- records.for-each:
from: ${#properties(global.ci)}
record.var-name: p
do:
- if: ${#isDebugEnabled() || p.value!=null}
log.info: "${' '+p.key+': '+p.value}"
log.info: {msg: "${' '+p.key+': '+p.value}"}

# Mark as initialized to prevent re-running this action
- var.set:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,13 @@ formatters:
desc: >-
(PREVIEW) If `DO_PR_COMMENT` is set to true (implied if any of the other two `PR_COMMENT_*` variables
are set), a Pull Request or Merge Request comment will be generated using an fcli-provided action
matching the current CI system like actionRef:_:github-pr-comment or, if specified, the custom fcli action
specified through `PR_COMMENT_ACTION`. Extra options for the fcli action can be specified through
the `PR_COMMENT_EXTRA_OPTS` environment variable, which may include fcli options to allow unsigned
custom actions to be used.
matching the current CI system. For GitHub, Azure DevOps, and Bitbucket the action is named
`<ci-id>-pr-comment` (e.g., actionRef:_:github-pr-comment or actionRef:_:ado-pr-comment).
For GitLab, the action is named `<ci-id>-mr-comment` (e.g., actionRef:_:gitlab-mr-comment)
to reflect GitLab's Merge Request terminology.
A custom action can be specified through `PR_COMMENT_ACTION`. Extra options for the fcli action
can be specified through the `PR_COMMENT_EXTRA_OPTS` environment variable, which may include
fcli options to allow unsigned custom actions to be used.
- names: DO_SAST_EXPORT\nSAST_EXPORT_ACTION\nSAST_EXPORT_EXTRA_OPTS
desc: >-
If `DO_SAST_EXPORT` is not set to `false` and a SAST scan was completed, the SAST vulnerability
Expand Down Expand Up @@ -413,10 +416,13 @@ formatters:
desc: >-
(PREVIEW) If `DO_PR_COMMENT` is set to true (implied if any of the other two `PR_COMMENT_*` variables
are set), a Pull Request or Merge Request comment will be generated using an fcli-provided action
matching the current CI system like actionRef:_:github-pr-comment or, if specified, the custom fcli action
specified through `PR_COMMENT_ACTION`. Extra options for the fcli action can be specified through
the `PR_COMMENT_EXTRA_OPTS` environment variable, which may include fcli options to allow unsigned
custom actions to be used.
matching the current CI system. For GitHub, Azure DevOps, and Bitbucket the action is named
`<ci-id>-pr-comment` (e.g., actionRef:_:github-pr-comment or actionRef:_:ado-pr-comment).
For GitLab, the action is named `<ci-id>-mr-comment` (e.g., actionRef:_:gitlab-mr-comment)
to reflect GitLab's Merge Request terminology.
A custom action can be specified through `PR_COMMENT_ACTION`. Extra options for the fcli action
can be specified through the `PR_COMMENT_EXTRA_OPTS` environment variable, which may include
fcli options to allow unsigned custom actions to be used.
- names: DO_SAST_EXPORT\nSAST_EXPORT_ACTION\nSAST_EXPORT_EXTRA_OPTS
desc: >-
If `DO_SAST_EXPORT` is not set to `false` and a SAST scan was completed, the SAST vulnerability
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public record AdoEnvironment(
String repositoryId,
String buildId,
String prTerminology,
String prCommentActionSuffix,
String ciName,
String ciId
) {
Expand All @@ -57,41 +58,57 @@ public record AdoEnvironment(
public static final String NAME = "Azure DevOps";
public static final String ID = "ado";
public static final String PR_TERMINOLOGY = "Pull Request";
public static final String PR_COMMENT_ACTION_SUFFIX = "pr-comment";

// Environment variable names
public static final String ENV_ORGANIZATION_URL = "System.TeamFoundationCollectionUri";
public static final String ENV_ORGANIZATION_URL_ALT = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI";
public static final String ENV_PROJECT = "System.TeamProject";
public static final String ENV_PROJECT_ALT = "SYSTEM_TEAMPROJECT";
public static final String ENV_REPOSITORY_NAME = "Build.Repository.Name";
public static final String ENV_REPOSITORY_NAME_ALT = "BUILD_REPOSITORY_NAME";
public static final String ENV_REPOSITORY_ID = "Build.Repository.ID";
public static final String ENV_REPOSITORY_ID_ALT = "BUILD_REPOSITORY_ID";
public static final String ENV_BUILD_ID = "Build.BuildId";
public static final String ENV_BUILD_ID_ALT = "BUILD_BUILDID";
public static final String ENV_SOURCE_BRANCH = "Build.SourceBranch";
public static final String ENV_SOURCE_BRANCH_ALT = "BUILD_SOURCEBRANCH";
public static final String ENV_SOURCE_BRANCH_NAME = "Build.SourceBranchName";
public static final String ENV_SOURCE_BRANCH_NAME_ALT = "BUILD_SOURCEBRANCHNAME";
public static final String ENV_SOURCE_VERSION = "Build.SourceVersion";
public static final String ENV_SOURCE_VERSION_ALT = "BUILD_SOURCEVERSION";
public static final String ENV_SOURCES_DIRECTORY = "Build.SourcesDirectory";
public static final String ENV_SOURCES_DIRECTORY_ALT = "BUILD_SOURCESDIRECTORY";
public static final String ENV_DEFAULT_WORKING_DIRECTORY = "System.DefaultWorkingDirectory";
public static final String ENV_DEFAULT_WORKING_DIRECTORY_ALT = "SYSTEM_DEFAULTWORKINGDIRECTORY";
public static final String ENV_PR_SOURCE_BRANCH = "System.PullRequest.SourceBranch";
public static final String ENV_PR_SOURCE_BRANCH_ALT = "SYSTEM_PULLREQUEST_SOURCEBRANCH";
public static final String ENV_PR_SOURCE_BRANCH_NAME = "System.PullRequest.SourceBranchName";
public static final String ENV_PR_SOURCE_BRANCH_NAME_ALT = "SYSTEM_PULLREQUEST_SOURCEBRANCHNAME";
public static final String ENV_PR_TARGET_BRANCH = "System.PullRequest.TargetBranch";
public static final String ENV_PR_TARGET_BRANCH_ALT = "SYSTEM_PULLREQUEST_TARGETBRANCH";
public static final String ENV_PR_TARGET_BRANCH_NAME = "System.PullRequest.TargetBranchName";
public static final String ENV_PR_TARGET_BRANCH_NAME_ALT = "SYSTEM_PULLREQUEST_TARGETBRANCHNAME";
public static final String ENV_PR_ID = "System.PullRequest.PullRequestId";
public static final String ENV_PR_ID_ALT = "SYSTEM_PULLREQUEST_PULLREQUESTID";
public static final String ENV_TOKEN = "ADO_TOKEN";

/**
* Detect Azure DevOps CI environment from environment variables.
* Returns null if not running in Azure DevOps.
*/
public static AdoEnvironment detect() {
var repoName = EnvHelper.env(ENV_REPOSITORY_NAME);
var repoName = env(ENV_REPOSITORY_NAME, ENV_REPOSITORY_NAME_ALT);
if (StringUtils.isBlank(repoName)) return null;

var sourceBranchRaw = EnvHelper.env(ENV_SOURCE_BRANCH);
var sourceBranchRaw = env(ENV_SOURCE_BRANCH, ENV_SOURCE_BRANCH_ALT);
var isPr = StringUtils.isNotBlank(sourceBranchRaw) && sourceBranchRaw.startsWith("refs/pull/");
var branchInfo = detectBranchInfo(isPr, sourceBranchRaw);
var sourceBranch = branchInfo[0];
var targetBranch = branchInfo[1];
var sha = EnvHelper.env(ENV_SOURCE_VERSION);
var repositoryId = EnvHelper.env(ENV_REPOSITORY_ID);
var buildId = EnvHelper.env(ENV_BUILD_ID);
var sha = env(ENV_SOURCE_VERSION, ENV_SOURCE_VERSION_ALT);
var repositoryId = env(ENV_REPOSITORY_ID, ENV_REPOSITORY_ID_ALT);
var buildId = env(ENV_BUILD_ID, ENV_BUILD_ID_ALT);

// Build standardized structures
// Extract simple repo name from full path if present
Expand All @@ -104,7 +121,9 @@ public static AdoEnvironment detect() {

var ciRepository = CiRepository.builder()
.workspaceDir(EnvHelper.envOrDefault(ENV_SOURCES_DIRECTORY,
EnvHelper.envOrDefault(ENV_DEFAULT_WORKING_DIRECTORY, ".")))
() -> EnvHelper.envOrDefault(ENV_SOURCES_DIRECTORY_ALT,
() -> EnvHelper.envOrDefault(ENV_DEFAULT_WORKING_DIRECTORY,
() -> EnvHelper.envOrDefault(ENV_DEFAULT_WORKING_DIRECTORY_ALT, ".")))))
.remoteUrl(null) // Not readily available in environment
.name(CiRepositoryName.builder()
.short_(shortRepoName)
Expand Down Expand Up @@ -132,19 +151,20 @@ public static AdoEnvironment detect() {
.build();

var pullRequest = isPr
? CiPullRequest.active(EnvHelper.env(ENV_PR_ID), targetBranch)
? CiPullRequest.active(env(ENV_PR_ID, ENV_PR_ID_ALT), targetBranch)
: CiPullRequest.inactive();

return AdoEnvironment.builder()
.organization(EnvHelper.env(ENV_ORGANIZATION_URL))
.project(EnvHelper.env(ENV_PROJECT))
.organization(env(ENV_ORGANIZATION_URL, ENV_ORGANIZATION_URL_ALT))
.project(env(ENV_PROJECT, ENV_PROJECT_ALT))
.repositoryId(repositoryId)
.buildId(buildId)
.ciRepository(ciRepository)
.ciBranch(ciBranch)
.ciCommit(ciCommit)
.pullRequest(pullRequest)
.prTerminology(PR_TERMINOLOGY)
.prCommentActionSuffix(PR_COMMENT_ACTION_SUFFIX)
.ciName(NAME)
.ciId(ID)
.build();
Expand All @@ -160,20 +180,29 @@ private static String[] detectBranchInfo(boolean isPr, String sourceBranchRaw) {

if (isPr) {
sourceBranch = EnvHelper.envOrDefault(ENV_PR_SOURCE_BRANCH,
EnvHelper.env(ENV_PR_SOURCE_BRANCH_NAME));
() -> EnvHelper.envOrDefault(ENV_PR_SOURCE_BRANCH_ALT,
() -> EnvHelper.envOrDefault(ENV_PR_SOURCE_BRANCH_NAME,
EnvHelper.env(ENV_PR_SOURCE_BRANCH_NAME_ALT))));
sourceBranch = StringUtils.isNotBlank(sourceBranch) ? sourceBranch.replaceAll("^refs/heads/", "") : null;

targetBranch = EnvHelper.envOrDefault(ENV_PR_TARGET_BRANCH,
EnvHelper.env(ENV_PR_TARGET_BRANCH_NAME));
() -> EnvHelper.envOrDefault(ENV_PR_TARGET_BRANCH_ALT,
() -> EnvHelper.envOrDefault(ENV_PR_TARGET_BRANCH_NAME,
EnvHelper.env(ENV_PR_TARGET_BRANCH_NAME_ALT))));
targetBranch = StringUtils.isNotBlank(targetBranch) ? targetBranch.replaceAll("^refs/heads/", "") : null;
} else {
sourceBranch = EnvHelper.envOrDefault(ENV_SOURCE_BRANCH_NAME,
StringUtils.isNotBlank(sourceBranchRaw) ? sourceBranchRaw.replaceAll("^refs/heads/", "") : null);
() -> EnvHelper.envOrDefault(ENV_SOURCE_BRANCH_NAME_ALT,
StringUtils.isNotBlank(sourceBranchRaw) ? sourceBranchRaw.replaceAll("^refs/heads/", "") : null));
targetBranch = null;
}

return new String[]{sourceBranch, targetBranch};
}

private static String env(String primaryName, String alternateName) {
return EnvHelper.envOrDefault(primaryName, () -> EnvHelper.env(alternateName));
}

/**
* Get qualified repository name for Fortify (repo:branch format).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ public record BitbucketEnvironment(
String repositoryFullName,
String pipelineUuid,
String prTerminology,
String prCommentActionSuffix,
String ciName,
String ciId
) {
public static final String TYPE = "bitbucket";
public static final String NAME = "Bitbucket";
public static final String ID = "bitbucket";
public static final String PR_TERMINOLOGY = "Pull Request";
public static final String PR_COMMENT_ACTION_SUFFIX = "pr-comment";

public static final String ENV_WORKSPACE = "BITBUCKET_WORKSPACE";
public static final String ENV_REPO_OWNER = "BITBUCKET_REPO_OWNER";
Expand Down Expand Up @@ -138,6 +140,7 @@ public static BitbucketEnvironment detect() {
.repositoryFullName(repoFullName)
.pipelineUuid(EnvHelper.env(ENV_PIPELINE_UUID))
.prTerminology(PR_TERMINOLOGY)
.prCommentActionSuffix(PR_COMMENT_ACTION_SUFFIX)
.ciName(NAME)
.ciId(ID)
.build();
Expand Down
Loading
Loading