From f2cc269be99840279d58c14a77cfe9cb89049170 Mon Sep 17 00:00:00 2001 From: cdatla Date: Wed, 4 Mar 2026 11:47:18 +0530 Subject: [PATCH] feat: Add --aviator-app-mapping option to ssc bulkaudit action --- .../cli/ssc/actions/zip/bulkaudit.yaml | 106 ++++++++++++------ 1 file changed, 71 insertions(+), 35 deletions(-) diff --git a/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/actions/zip/bulkaudit.yaml b/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/actions/zip/bulkaudit.yaml index 27f0875e67..4b9cf0eb18 100644 --- a/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/actions/zip/bulkaudit.yaml +++ b/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/actions/zip/bulkaudit.yaml @@ -12,6 +12,10 @@ usage: For application versions that don't already exist in Aviator, the action will automatically create them before submitting for audit. + By default (--aviator-app-mapping=app), SSC project versions map to a single + Aviator application per SSC project. Set --aviator-app-mapping=version to map + each SSC project version to its own Aviator application (project_name__version_name). + When quota limits are exceeded for any application, issues are prioritized by folder using either the default order (Critical > High > Medium > Low) or a custom priority order specified via --folder-priority-order. The same priority @@ -39,6 +43,11 @@ cli.options: required: false default: -1 type: int + aviator-app-mapping: + names: --aviator-app-mapping + description: "Controls how SSC project versions map to Aviator applications. 'app' (default) maps one Aviator app per SSC project; 'version' maps one Aviator app per SSC project version (using project_name__version_name)." + required: false + default: app filter: names: --filter, -f description: "Optional filter to apply when querying SSC for projects. Example: 'Languages:java'. Default: no filtering" @@ -61,7 +70,7 @@ cli.options: required: false add-aviator-tags: names: --add-aviator-tags - description: "If specified, runs 'fcli aviator prepare' for the application before audit. Required unless --tag-mapping is specified." + description: "If specified, runs 'fcli aviator ssc prepare' for the application before audit. Required unless --tag-mapping is specified." required: false type: boolean default: false @@ -93,6 +102,8 @@ steps: - log.progress: "ERROR: Either --tag-mapping or --add-aviator-tags must be specified." - throw: "Either --tag-mapping or --add-aviator-tags must be specified." + - log.progress: "Using Aviator app mapping: ${cli['aviator-app-mapping']}" + # Get existing Aviator applications - log.progress: Retrieving existing Aviator applications... - run.fcli: @@ -132,7 +143,13 @@ steps: do: - var.set: current_project_name: ${version.project_details.project.name} - project_exists_in_aviator: ${aviator_app_names != null && aviator_app_names.contains(version.project_details.project.name)} + current_version_name: ${version.project_details.name} + current_aviator_app_name: ${version.project_details.project.name.replaceAll('"', '')} + - if: ${'version'.equals(cli['aviator-app-mapping'])} + var.set: + current_aviator_app_name: ${(current_project_name + '__' + current_version_name).replaceAll('"', '')} + - var.set: + project_exists_in_aviator: ${aviator_app_names != null && aviator_app_names.contains(current_aviator_app_name)} - var.set: enriched_versions..: {fmt: enriched_project} @@ -219,7 +236,8 @@ steps: # Process audit candidates - if: ${audit_candidates != null && audit_candidates.size() > 0} - do: # Initialize execution tracking + do: + # Initialize execution tracking - var.set: stats.create_attempts: 0 stats.create_successes: 0 @@ -228,65 +246,91 @@ steps: stats.audit_failures: 0 stats.entitlement_exhausted: false stats.create_skipped_due_to_entitlement: 0 + stats.would_create_count: 0 + known_aviator_app_names: ${aviator_app_names} # Process each candidate - records.for-each: from: ${audit_candidates} record.var-name: project do: + - var.set: + app_known_in_aviator: ${known_aviator_app_names != null && known_aviator_app_names.contains(project.aviator_app_name)} + - if: ${cli['dry-run']} do: - - if: ${!project.exists_in_aviator} - log.progress: Would create app ${project.project_name} + - if: ${!app_known_in_aviator} + do: + - log.info: Would create app ${project.aviator_app_name} + - var.set: + stats.would_create_count: ${stats.would_create_count + 1} + known_aviator_app_names..: ${project.aviator_app_name} - if: ${cli['add-aviator-tags']} - log.progress: Would prepare tags for ${project.project_name}:${project.version_name} + do: + - log.info: Would prepare tags for ${project.project_name}:${project.version_name} + - if: "${cli['folder-priority-order'] != null && cli['folder-priority-order'] != ''}" - log.progress: "Would audit ${project.project_name}:${project.version_name} with custom priority order: ${cli['folder-priority-order']}" + do: + - log.info: "Would audit ${project.project_name}:${project.version_name} with custom priority order: ${cli['folder-priority-order']}" - if: "${cli['folder-priority-order'] == null || cli['folder-priority-order'] == ''}" - log.progress: Would audit ${project.project_name}:${project.version_name} + do: + - log.info: Would audit ${project.project_name}:${project.version_name} - if: ${!cli['dry-run']} do: - var.set: - app_ready: ${project.exists_in_aviator} + app_ready: ${app_known_in_aviator} # Create app if needed - - if: ${!project.exists_in_aviator && !stats.entitlement_exhausted} + - if: ${!app_known_in_aviator && !stats.entitlement_exhausted} do: - var.set: stats.create_attempts: ${stats.create_attempts + 1} - run.fcli: create_app: - cmd: aviator app create "${project.project_name}" + cmd: aviator app create "${project.aviator_app_name}" status.check: false - if: ${create_app.exitCode == 0} var.set: app_ready: true stats.create_successes: ${stats.create_successes + 1} + known_aviator_app_names..: ${project.aviator_app_name} - if: ${create_app.exitCode != 0} do: - var.set: - stats.create_failures: ${stats.create_failures + 1} - - if: ${!stats.entitlement_exhausted} + create_app_error_text: "${(create_app.stderr == null ? '' : create_app.stderr) + ' ' + (create_app.stdout == null ? '' : create_app.stdout)}" + create_app_already_exists: ${create_app_error_text.toLowerCase().contains('already exists')} + create_app_entitlement_or_quota_error: ${create_app_error_text.toLowerCase().contains('entitlement') || create_app_error_text.toLowerCase().contains('quota')} + - if: ${create_app_already_exists} + var.set: + app_ready: true + known_aviator_app_names..: ${project.aviator_app_name} + - if: ${!create_app_already_exists} do: - - log.warn: App creation failed - suppressing further create attempts - var.set: - stats.entitlement_exhausted: true - - - if: ${!project.exists_in_aviator && stats.entitlement_exhausted} + stats.create_failures: ${stats.create_failures + 1} + - if: ${create_app_entitlement_or_quota_error && !stats.entitlement_exhausted} + do: + - log.warn: App creation failed due to entitlement/quota - suppressing further create attempts + - var.set: + stats.entitlement_exhausted: true + - if: ${!create_app_entitlement_or_quota_error} + log.warn: App creation failed for ${project.aviator_app_name}; continuing with remaining candidates + + - if: ${!app_known_in_aviator && stats.entitlement_exhausted} var.set: stats.create_skipped_due_to_entitlement: ${stats.create_skipped_due_to_entitlement + 1} # Prepare Aviator tags if requested - if: ${cli['add-aviator-tags']} do: - - log.progress: Preparing Aviator tags for ${project.project_name} + - log.progress: Preparing Aviator tags for ${project.project_name}:${project.version_name} - run.fcli: prepare_tags: - cmd: aviator ssc prepare --av "${project.project_name}:${project.version_name}" + cmd: aviator ssc prepare --av "${project.id}" status.check: false - if: ${prepare_tags.exitCode != 0} do: - - log.warn: Aviator tag preparation failed for ${project.project_name} + - log.warn: Aviator tag preparation failed for ${project.project_name}:${project.version_name} # Run audit if app is ready - if: ${app_ready} @@ -297,40 +341,31 @@ steps: - if: ${cli['tag-mapping'] != null && cli['tag-mapping'] != ''} run.fcli: run_audit: - cmd: "aviator ssc audit --av \"${project.project_name}:${project.version_name}\" --app \"${project.project_name}\" --log-level=INFO --tag-mapping=\"${cli['tag-mapping']}\" --refresh=${cli.refresh} --refresh-timeout=\"${cli['refresh-timeout']}\"${cli['folder-priority-order'] != null && cli['folder-priority-order'] != '' ? ' --folder-priority-order=\"' + cli['folder-priority-order'] + '\"' : ''}" + cmd: "aviator ssc audit --av \"${project.id}\" --app \"${project.aviator_app_name}\" --log-level=INFO --tag-mapping=\"${cli['tag-mapping']}\" --refresh=${cli.refresh} --refresh-timeout=\"${cli['refresh-timeout']}\"${cli['folder-priority-order'] != null && cli['folder-priority-order'] != '' ? ' --folder-priority-order=\"' + cli['folder-priority-order'] + '\"' : ''}" status.check: false - if: ${cli['tag-mapping'] == null || cli['tag-mapping'] == ''} run.fcli: run_audit: - cmd: "aviator ssc audit --av \"${project.project_name}:${project.version_name}\" --app \"${project.project_name}\" --log-level=INFO --refresh=${cli.refresh} --refresh-timeout=\"${cli['refresh-timeout']}\"${cli['folder-priority-order'] != null && cli['folder-priority-order'] != '' ? ' --folder-priority-order=\"' + cli['folder-priority-order'] + '\"' : ''}" + cmd: "aviator ssc audit --av \"${project.id}\" --app \"${project.aviator_app_name}\" --log-level=INFO --refresh=${cli.refresh} --refresh-timeout=\"${cli['refresh-timeout']}\"${cli['folder-priority-order'] != null && cli['folder-priority-order'] != '' ? ' --folder-priority-order=\"' + cli['folder-priority-order'] + '\"' : ''}" status.check: false - if: ${run_audit.exitCode != 0} do: - var.set: stats.audit_failures: ${stats.audit_failures + 1} - - log.warn: Audit failed for ${project.project_name}:${project.version_name} + - log.warn: Audit failed for ${project.aviator_app_name}:${project.version_name} # Summary - if: ${cli['dry-run']} do: - - var.set: - would_create_count: 0 - - records.for-each: - from: ${audit_candidates} - record.var-name: dr_proj - if: ${!dr_proj.exists_in_aviator} - do: - - var.set: - would_create_count: ${would_create_count + 1} - - log.progress: Dry-run complete - would process ${audit_candidates.size()} versions and create ${would_create_count} apps + - log.info: "Dry-run complete (mapping: ${cli['aviator-app-mapping']}) - would process ${audit_candidates.size()} versions and create ${stats.would_create_count} apps" - if: ${!cli['dry-run']} do: - - log.progress: Complete - Apps created ${stats.create_successes}/${stats.create_attempts}, Audits attempted ${stats.audit_attempts} + - log.info: "Complete (mapping: ${cli['aviator-app-mapping']}) - Apps created ${stats.create_successes}/${stats.create_attempts}, Audits attempted ${stats.audit_attempts}" - if: ${stats.entitlement_exhausted} - log.progress: Note - Entitlement exhausted, some app creations were skipped + log.info: Note - Entitlement exhausted, some app creations were skipped formatters: enriched_project: @@ -339,4 +374,5 @@ formatters: issuesPendingReview: ${version.issuesPendingReview} project_name: ${version.project_details.project.name} version_name: ${version.project_details.name} + aviator_app_name: ${current_aviator_app_name} exists_in_aviator: ${project_exists_in_aviator}