From 8db03f884018979612fd689facb2d5b85c44a1f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Di=C3=A9ffrei=20Quadros?= <dieffrei@gmail.com>
Date: Tue, 28 May 2024 16:41:02 -0300
Subject: [PATCH] feat: fail if promoted flag

---
 packages/sfp-cli/messages/promote.json                |  3 ++-
 packages/sfp-cli/messages/release.json                |  3 ++-
 packages/sfp-cli/src/commands/artifacts/promote.ts    | 11 +++++++++--
 packages/sfp-cli/src/commands/release.ts              |  7 +++++++
 .../package/promote/PromoteUnlockedPackageImpl.ts     | 11 +++++++++--
 packages/sfp-cli/src/impl/deploy/DeployImpl.ts        |  4 +++-
 packages/sfp-cli/src/impl/release/ReleaseImpl.ts      | 10 ++++++----
 7 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/packages/sfp-cli/messages/promote.json b/packages/sfp-cli/messages/promote.json
index 6292af8fc..0f6069382 100644
--- a/packages/sfp-cli/messages/promote.json
+++ b/packages/sfp-cli/messages/promote.json
@@ -3,5 +3,6 @@
     "artifactDirectoryFlagDescription": "The directory where artifacts are located",
     "outputDirectoryFlagDescription": "Output directory where promoted artifacts are written",
     "devhubAliasFlagDescription": "Provide the alias of the devhub previously authenticated, default value is HubOrg if using the Authenticate Devhub task",
-    "logsGroupSymbolFlagDescription": "Symbol used by CICD platform to group/collapse logs in the console. Provide an opening group, and an optional closing group symbol."
+    "logsGroupSymbolFlagDescription": "Symbol used by CICD platform to group/collapse logs in the console. Provide an opening group, and an optional closing group symbol.",
+    "failifalreadypromotedFlagDescription": "Fail if version: \"major.minor.patch\" was already promoted"
 }
diff --git a/packages/sfp-cli/messages/release.json b/packages/sfp-cli/messages/release.json
index 45ca3c7c3..e25727cec 100644
--- a/packages/sfp-cli/messages/release.json
+++ b/packages/sfp-cli/messages/release.json
@@ -16,5 +16,6 @@
     "devhubAliasFlagDescription": "Provide the alias of the devhub previously authenticated, default value is HubOrg",
     "directoryFlagDescription": "Relative path to directory to which the changelog should be generated, if the directory doesnt exist, it will be created",
     "branchNameFlagDescription": "Repository branch in which the changelog files are located",
-    "changelogByDomainsFlagDescription":"Create changelog files by domains or name mentioned in release config"
+    "changelogByDomainsFlagDescription":"Create changelog files by domains or name mentioned in release config",
+    "failifalreadypromotedFlagDescription": "Fail if version: \"major.minor.patch\" was already promoted"
 }
diff --git a/packages/sfp-cli/src/commands/artifacts/promote.ts b/packages/sfp-cli/src/commands/artifacts/promote.ts
index 9a4fa8d18..fbf0ef6eb 100644
--- a/packages/sfp-cli/src/commands/artifacts/promote.ts
+++ b/packages/sfp-cli/src/commands/artifacts/promote.ts
@@ -17,7 +17,7 @@ const messages = Messages.loadMessages('@flxbl-io/sfp', 'promote');
 export default class Promote extends SfpCommand {
     public static description = messages.getMessage('commandDescription');
     static aliases = ['orchestrator:promote']
-    
+
 
     public static examples = [`$ sfp promote -d path/to/artifacts -v <org>`];
 
@@ -31,6 +31,12 @@ export default class Promote extends SfpCommand {
             description: messages.getMessage('artifactDirectoryFlagDescription'),
             default: 'artifacts',
         }),
+        failifalreadypromoted: Flags.boolean({
+            required: true,
+            char: 'x',
+            description: messages.getMessage('failifalreadypromotedFlagDescription'),
+            default: false,
+        }),
        loglevel
     };
 
@@ -59,7 +65,8 @@ export default class Promote extends SfpCommand {
                         let promoteUnlockedPackageImpl = new PromoteUnlockedPackageImpl(
                             artifact.sourceDirectoryPath,
                             sfpPackage.package_version_id,
-                            this.hubOrg.getUsername()
+                            this.hubOrg.getUsername(),
+                            this.flags.failifalreadypromoted
                         );
                         await promoteUnlockedPackageImpl.promote();
                     }
diff --git a/packages/sfp-cli/src/commands/release.ts b/packages/sfp-cli/src/commands/release.ts
index 268695373..101332f84 100644
--- a/packages/sfp-cli/src/commands/release.ts
+++ b/packages/sfp-cli/src/commands/release.ts
@@ -106,6 +106,12 @@ export default class Release extends SfpCommand {
             description: messages.getMessage('changelogByDomainsFlagDescription'),
             hidden: true,
         }),
+        failifalreadypromoted: Flags.boolean({
+            required: true,
+            char: 'x',
+            description: messages.getMessage('failifalreadypromotedFlagDescription'),
+            default: false,
+        }),
         devhubalias: optionalDevHubFlag,
         loglevel,
     };
@@ -171,6 +177,7 @@ export default class Release extends SfpCommand {
                 devhubUserName: this.flags.devhubalias,
                 branch: this.flags.branchname,
                 directory: this.flags.directory,
+                failifalreadypromoted: this.flags.failifalreadypromoted
             };
 
             let releaseImpl: ReleaseImpl = new ReleaseImpl(props, new ConsoleLogger());
