forked from dxatscale/sfpowerscripts
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(impact): add impact:package and impact:releaseconfig commands
Add two new helper commands that can be used to figure out impacted packages or impacted releaseconfigs by comparing against last know gith t tags
- Loading branch information
1 parent
018e283
commit ec884dc
Showing
7 changed files
with
436 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"commandDescription": "Figures out impacted packages of a project, due to a change from the last known tags", | ||
"baseCommitOrBranchFlagDescription": "The base branch on which the git tags should be used" | ||
} |
6 changes: 6 additions & 0 deletions
6
packages/sfpowerscripts-cli/messages/impact_release_config.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"commandDescription": "Figures out impacted release configurations of a project, due to a change,from the last known tags", | ||
"releaseConfigFileFlagDescription":"Path to the directory containing release defns", | ||
"baseCommitOrBranchFlagDescription": "The base branch on which the git tags should be used from", | ||
"filterByFlagDescription": "Filter by a specific release config name" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
packages/sfpowerscripts-cli/src/commands/impact/package.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { Messages } from '@salesforce/core'; | ||
import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; | ||
import { Stage } from '../../impl/Stage'; | ||
import SFPLogger, { COLOR_KEY_MESSAGE, ConsoleLogger } from '@dxatscale/sfp-logger'; | ||
import { Flags } from '@oclif/core'; | ||
import { loglevel } from '../../flags/sfdxflags'; | ||
import { ZERO_BORDER_TABLE } from '../../ui/TableConstants'; | ||
import ImpactedPackageResolver, { ImpactedPackageProps } from '../../impl/impact/ImpactedPackagesResolver'; | ||
const Table = require('cli-table'); | ||
import path from 'path'; | ||
import * as fs from 'fs-extra'; | ||
|
||
|
||
Messages.importMessagesDirectory(__dirname); | ||
const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'impact_package'); | ||
|
||
export default class Package extends SfpowerscriptsCommand { | ||
public static flags = { | ||
loglevel, | ||
basebranch: Flags.string({ | ||
description: messages.getMessage('baseCommitOrBranchFlagDescription'), | ||
required: true, | ||
}) | ||
}; | ||
|
||
public static description = messages.getMessage('commandDescription'); | ||
private props: ImpactedPackageProps; | ||
|
||
async execute(): Promise<any> { | ||
// Read Manifest | ||
|
||
this.props = { | ||
currentStage: Stage.BUILD, | ||
baseBranch: this.flags.basebranch, | ||
diffOptions: { | ||
useLatestGitTags: true, | ||
skipPackageDescriptorChange: false, | ||
}, | ||
}; | ||
|
||
const impactedPackageResolver = new ImpactedPackageResolver(this.props, new ConsoleLogger()); | ||
|
||
let packagesToBeBuiltWithReasons = await impactedPackageResolver.getImpactedPackages(); | ||
let packageDiffTable = this.createDiffPackageScheduledDisplayedAsATable(packagesToBeBuiltWithReasons); | ||
const packagesToBeBuilt = Array.from(packagesToBeBuiltWithReasons.keys()); | ||
|
||
//Log Packages to be built | ||
SFPLogger.log(COLOR_KEY_MESSAGE('Packages impacted...')); | ||
SFPLogger.log(packageDiffTable.toString()); | ||
|
||
|
||
const outputPath = path.join(process.cwd(), 'impacted-package.json'); | ||
if (packagesToBeBuilt && packagesToBeBuilt.length > 0) | ||
fs.writeFileSync(outputPath, JSON.stringify(packagesToBeBuilt, null, 2)); | ||
else fs.writeFileSync(outputPath, JSON.stringify([], null, 2)); | ||
SFPLogger.log(`Impacted packages if any written to ${outputPath}`); | ||
|
||
|
||
return packagesToBeBuilt; | ||
} | ||
|
||
private createDiffPackageScheduledDisplayedAsATable(packagesToBeBuilt: Map<string, any>) { | ||
let tableHead = ['Package', 'Reason', 'Last Known Tag']; | ||
let table = new Table({ | ||
head: tableHead, | ||
chars: ZERO_BORDER_TABLE, | ||
}); | ||
for (const pkg of packagesToBeBuilt.keys()) { | ||
let item = [ | ||
pkg, | ||
packagesToBeBuilt.get(pkg).reason, | ||
packagesToBeBuilt.get(pkg).tag ? packagesToBeBuilt.get(pkg).tag : '', | ||
]; | ||
table.push(item); | ||
} | ||
return table; | ||
} | ||
|
||
|
||
} |
128 changes: 128 additions & 0 deletions
128
packages/sfpowerscripts-cli/src/commands/impact/releaseconfig.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import { Messages } from '@salesforce/core'; | ||
import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; | ||
import { Stage } from '../../impl/Stage'; | ||
import * as fs from 'fs-extra'; | ||
import SFPLogger, { COLOR_KEY_MESSAGE, ConsoleLogger } from '@dxatscale/sfp-logger'; | ||
import { Flags } from '@oclif/core'; | ||
import { loglevel } from '../../flags/sfdxflags'; | ||
import { ZERO_BORDER_TABLE } from '../../ui/TableConstants'; | ||
import path from 'path'; | ||
import ImpactedPackageResolver, { ImpactedPackageProps } from '../../impl/impact/ImpactedPackagesResolver'; | ||
import ImpactedRelaseConfigResolver from '../../impl/impact/ImpactedReleaseConfig'; | ||
const Table = require('cli-table'); | ||
|
||
|
||
Messages.importMessagesDirectory(__dirname); | ||
const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'impact_release_config'); | ||
|
||
export default class ReleaseConfig extends SfpowerscriptsCommand { | ||
public static flags = { | ||
loglevel, | ||
basebranch: Flags.string({ | ||
description: messages.getMessage('baseCommitOrBranchFlagDescription'), | ||
required: true, | ||
}), | ||
releaseconfig: Flags.string({ | ||
description: messages.getMessage('releaseConfigFileFlagDescription'), | ||
default: 'config', | ||
}), | ||
filterBy: Flags.string({ | ||
description: messages.getMessage('filterByFlagDescription'), | ||
}), | ||
}; | ||
|
||
public static description = messages.getMessage('commandDescription'); | ||
private props: ImpactedPackageProps; | ||
isMultiConfigFilesEnabled: boolean; | ||
|
||
async execute(): Promise<any> { | ||
// Read Manifest | ||
|
||
this.props = { | ||
branch: this.flags.branch, | ||
currentStage: Stage.VALIDATE, | ||
baseBranch: this.flags.basebranch, | ||
diffOptions: { | ||
useLatestGitTags: true, | ||
skipPackageDescriptorChange: false, | ||
}, | ||
}; | ||
|
||
const impactedPackageResolver = new ImpactedPackageResolver(this.props, new ConsoleLogger()); | ||
|
||
let packagesToBeBuiltWithReasons = await impactedPackageResolver.getImpactedPackages(); | ||
let packageDiffTable = this.createDiffPackageScheduledDisplayedAsATable(packagesToBeBuiltWithReasons); | ||
const packagesToBeBuilt = Array.from(packagesToBeBuiltWithReasons.keys()); | ||
|
||
//Log Packages to be built | ||
SFPLogger.log(COLOR_KEY_MESSAGE('Packages impacted...')); | ||
SFPLogger.log(packageDiffTable.toString()); | ||
|
||
const impactedReleaseConfigResolver = new ImpactedRelaseConfigResolver(); | ||
|
||
let impactedReleaseConfigs = impactedReleaseConfigResolver.getImpactedReleaseConfigs( | ||
packagesToBeBuilt, | ||
this.flags.releaseconfig, | ||
this.flags.filterBy | ||
); | ||
|
||
let impactedReleaseConfigTable = this.createImpactedReleaseConfigsAsATable(impactedReleaseConfigs.include); | ||
//Log Packages to be built | ||
SFPLogger.log(COLOR_KEY_MESSAGE('Release Configs impacted...')); | ||
SFPLogger.log(impactedReleaseConfigTable.toString()); | ||
|
||
const outputPath = path.join(process.cwd(), 'impacted-release-configs.json'); | ||
if (impactedReleaseConfigs && impactedReleaseConfigs.include.length > 0) | ||
fs.writeFileSync(outputPath, JSON.stringify(impactedReleaseConfigs, null, 2)); | ||
else fs.writeFileSync(outputPath, JSON.stringify([], null, 2)); | ||
if (!this.flags.filterBy) SFPLogger.log(`Impacted release configs written to ${outputPath}`); | ||
else | ||
SFPLogger.log( | ||
`Impacted release configs written to ${outputPath},${ | ||
impactedReleaseConfigs.include[0]?.releaseName | ||
? `filtered impacted release config found for ${impactedReleaseConfigs.include[0]?.releaseName}` | ||
: `no impacted release config found for ${this.flags.filterBy}` | ||
}` | ||
); | ||
|
||
return impactedReleaseConfigs.include; | ||
} | ||
|
||
private createDiffPackageScheduledDisplayedAsATable(packagesToBeBuilt: Map<string, any>) { | ||
let tableHead = ['Package', 'Reason', 'Last Known Tag']; | ||
if (this.isMultiConfigFilesEnabled && this.props.currentStage == Stage.BUILD) { | ||
tableHead.push('Scratch Org Config File'); | ||
} | ||
let table = new Table({ | ||
head: tableHead, | ||
chars: ZERO_BORDER_TABLE, | ||
}); | ||
for (const pkg of packagesToBeBuilt.keys()) { | ||
let item = [ | ||
pkg, | ||
packagesToBeBuilt.get(pkg).reason, | ||
packagesToBeBuilt.get(pkg).tag ? packagesToBeBuilt.get(pkg).tag : '', | ||
]; | ||
|
||
table.push(item); | ||
} | ||
return table; | ||
} | ||
|
||
private createImpactedReleaseConfigsAsATable(impacatedReleaseConfigs: any[]) { | ||
let tableHead = ['Release/Domain Name', 'Pools', 'ReleaseConfig Path']; | ||
let table = new Table({ | ||
head: tableHead, | ||
chars: ZERO_BORDER_TABLE, | ||
}); | ||
for (const impactedReleaseConfig of impacatedReleaseConfigs) { | ||
let item = [ | ||
impactedReleaseConfig.releaseName, | ||
impactedReleaseConfig.domainNameUsedForPools, | ||
impactedReleaseConfig.filePath, | ||
]; | ||
table.push(item); | ||
} | ||
return table; | ||
} | ||
} |
140 changes: 140 additions & 0 deletions
140
packages/sfpowerscripts-cli/src/impl/impact/ImpactedPackagesResolver.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import PackageDiffImpl, { PackageDiffOptions } from '@dxatscale/sfpowerscripts.core/lib/package/diff/PackageDiffImpl'; | ||
import { Stage } from '../Stage'; | ||
import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; | ||
import { PackageType } from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; | ||
import * as fs from 'fs-extra'; | ||
import { Logger } from '@dxatscale/sfp-logger'; | ||
import BuildCollections from '../parallelBuilder/BuildCollections'; | ||
|
||
export interface ImpactedPackageProps { | ||
projectDirectory?: string; | ||
branch?: string; | ||
configFilePath?: string; | ||
currentStage: Stage; | ||
baseBranch?: string; | ||
diffOptions?: PackageDiffOptions; | ||
includeOnlyPackages?: string[]; | ||
} | ||
|
||
export default class ImpactedPackageResolver { | ||
|
||
|
||
constructor(private props: ImpactedPackageProps, private logger: Logger) { | ||
} | ||
|
||
async getImpactedPackages(): Promise<Map<string, any>> { | ||
let projectConfig = ProjectConfig.getSFDXProjectConfig(this.props.projectDirectory); | ||
let packagesToBeBuilt = this.getPackagesToBeBuilt(this.props.projectDirectory); | ||
let packagesToBeBuiltWithReasons = await this.filterPackagesToBeBuiltByChanged( | ||
this.props.projectDirectory, | ||
projectConfig, | ||
packagesToBeBuilt | ||
); | ||
|
||
return packagesToBeBuiltWithReasons; | ||
} | ||
|
||
/** | ||
* Get the file path of the forceignore for current stage, from project config. | ||
* Returns null if a forceignore path is not defined in the project config for the current stage. | ||
* | ||
* @param projectConfig | ||
* @param currentStage | ||
*/ | ||
private getPathToForceIgnoreForCurrentStage(projectConfig: any, currentStage: Stage): string { | ||
let stageForceIgnorePath: string; | ||
|
||
let ignoreFiles: { [key in Stage]: string } = projectConfig.plugins?.sfpowerscripts?.ignoreFiles; | ||
if (ignoreFiles) { | ||
Object.keys(ignoreFiles).forEach((key) => { | ||
if (key.toLowerCase() == currentStage) { | ||
stageForceIgnorePath = ignoreFiles[key]; | ||
} | ||
}); | ||
} | ||
|
||
if (stageForceIgnorePath) { | ||
if (fs.existsSync(stageForceIgnorePath)) { | ||
return stageForceIgnorePath; | ||
} else throw new Error(`${stageForceIgnorePath} forceignore file does not exist`); | ||
} else return null; | ||
} | ||
|
||
private async filterPackagesToBeBuiltByChanged(projectDirectory: string,projectConfig:any, allPackagesInRepo: any) { | ||
let packagesToBeBuilt = new Map<string, any>(); | ||
let buildCollections = new BuildCollections(projectDirectory); | ||
if (this.props.diffOptions) | ||
this.props.diffOptions.pathToReplacementForceIgnore = this.getPathToForceIgnoreForCurrentStage( | ||
projectConfig, | ||
this.props.currentStage | ||
); | ||
|
||
for await (const pkg of allPackagesInRepo) { | ||
let diffImpl: PackageDiffImpl = new PackageDiffImpl( | ||
this.logger, | ||
pkg, | ||
this.props.projectDirectory, | ||
this.props.diffOptions | ||
); | ||
let packageDiffCheck = await diffImpl.exec(); | ||
|
||
if (packageDiffCheck.isToBeBuilt) { | ||
packagesToBeBuilt.set(pkg, { | ||
reason: packageDiffCheck.reason, | ||
tag: packageDiffCheck.tag, | ||
}); | ||
//Add Bundles | ||
if (buildCollections.isPackageInACollection(pkg)) { | ||
buildCollections.listPackagesInCollection(pkg).forEach((packageInCollection) => { | ||
if (!packagesToBeBuilt.has(packageInCollection)) { | ||
packagesToBeBuilt.set(packageInCollection, { | ||
reason: 'Part of a build collection', | ||
}); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
return packagesToBeBuilt; | ||
} | ||
|
||
private getPackagesToBeBuilt(projectDirectory: string, includeOnlyPackages?: string[]): string[] { | ||
let projectConfig = ProjectConfig.getSFDXProjectConfig(projectDirectory); | ||
let sfdxpackages = []; | ||
|
||
let packageDescriptors = projectConfig['packageDirectories'].filter((pkg) => { | ||
if ( | ||
pkg.ignoreOnStage?.find((stage) => { | ||
stage = stage.toLowerCase(); | ||
return stage === this.props.currentStage; | ||
}) | ||
) | ||
return false; | ||
else return true; | ||
}); | ||
|
||
//Filter Packages | ||
if (includeOnlyPackages) { | ||
packageDescriptors = packageDescriptors.filter((pkg) => { | ||
if ( | ||
includeOnlyPackages.find((includedPkg) => { | ||
return includedPkg == pkg.package; | ||
}) | ||
) | ||
return true; | ||
else return false; | ||
}); | ||
} | ||
|
||
// Ignore aliasfied packages on stages fix #1289 | ||
packageDescriptors = packageDescriptors.filter((pkg) => { | ||
return !(this.props.currentStage === 'prepare' && pkg.aliasfy && pkg.type !== PackageType.Data); | ||
}); | ||
|
||
for (const pkg of packageDescriptors) { | ||
if (pkg.package && pkg.versionNumber) sfdxpackages.push(pkg['package']); | ||
} | ||
return sfdxpackages; | ||
} | ||
|
||
} |
Oops, something went wrong.