Skip to content
This repository has been archived by the owner on Mar 18, 2024. It is now read-only.

All package types must be promoted to be deployed to production #438

Merged
merged 3 commits into from
Mar 16, 2021
Merged
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
1 change: 1 addition & 0 deletions packages/core/src/PackageMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default interface PackageMetadata {
apexTestClassses?:string[];
isTriggerAllTests?:boolean;
isProfilesFound?:boolean;
isPromoted?: boolean;
tag?:string;
isDependencyValidated?:boolean;
destructiveChanges?:any;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/org/OrgDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const retry = require("async-retry");

export default class OrgDetails {

public static async getOrgDetails(username: string): Promise<string> {
public static async getOrgDetails(username: string): Promise<any> {

return await retry(
async bail => {
Expand Down
3 changes: 2 additions & 1 deletion packages/sfpowerscripts-cli/messages/deploy.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
"tagFlagDescription":"Tag the deploy with a label, useful for identification in metrics",
"validateModeFlagDescription": "Enable for validation deployments",
"skipIfAlreadyInstalled":"Skip the package installation if the package is already installed in the org",
"baselineorgFlagDescription": "The org against which the package skip should be baselined"
"baselineorgFlagDescription": "The org against which the package skip should be baselined",
"allowUnpromotedPackagesFlagDescription": "Allow un-promoted packages to be installed in production"
}
1 change: 1 addition & 0 deletions packages/sfpowerscripts-cli/messages/promote.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"commandDescription": "Promotes validated unlocked packages with code coverage greater than 75%",
"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"
}
15 changes: 12 additions & 3 deletions packages/sfpowerscripts-cli/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/sfpowerscripts-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@oclif/errors": "^1",
"@salesforce/command": "^2",
"@salesforce/core": "^2",
"adm-zip": "^0.5.0",
"adm-zip": "^0.5.4",
"async-retry": "^1.3.1",
"bottleneck": "^2.19.5",
"cli-table": "^0.3.4",
Expand All @@ -34,6 +34,7 @@
"ts-node": "^9.0.0"
},
"devDependencies": {
"@types/adm-zip": "^0.4.33",
"@types/jest": "^26.0.20",
"jest": "^26.6.3",
"ts-jest": "^26.5.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ export default class Deploy extends SfpowerscriptsCommand {
required: false,
dependsOn: ['skipifalreadyinstalled']
}),
allowunpromotedpackages: flags.boolean({
description: messages.getMessage("allowUnpromotedPackagesFlagDescription"),
hidden: true
})
};

public async execute() {
Expand Down Expand Up @@ -99,7 +103,8 @@ export default class Deploy extends SfpowerscriptsCommand {
skipIfPackageInstalled:this.flags.skipifalreadyinstalled,
logsGroupSymbol:this.flags.logsgroupsymbol,
currentStage:Stage.DEPLOY,
baselineOrg: this.flags.baselineorg
baselineOrg: this.flags.baselineorg,
isCheckIfPackagesPromoted: !this.flags.allowunpromotedpackages
}

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import * as fs from "fs-extra"
import PromoteUnlockedPackageImpl from "@dxatscale/sfpowerscripts.core/lib/sfdxwrappers/PromoteUnlockedPackageImpl"
import ArtifactFilePathFetcher from "@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactFilePathFetcher";
import PackageMetadata from "@dxatscale/sfpowerscripts.core/lib/PackageMetadata";
import AdmZip = require("adm-zip");
import path = require("path");

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'promote');
Expand All @@ -21,14 +23,28 @@ export default class Promote extends SfpowerscriptsCommand {
protected static requiresDevhubUsername = false;

protected static flagsConfig = {
artifactdir: flags.directory({required: true, char: 'd', description: messages.getMessage('artifactDirectoryFlagDescription'), default: 'artifacts'}),
devhubalias: flags.string({char: 'v', description: messages.getMessage('devhubAliasFlagDescription'), default: 'HubOrg'}),
artifactdir: flags.directory({
required: true, char: 'd',
description: messages.getMessage('artifactDirectoryFlagDescription'),
default: 'artifacts'
}),
outputdir: flags.directory({
required: true,
char: 'o',
description: messages.getMessage('outputDirectoryFlagDescription')
}),
devhubalias: flags.string({
char: 'v',
description: messages.getMessage('devhubAliasFlagDescription'),
default: 'HubOrg'
})
};


public async execute(){
if (this.flags.outputdir === this.flags.artifactdir)
throw new Error("--outputdir flag cannot be the same as --artifactdir flag");


console.log("-----------sfpowerscripts orchestrator ------------------");
console.log("command: promote");
console.log("---------------------------------------------------------");
Expand All @@ -44,31 +60,56 @@ export default class Promote extends SfpowerscriptsCommand {
throw new Error(`No artifacts found at ${this.flags.artifactdir}`);
}

fs.mkdirpSync(this.flags.outputdir);

let result: boolean = true;
let promotedPackages: string[] = [];
for (let artifact of artifacts) {
let packageMetadata: PackageMetadata = JSON.parse(
fs.readFileSync(artifact.packageMetadataFilePath, 'utf8')
);

if (packageMetadata.package_type === "unlocked") {
try {
try {
if (packageMetadata.package_type === "unlocked") {
let promoteUnlockedPackageImpl = new PromoteUnlockedPackageImpl(
artifact.sourceDirectoryPath,
packageMetadata.package_version_id,
this.flags.devhubalias
);
await promoteUnlockedPackageImpl.exec();

promotedPackages.push(packageMetadata.package_name);
} catch (err) {
result = false;

unpromotedPackages.push({
name: packageMetadata.package_name,
error: err.message
});
}

packageMetadata.isPromoted = true;
fs.writeFileSync(
artifact.packageMetadataFilePath,
JSON.stringify(packageMetadata, null, 4)
);

let artifactRootDir: string = path.dirname(artifact.sourceDirectoryPath);
let zip = new AdmZip();
zip.addLocalFolder(
artifactRootDir,
path.basename(artifactRootDir)
);


let zipArtifactFilepath: string = path.resolve(
this.flags.outputdir,
packageMetadata.package_name + `_sfpowerscripts_artifact_` +
this.substituteBuildNumberWithPreRelease(packageMetadata.package_version_number) +
`.zip`
);
zip.writeZip(zipArtifactFilepath);


promotedPackages.push(packageMetadata.package_name);
} catch (err) {
result = false;

unpromotedPackages.push({
name: packageMetadata.package_name,
error: err.message
});
}
}
console.log(`Promoted packages:`, promotedPackages);
Expand All @@ -89,4 +130,23 @@ export default class Promote extends SfpowerscriptsCommand {
process.exitCode = 1;
}
}

private substituteBuildNumberWithPreRelease(
packageVersionNumber: string
) {
let segments = packageVersionNumber.split(".");

if (segments.length === 4) {
packageVersionNumber = segments.reduce(
(version, segment, segmentsIdx) => {
if (segmentsIdx === 3) return version + "-" + segment;
else return version + "." + segment;
}
);
}

return packageVersionNumber;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export default class Promote extends SfpowerscriptsCommand {

protected static flagsConfig = {
artifactdir: flags.directory({
required: true, char: 'd',
required: true,
char: 'd',
description: messages.getMessage('artifactDirectoryFlagDescription'),
default: 'artifacts'
}),
Expand Down
20 changes: 19 additions & 1 deletion packages/sfpowerscripts-cli/src/impl/deploy/DeployImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import InstallSourcePackageImpl from "@dxatscale/sfpowerscripts.core/lib/sfpcomm
import InstallDataPackageImpl from "@dxatscale/sfpowerscripts.core/lib/sfpcommands/package/InstallDataPackageImpl";
import ArtifactInstallationStatusChecker from "@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactInstallationStatusChecker"
import InstalledAritfactsFetcher from "@dxatscale/sfpowerscripts.core/lib/artifacts/InstalledAritfactsFetcher"
import OrgDetails from "@dxatscale/sfpowerscripts.core/lib/org/OrgDetails";

import fs = require("fs");
import path = require("path");
Expand Down Expand Up @@ -47,6 +48,7 @@ export interface DeployProps {
packageLogger?: any;
currentStage?: Stage;
baselineOrg?:string;
isCheckIfPackagesPromoted?: boolean;
}

export default class DeployImpl {
Expand All @@ -58,6 +60,8 @@ export default class DeployImpl {
testFailure: string;
error: any;
}> {
let orgDetails = await OrgDetails.getOrgDetails(this.props.targetUsername);

let deployed: string[] = [];
let failed: string[] = [];

Expand Down Expand Up @@ -108,7 +112,10 @@ export default class DeployImpl {
this.printArtifactVersions(queue,packagesToPackageInfo);
}


if (!orgDetails.IsSandbox) {
if (this.props.isCheckIfPackagesPromoted)
this.checkIfPackagesPromoted(queue, packagesToPackageInfo);
}

SFPStatsSender.logCount("deploy.scheduled",this.props.tags);
SFPStatsSender.logGauge(
Expand Down Expand Up @@ -265,6 +272,17 @@ export default class DeployImpl {
}


private checkIfPackagesPromoted(queue: any[], packagesToPackageInfo: { [p: string]: PackageInfo; }) {
let unpromotedPackages: string[] = [];
queue.forEach((pkg) => {
if (!packagesToPackageInfo[pkg.package].packageMetadata.isPromoted)
unpromotedPackages.push(pkg.package);
});

if (unpromotedPackages.length > 0)
throw new Error(`Packages must be promoted for deployments to production org: ${unpromotedPackages}`);
}

private printArtifactVersionsWhenSkipped(queue:any[],packagesToPackageInfo:{[p: string]: PackageInfo},isBaselinOrgModeActivated:boolean) {
this.printOpenLoggingGroup(`Full Deployment Breakdown`);
let maxTable = new Table({
Expand Down