diff --git a/packages/sfp-cli/src/core/package/promote/PromoteUnlockedPackageImpl.ts b/packages/sfp-cli/src/core/package/promote/PromoteUnlockedPackageImpl.ts
index f41d102e8..2936d2233 100644
--- a/packages/sfp-cli/src/core/package/promote/PromoteUnlockedPackageImpl.ts
+++ b/packages/sfp-cli/src/core/package/promote/PromoteUnlockedPackageImpl.ts
@@ -1,12 +1,14 @@
 import { SfProject } from '@salesforce/core';
 import { PackageSaveResult, PackageVersion } from '@salesforce/packaging';
 import SFPOrg from '../../org/SFPOrg';
+import SFPLogger, {COLOR_HEADER} from "@flxbl-io/sfp-logger";
 
 export default class PromoteUnlockedPackageImpl {
     public constructor(
         private project_directory: string,
         private package_version_id: string,
-        private devhub_alias: string
+        private devhub_alias: string,
+        private fail_if_is_alreadyPromoted: boolean
     ) {}
 
     public async promote(): Promise<void> {
@@ -26,7 +28,12 @@ export default class PromoteUnlockedPackageImpl {
             result.id = packageVersionData.SubscriberPackageVersionId;
         } catch (e) {
             if (e.message.includes('previously released')) {
-                throw new Error(`The package version ${packageVersionData.MajorVersion}.${packageVersionData.MinorVersion}.${packageVersionData.PatchVersion} was already promoted in a previous build. For a given this version number, you can promote only one version.`);
+                let errorMessage:string = `The package version ${packageVersionData.MajorVersion}.${packageVersionData.MinorVersion}.${packageVersionData.PatchVersion} was already promoted in a previous build. For a given this version number, you can promote only one version.`;
+                if (this.fail_if_is_alreadyPromoted) {
+                    throw new Error(errorMessage);
+                } else {
+                    SFPLogger.log(COLOR_HEADER(errorMessage));
+                }
             } else throw e;
         }
     }
diff --git a/packages/sfp-cli/src/impl/deploy/DeployImpl.ts b/packages/sfp-cli/src/impl/deploy/DeployImpl.ts
index 55f17ab00..e1f7c77a5 100644
--- a/packages/sfp-cli/src/impl/deploy/DeployImpl.ts
+++ b/packages/sfp-cli/src/impl/deploy/DeployImpl.ts
@@ -58,6 +58,7 @@ export interface DeployProps {
     releaseConfigPath?: string;
     filterByProvidedArtifacts?: string[];
     impactedPackagesAsPerBranch?: Map<string, string[]>;
+    failifalreadypromoted?: boolean;
 }
 
 export default class DeployImpl {
@@ -409,7 +410,8 @@ export default class DeployImpl {
                     let promoteUnlockedPackageImpl: PromoteUnlockedPackageImpl = new PromoteUnlockedPackageImpl(
                         sourceDirectory,
                         sfpPackage.package_version_id,
-                        this.props.devhubUserName
+                        this.props.devhubUserName,
+                        this.props.failifalreadypromoted
                     );
                     await promoteUnlockedPackageImpl.promote();
                 }
diff --git a/packages/sfp-cli/src/impl/release/ReleaseImpl.ts b/packages/sfp-cli/src/impl/release/ReleaseImpl.ts
index af77f5846..0bb7de5d7 100644
--- a/packages/sfp-cli/src/impl/release/ReleaseImpl.ts
+++ b/packages/sfp-cli/src/impl/release/ReleaseImpl.ts
@@ -36,6 +36,7 @@ export interface ReleaseProps {
     devhubUserName: string;
     branch: string;
     directory: string;
+    failifalreadypromoted: boolean
 }
 
 type DeploymentStatus = {
@@ -46,7 +47,7 @@ type DeploymentStatus = {
 
 export default class ReleaseImpl {
 
-   
+
 
     constructor(private props: ReleaseProps, private logger?: Logger) {}
 
@@ -81,7 +82,7 @@ export default class ReleaseImpl {
         SFPLogger.log(`Clearing installation output`, LoggerLevel.TRACE, this.logger);
         FileOutputHandler.getInstance().deleteOutputFile(`deployment-breakdown.md`);
         FileOutputHandler.getInstance().deleteOutputFile(`release-changelog.md`);
-      
+
         let deploymentResults = await this.deployArtifacts(sortedReleaseDefns);
 
         //Get all suceeded deploys
@@ -229,7 +230,7 @@ export default class ReleaseImpl {
         return numberOfCommits;
     }
 
-  
+
 
     private async deployArtifacts(releaseDefinitions: ReleaseDefinition[]): Promise<DeploymentStatus[]> {
         let deploymentResults: { releaseDefinition: ReleaseDefinition; result: DeploymentResult }[] = [];
@@ -257,6 +258,7 @@ export default class ReleaseImpl {
                     : false,
                 promotePackagesBeforeDeploymentToOrg: releaseDefinition.promotePackagesBeforeDeploymentToOrg,
                 devhubUserName: this.props.devhubUserName,
+                failifalreadypromoted: this.props.failifalreadypromoted
             };
 
             FileOutputHandler.getInstance().appendOutput(`deployment-breakdown.md`,`## ReleaseConfig: ${releaseDefinition.releaseConfigName?releaseDefinition.releaseConfigName:""}\n`);
@@ -281,7 +283,7 @@ export default class ReleaseImpl {
 
         let sfpPackageInquirer: SfpPackageInquirer = new SfpPackageInquirer(sfpPackages, logger);
         let sfdxProjectConfig = sfpPackageInquirer.getLatestProjectConfig();
-       
+
         let releaseDefinitionSorter = new ReleaseDefinitionSorter();
         return releaseDefinitionSorter.sortReleaseDefinitions(releaseDefns, sfdxProjectConfig, logger);
     }