From 64de1c2c007ee1747d16ab66d6131444205f6ff0 Mon Sep 17 00:00:00 2001 From: sfopsbot Date: Wed, 13 Dec 2023 17:50:38 +1100 Subject: [PATCH] chore(sfp): move to sfp cli from flxbl-io --- packages/sfpowerscripts-cli/CHANGELOG.md | 4 +- packages/sfpowerscripts-cli/README.md | 48 +- .../sfpowerscripts-cli/coverage/clover.xml | 2694 +++++++++++ .../coverage/coverage-final.json | 72 + .../coverage/lcov-report/base.css | 224 + .../coverage/lcov-report/block-navigation.js | 87 + .../coverage/lcov-report/core/git/Git.ts.html | 823 ++++ .../lcov-report/core/git/GitIdentity.ts.html | 187 + .../coverage/lcov-report/core/git/index.html | 131 + .../coverage/lcov-report/favicon.png | Bin 0 -> 445 bytes .../impl/changelog/OrgsUpdater.ts.html | 466 ++ .../impl/changelog/WorkItemUpdater.ts.html | 208 + .../lcov-report/impl/changelog/index.html | 131 + .../parallelBuilder/UndirectedGraph.ts.html | 232 + .../impl/parallelBuilder/index.html | 116 + .../impl/release/ReleaseDefinition.ts.html | 322 ++ .../lcov-report/impl/release/index.html | 116 + .../coverage/lcov-report/index.html | 581 +++ .../coverage/lcov-report/prettify.css | 1 + .../coverage/lcov-report/prettify.js | 2 + .../lcov-report/sort-arrow-sprite.png | Bin 0 -> 138 bytes .../coverage/lcov-report/sorter.js | 196 + .../lcov-report/src/ProjectValidation.ts.html | 346 ++ .../src/core/apex/ApexClassFetcher.ts.html | 166 + .../src/core/apex/ApexTriggerFetcher.ts.html | 169 + .../ApexCodeCoverageAggregateFetcher.ts.html | 211 + .../coverage/IndividualClassCoverage.ts.html | 313 ++ .../src/core/apex/coverage/index.html | 131 + .../lcov-report/src/core/apex/index.html | 131 + .../src/core/apextest/ApexTestSuite.ts.html | 175 + .../ImpactedApexTestClassFetcher.ts.html | 355 ++ .../lcov-report/src/core/apextest/index.html | 131 + .../core/artifacts/ArtifactFetcher.ts.html | 757 +++ .../lcov-report/src/core/artifacts/index.html | 116 + .../src/core/display/TableConstants.ts.html | 199 + .../lcov-report/src/core/display/index.html | 116 + .../lcov-report/src/core/git/Git.ts.html | 823 ++++ .../src/core/git/GitDiffUtil.ts.html | 610 +++ .../src/core/git/GitIdentity.ts.html | 187 + .../lcov-report/src/core/git/GitTags.ts.html | 538 +++ .../lcov-report/src/core/git/index.html | 161 + .../src/core/metadata/MetadataFiles.ts.html | 1114 +++++ .../src/core/metadata/MetadataInfo.ts.html | 727 +++ .../src/core/metadata/SettingsFetcher.ts.html | 151 + .../lcov-report/src/core/metadata/index.html | 146 + .../lcov-report/src/core/org/SFPOrg.ts.html | 898 ++++ .../lcov-report/src/core/org/index.html | 116 + .../InstalledPackagesQueryExecutor.ts.html | 127 + .../src/core/org/packageQuery/index.html | 116 + .../src/core/package/SfpPackage.ts.html | 514 ++ .../core/package/SfpPackageBuilder.ts.html | 919 ++++ .../package/analyser/AnalyzerRegistry.ts.html | 145 + .../core/package/analyser/FHTAnalyzer.ts.html | 313 ++ .../core/package/analyser/FTAnalyzer.ts.html | 307 ++ .../package/analyser/PicklistAnalyzer.ts.html | 241 + .../src/core/package/analyser/index.html | 161 + .../components/PackageManifest.ts.html | 898 ++++ .../components/PackageToComponent.ts.html | 169 + .../src/core/package/components/index.html | 131 + .../coverage/PackageTestCoverage.ts.html | 904 ++++ .../src/core/package/coverage/index.html | 116 + ...ExternalPackage2DependencyResolver.ts.html | 394 ++ .../PackageDependencyResolver.ts.html | 892 ++++ .../TransitiveDependencyResolver.ts.html | 412 ++ .../src/core/package/dependencies/index.html | 146 + .../EntitlementVersionFilter.ts.html | 415 ++ .../core/package/deploymentFilters/index.html | 116 + .../package/diff/PackageComponentDiff.ts.html | 1357 ++++++ .../core/package/diff/PackageDiffImpl.ts.html | 583 +++ .../src/core/package/diff/index.html | 131 + .../lcov-report/src/core/package/index.html | 131 + .../CreateDataPackageImpl.ts.html | 343 ++ .../CreateDiffPackageImpl.ts.html | 898 ++++ .../packageCreators/CreatePackage.ts.html | 523 ++ .../core/package/packageCreators/index.html | 146 + .../AssignPermissionSetFetcher.ts.html | 154 + .../DestructiveManifestPathFetcher.ts.html | 172 + .../ReconcileProfilePropertyFetcher.ts.html | 115 + .../core/package/propertyFetchers/index.html | 146 + .../validators/PackageEmptyChecker.ts.html | 340 ++ .../src/core/package/validators/index.html | 116 + .../version/Package2VersionFetcher.ts.html | 412 ++ .../version/PackageVersionUpdater.ts.html | 139 + .../src/core/package/version/index.html | 131 + .../permsets/AssignPermissionSetsImpl.ts.html | 364 ++ .../permsets/PermissionSetFetcher.ts.html | 130 + .../PermissionSetGroupUpdateAwaiter.ts.html | 223 + .../lcov-report/src/core/permsets/index.html | 146 + .../src/core/project/ProjectConfig.ts.html | 928 ++++ .../UserDefinedExternalDependency.ts.html | 268 ++ .../lcov-report/src/core/project/index.html | 131 + .../core/queryHelper/ChunkCollection.ts.html | 196 + .../src/core/queryHelper/QueryHelper.ts.html | 139 + .../src/core/queryHelper/index.html | 131 + .../src/core/stats/NativeMetricSender.ts.html | 151 + .../src/core/stats/SFPStatsSender.ts.html | 427 ++ .../lcov-report/src/core/stats/index.html | 131 + .../DataDogMetricSender.ts.html | 241 + .../NewRelicMetricSender.ts.html | 283 ++ .../SplunkMetricSender.ts.html | 232 + .../stats/nativeMetricSenderImpl/index.html | 146 + .../src/core/utils/AliasList.ts.html | 133 + .../src/core/utils/ChunkArray.ts.html | 118 + .../lcov-report/src/core/utils/Delay.ts.html | 94 + .../src/core/utils/FileSystem.ts.html | 241 + .../src/core/utils/Fileutils.ts.html | 697 +++ .../src/core/utils/ObjectCRUDHelper.ts.html | 199 + .../core/utils/VersionNumberConverter.ts.html | 184 + .../core/utils/extractDomainFromUrl.ts.html | 115 + .../lcov-report/src/core/utils/index.html | 236 + .../src/core/utils/xml2json.ts.html | 115 + .../src/impl/changelog/CommitUpdater.ts.html | 256 + .../src/impl/changelog/OrgsUpdater.ts.html | 466 ++ .../impl/changelog/WorkItemUpdater.ts.html | 208 + .../lcov-report/src/impl/changelog/index.html | 146 + .../src/impl/dependency/ShrinkImpl.ts.html | 328 ++ .../src/impl/dependency/index.html | 116 + .../parallelBuilder/BuildCollections.ts.html | 280 ++ .../parallelBuilder/UndirectedGraph.ts.html | 232 + .../src/impl/parallelBuilder/index.html | 131 + .../impl/release/ReleaseDefinition.ts.html | 322 ++ .../lcov-report/src/impl/release/index.html | 116 + .../coverage/lcov-report/src/index.html | 116 + .../src/utils/Get18DigitSalesforceId.ts.html | 142 + .../coverage/lcov-report/src/utils/index.html | 116 + .../utils/Get18DigitSalesforceId.ts.html | 142 + .../coverage/lcov-report/utils/index.html | 116 + .../sfpowerscripts-cli/coverage/lcov.info | 4285 +++++++++++++++++ .../messages/generate_changelog.json | 2 +- .../messages/install_package.json | 2 +- .../messages/install_source_package.json | 2 +- .../messages/install_unlocked_package.json | 2 +- .../messages/metrics_report.json | 2 +- .../messages/pool_delete.json | 2 +- packages/sfpowerscripts-cli/package.json | 52 +- .../resources/metadatainfo.json | 1075 +++++ .../schemas/pooldefinition.schema.json | 147 + .../schemas/releasedefinition.schema.json | 4 +- .../releasedefinitiongenerator.schema.json | 2 +- .../schemas/sfdx-project.schema.json | 20 +- packages/sfpowerscripts-cli/src/BuildBase.ts | 20 +- .../src/InstallPackageCommand.ts | 20 +- .../src/PackageCreateCommand.ts | 22 +- .../src/ProjectValidation.ts | 8 +- ...SfpowerscriptsCommand.ts => SfpCommand.ts} | 43 +- .../src/commands/apextests/trigger.ts | 20 +- .../src/commands/artifacts/fetch.ts | 12 +- .../src/commands/artifacts/query.ts | 14 +- .../src/commands/changelog/generate.ts | 8 +- .../src/commands/dependency/expand.ts | 14 +- .../src/commands/dependency/install.ts | 18 +- .../src/commands/dependency/shrink.ts | 10 +- .../src/commands/impact/package.ts | 8 +- .../src/commands/impact/releaseconfig.ts | 8 +- .../src/commands/metrics/report.ts | 18 +- .../src/commands/orchestrator/build.ts | 2 +- .../src/commands/orchestrator/deploy.ts | 16 +- .../src/commands/orchestrator/prepare.ts | 29 +- .../src/commands/orchestrator/promote.ts | 22 +- .../src/commands/orchestrator/publish.ts | 40 +- .../src/commands/orchestrator/quickbuild.ts | 2 +- .../src/commands/orchestrator/release.ts | 10 +- .../src/commands/orchestrator/validate.ts | 12 +- .../orchestrator/validateAgainstOrg.ts | 10 +- .../src/commands/package/data/create.ts | 10 +- .../src/commands/package/data/install.ts | 10 +- .../src/commands/package/install.ts | 12 +- .../src/commands/package/source/create.ts | 8 +- .../src/commands/package/source/install.ts | 22 +- .../src/commands/package/unlocked/create.ts | 8 +- .../src/commands/package/unlocked/install.ts | 10 +- .../src/commands/pool/delete.ts | 24 +- .../src/commands/pool/fetch.ts | 28 +- .../src/commands/pool/list.ts | 20 +- .../src/commands/pool/metrics/publish.ts | 38 +- .../src/commands/pool/org/delete.ts | 10 +- .../src/commands/profile/merge.ts | 14 +- .../src/commands/profile/reconcile.ts | 16 +- .../src/commands/profile/retrieve.ts | 12 +- .../commands/releasedefinition/generate.ts | 8 +- .../src/commands/repo/patch.ts | 26 +- .../src/core/apex/ApexClassFetcher.ts | 28 + .../src/core/apex/ApexTriggerFetcher.ts | 28 + .../ApexCodeCoverageAggregateFetcher.ts | 42 + .../apex/coverage/IndividualClassCoverage.ts | 76 + .../src/core/apex/parser/ApexTypeFetcher.ts | 134 + .../apex/parser/listeners/ApexTypeListener.ts | 38 + .../src/core/apextest/ApexTestSuite.ts | 30 + .../src/core/apextest/ClearCodeCoverage.ts | 53 + .../apextest/ImpactedApexTestClassFetcher.ts | 90 + .../src/core/apextest/JSONReporter.ts | 174 + .../src/core/apextest/TestOptions.ts | 73 + .../src/core/apextest/TestReportDisplayer.ts | 118 + .../src/core/apextest/TriggerApexTests.ts | 767 +++ .../src/core/artifacts/ArtifactFetcher.ts | 224 + .../artifacts/generators/ArtifactGenerator.ts | 105 + .../changelog/GeneratePackageChangelog.ts | 76 + .../interfaces/GenericChangelogInterfaces.ts | 30 + .../dependency/ChangedComponentsFetcher.ts | 93 + .../src/core/dependency/Component.ts | 16 + .../core/dependency/DependencyViolation.ts | 7 + .../src/core/dependency/Entrypoint.ts | 20 + .../core/deployers/DeploySourceToOrgImpl.ts | 230 + .../src/core/deployers/DeploymentExecutor.ts | 17 + .../deployers/DeploymentSettingsService.ts | 64 + .../display/DependencyViolationDisplayer.ts | 31 + .../src/core/display/DeployErrorDisplayer.ts | 103 + .../core/display/DeploymentOptionDisplayer.ts | 51 + .../display/ExternalDependencyDisplayer.ts | 30 + .../display/InstalledArtifactsDisplayer.ts | 22 + .../display/InstalledPackagesDisplayer.ts | 22 + .../core/display/PackageComponentPrinter.ts | 27 + .../display/PackageDependencyDisplayer.ts | 35 + .../core/display/PackageMetadataPrinter.ts | 38 + .../src/core/display/PushErrorDisplayer.ts | 75 + .../src/core/display/TableConstants.ts | 38 + .../sfpowerscripts-cli/src/core/git/Git.ts | 246 + .../src/core/git/GitDiffUtil.ts | 175 + .../src/core/git/GitIdentity.ts | 34 + .../src/core/git/GitTags.ts | 151 + .../src/core/ignore/IgnoreFiles.ts | 13 + .../src/core/limits/LimitsFetcher.ts | 30 + .../src/core/metadata/CustomFieldFetcher.ts | 45 + .../src/core/metadata/MetadataFetcher.ts | 71 + .../src/core/metadata/MetadataFiles.ts | 343 ++ .../src/core/metadata/MetadataInfo.ts | 214 + .../src/core/metadata/SettingsFetcher.ts | 22 + .../src/core/org/OrgDetailsFetcher.ts | 120 + .../src/core/org/OrganizationFetcher.ts | 12 + .../sfpowerscripts-cli/src/core/org/SFPOrg.ts | 271 ++ .../src/core/org/ScratchOrgInfoFetcher.ts | 30 + .../InstalledPackagesQueryExecutor.ts | 14 + .../src/core/package/Package2Detail.ts | 10 + .../src/core/package/SfpPackage.ts | 143 + .../src/core/package/SfpPackageBuilder.ts | 278 ++ .../src/core/package/SfpPackageInquirer.ts | 178 + .../src/core/package/SfpPackageInstaller.ts | 63 + .../core/package/analyser/AnalyzerRegistry.ts | 20 + .../src/core/package/analyser/FHTAnalyzer.ts | 76 + .../src/core/package/analyser/FTAnalyzer.ts | 74 + .../core/package/analyser/PackageAnalyzer.ts | 12 + .../core/package/analyser/PicklistAnalyzer.ts | 52 + .../DeployDestructiveManifestToOrgImpl.ts | 129 + .../core/package/components/MetadataCount.ts | 18 + .../package/components/PackageManifest.ts | 271 ++ .../package/components/PackageToComponent.ts | 28 + .../ReconcileProfileAgainstOrgImpl.ts | 53 + .../package/coverage/PackageTestCoverage.ts | 273 ++ .../coverage/PackageVersionCoverage.ts | 39 + .../ExternalPackage2DependencyResolver.ts | 103 + .../dependencies/PackageDependencyResolver.ts | 269 ++ .../TransitiveDependencyResolver.ts | 109 + .../DeploymentCustomizer.ts | 27 + .../deploymentCustomizers/FHTEnabler.ts | 121 + .../deploymentCustomizers/FTEnabler.ts | 107 + .../MetadataDeploymentCustomizer.ts | 83 + .../deploymentCustomizers/PicklistEnabler.ts | 218 + .../PostDeployersRegistry.ts | 18 + .../PreDeployersRegistry.ts | 14 + .../deploymentFilters/DeploymentFilter.ts | 13 + .../DeploymentFilterRegistry.ts | 18 + .../EntitlementVersionFilter.ts | 110 + .../core/package/diff/PackageComponentDiff.ts | 424 ++ .../src/core/package/diff/PackageDiffImpl.ts | 166 + .../generators/SfpPackageContentGenerator.ts | 301 ++ .../packageCreators/CreateDataPackageImpl.ts | 86 + .../packageCreators/CreateDiffPackageImpl.ts | 271 ++ .../package/packageCreators/CreatePackage.ts | 146 + .../CreateSourcePackageImpl.ts | 117 + .../CreateUnlockedPackageImpl.ts | 262 + .../SourceToMDAPIConvertor.ts | 52 + .../InstallDataPackageImpl.ts | 104 + .../packageInstallers/InstallPackage.ts | 497 ++ .../InstallSourcePackageImpl.ts | 397 ++ .../InstallUnlockedPackage.ts | 91 + .../InstallUnlockedPackageCollection.ts | 134 + .../InstallUnlockedPackageImpl.ts | 96 + .../PackageInstallationResult.ts | 15 + .../packageMerger/PackageMergeManager.ts | 160 + .../promote/PromoteUnlockedPackageImpl.ts | 34 + .../AssignPermissionSetFetcher.ts | 23 + .../DestructiveManifestPathFetcher.ts | 29 + .../propertyFetchers/PropertyFetcher.ts | 11 + .../ReconcileProfilePropertyFetcher.ts | 10 + .../package/validators/PackageEmptyChecker.ts | 85 + .../package/version/Package2VersionFetcher.ts | 109 + .../version/Package2VersionInstaller.ts | 26 + .../package/version/PackageVersionLister.ts | 62 + .../package/version/PackageVersionUpdater.ts | 18 + .../src/core/permsets/AssignPermissionSets.ts | 17 + .../core/permsets/AssignPermissionSetsImpl.ts | 93 + .../src/core/permsets/PermissionSetFetcher.ts | 15 + .../PermissionSetGroupUpdateAwaiter.ts | 46 + .../src/core/project/ProjectConfig.ts | 281 ++ .../project/UserDefinedExternalDependency.ts | 61 + .../src/core/queryHelper/ChunkCollection.ts | 38 + .../src/core/queryHelper/QueryHelper.ts | 18 + .../src/core/scratchorg/PasswordGenerator.ts | 41 + .../src/core/scratchorg/ScratchOrg.ts | 18 + .../src/core/scratchorg/ScratchOrgOperator.ts | 149 + .../scratchorg/pool/ClientSourceTracking.ts | 205 + .../scratchorg/pool/OrphanedOrgsDeleteImpl.ts | 47 + .../src/core/scratchorg/pool/PoolBaseImpl.ts | 22 + .../src/core/scratchorg/pool/PoolConfig.ts | 40 + .../core/scratchorg/pool/PoolCreateImpl.ts | 432 ++ .../core/scratchorg/pool/PoolDeleteImpl.ts | 65 + .../src/core/scratchorg/pool/PoolError.ts | 13 + .../src/core/scratchorg/pool/PoolFetchImpl.ts | 244 + .../core/scratchorg/pool/PoolJobExecutor.ts | 41 + .../src/core/scratchorg/pool/PoolListImpl.ts | 48 + .../core/scratchorg/pool/PoolOrgDeleteImpl.ts | 26 + .../prequisitecheck/IsValidSfdxAuthUrl.ts | 19 + .../pool/prequisitecheck/PreRequisiteCheck.ts | 63 + .../pool/services/fetchers/GetUserEmail.ts | 28 + .../fetchers/ScratchOrgInfoFetcher.ts | 187 + .../fetchers/ScratchOrgLimitsFetcher.ts | 13 + .../updaters/ScratchOrgInfoAssigner.ts | 24 + .../scriptExecutor/ScriptExecutorHelpers.ts | 19 + .../src/core/sfdmuwrapper/SFDMURunImpl.ts | 28 + .../src/core/stats/NativeMetricSender.ts | 22 + .../src/core/stats/SFPStatsSender.ts | 114 + .../DataDogMetricSender.ts | 52 + .../NewRelicMetricSender.ts | 66 + .../SplunkMetricSender.ts | 49 + .../src/core/utils/AliasList.ts | 16 + .../src/core/utils/ChunkArray.ts | 11 + .../src/core/utils/DefaultShell.ts | 7 + .../src/core/utils/Delay.ts | 3 + .../src/core/utils/FileSystem.ts | 52 + .../src/core/utils/Fileutils.ts | 204 + .../src/core/utils/GetFormattedTime.ts | 6 + .../src/core/utils/ObjectCRUDHelper.ts | 38 + .../src/core/utils/OnExit.ts | 17 + .../src/core/utils/RandomId.ts | 9 + .../src/core/utils/VersionNumberConverter.ts | 33 + .../src/core/utils/extractDomainFromUrl.ts | 10 + .../src/core/utils/xml2json.ts | 10 + .../vlocitywrapper/VlocityInitialInstall.ts | 21 + .../vlocitywrapper/VlocityPackDeployImpl.ts | 31 + .../VlocityPackUpdateSettings.ts | 21 + .../core/vlocitywrapper/VlocityRefreshBase.ts | 31 + .../src/errors/ReleaseError.ts | 4 +- .../{SfpowerscriptsError.ts => SfpError.ts} | 2 +- .../src/errors/ValidateError.ts | 4 +- .../sfpowerscripts-cli/src/flags/duration.ts | 2 +- .../src/flags/orgApiVersion.ts | 4 +- .../sfpowerscripts-cli/src/flags/sfdxflags.ts | 6 +- .../impl/artifacts/FetchAnArtifactFromNPM.ts | 6 +- .../artifacts/FetchAnArtifactUsingScript.ts | 4 +- .../src/impl/artifacts/FetchArtifactsError.ts | 4 +- .../src/impl/artifacts/FetchImpl.ts | 8 +- .../src/impl/changelog/ChangelogImpl.ts | 12 +- .../src/impl/changelog/CommitUpdater.ts | 2 +- .../impl/changelog/ReadPackageChangelog.ts | 2 +- .../src/impl/changelog/ReleaseChangelog.ts | 2 +- .../impl/changelog/ReleaseChangelogUpdater.ts | 2 +- .../src/impl/changelog/WorkItemUpdater.ts | 2 +- .../src/impl/demoreelplayer/DemoReelPlayer.ts | 2 +- .../src/impl/dependency/ShrinkImpl.ts | 8 +- .../src/impl/deploy/DeployImpl.ts | 28 +- .../src/impl/deploy/PostDeployHook.ts | 6 +- .../src/impl/deploy/PreDeployHook.ts | 4 +- .../impl/impact/ImpactedPackagesResolver.ts | 10 +- .../impl/parallelBuilder/BuildCollections.ts | 2 +- .../src/impl/parallelBuilder/BuildImpl.ts | 50 +- .../impl/parallelBuilder/DependencyHelper.ts | 2 +- .../src/impl/prepare/PrepareImpl.ts | 30 +- .../src/impl/prepare/PrepareOrgJob.ts | 56 +- .../src/impl/release/ReleaseConfig.ts | 4 +- .../src/impl/release/ReleaseDefinition.ts | 4 +- .../release/ReleaseDefinitionGenerator.ts | 10 +- .../src/impl/release/ReleaseImpl.ts | 10 +- .../src/impl/repo/AlignImpl.ts | 2 +- .../src/impl/validate/Analyzer.ts | 4 +- .../src/impl/validate/ApexTestValidator.ts | 10 +- .../src/impl/validate/ValidateImpl.ts | 54 +- .../src/impl/validate/ValidateResult.ts | 2 +- .../src/outputs/FileOutputHandler.ts | 2 +- .../src/ui/GroupConsoleLogs.ts | 4 +- .../src/ui/OrgInfoDisplayer.ts | 6 +- .../tests/ProjectValidation.test.ts | 8 +- .../tests/core/apextest/ApexTestSuite.test.ts | 77 + .../artifacts/ArtifactsFromFileSystem.test.ts | 52 + .../coverage/IndividualClassCoverage.test.ts | 167 + .../tests/core/git/GitTags.test.ts | 83 + .../tests/core/org/ArtifactsToOrg.test.ts | 214 + .../tests/core/org/ListAllPackages.test.ts | 59 + .../package/Package2VersionFetcher.test.ts | 79 + .../core/package/PackageDiffImpl.test.ts | 295 ++ .../core/package/PackageManifest.test.ts | 300 ++ .../core/package/SFPackageBuilder.test.ts | 374 ++ .../package/analysers/FHTAnalyzer.test.ts | 360 ++ .../core/package/analysers/FTAnalyzer.test.ts | 363 ++ .../coverage/PackageTestCoverage.test.ts | 330 ++ .../PackageDependencyResolver.test.ts | 346 ++ .../TransitiveDependencyResolver.test.ts | 235 + .../EntitlementVersionFilter.test.ts | 500 ++ .../packageMerger/PackageMergeManager.test.ts | 39 + .../core2_sfpowerscripts_artifact_1.0.4-1.zip | Bin 0 -> 8405 bytes ...-mgmt3_sfpowerscripts_artifact_1.0.6-1.zip | Bin 0 -> 35211 bytes .../AssignPermissionSetFetcher.test.ts | 55 + .../DestructiveManifestPathFetcher.test.ts | 85 + .../ReconcileProfilePropertyFetcher.test.ts | 48 + .../permsets/AssignPermissionSets.test.ts | 198 + .../permsets/PermissionSetFetcher.test.ts | 115 + .../PermissionSetGroupUpdateAwaiter.test.ts | 35 + .../tests/core/project/ProjectConfig.test.ts | 278 ++ .../core/queryHelper/ChunkCollection.test.ts | 30 + .../tests/core/utils/ChunkArray.test.ts | 24 + .../tests/core/utils/FileSystem.test.ts | 35 + .../core/utils/extractDomainFromUrl.test.ts | 34 + .../tests/core/utils/resources/a/b/b1.file | 0 .../tests/core/utils/resources/a/b/c/c1.file | 0 .../tests/core/utils/resources/a/b/c/c2.file | 0 .../tests/core/utils/resources/a/b/d/d1.file | 0 .../core/utils/resources/a/b/d/x/x1.file | 0 .../tests/core/utils/resources/a/b/e/e1.file | 0 .../impl/changelog/CommitUpdater.test.ts | 2 +- .../tests/impl/changelog/OrgUpdater.test.ts | 2 +- .../impl/changelog/WorkItemUpdater.test.ts | 2 +- .../resources/ESBaseCodeLWCChangelog.json | 2 +- ...update_latestRelease_with_all_commits.json | 2 +- ..._latestRelease_with_subset_of_commits.json | 2 +- ..._update_latestRelease_with_work_items.json | 2 +- .../tests/impl/dependency/ShrinkImpl.test.ts | 6 +- .../parallelBuilder/BuildCollections.test.ts | 2 +- 426 files changed, 63824 insertions(+), 597 deletions(-) create mode 100644 packages/sfpowerscripts-cli/coverage/clover.xml create mode 100644 packages/sfpowerscripts-cli/coverage/coverage-final.json create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/base.css create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/block-navigation.js create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/core/git/Git.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/core/git/GitIdentity.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/core/git/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/favicon.png create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/impl/changelog/OrgsUpdater.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/impl/changelog/WorkItemUpdater.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/impl/changelog/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/impl/parallelBuilder/UndirectedGraph.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/impl/parallelBuilder/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/impl/release/ReleaseDefinition.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/impl/release/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/prettify.css create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/prettify.js create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/sort-arrow-sprite.png create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/sorter.js create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/ProjectValidation.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/ApexClassFetcher.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/ApexTriggerFetcher.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/coverage/ApexCodeCoverageAggregateFetcher.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/coverage/IndividualClassCoverage.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/coverage/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apextest/ApexTestSuite.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apextest/ImpactedApexTestClassFetcher.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apextest/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/artifacts/ArtifactFetcher.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/artifacts/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/display/TableConstants.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/display/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/Git.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/GitDiffUtil.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/GitIdentity.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/GitTags.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/MetadataFiles.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/MetadataInfo.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/SettingsFetcher.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/SFPOrg.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/packageQuery/InstalledPackagesQueryExecutor.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/packageQuery/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/SfpPackage.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/SfpPackageBuilder.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/AnalyzerRegistry.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/FHTAnalyzer.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/FTAnalyzer.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/PicklistAnalyzer.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/components/PackageManifest.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/components/PackageToComponent.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/components/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/coverage/PackageTestCoverage.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/coverage/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/ExternalPackage2DependencyResolver.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/PackageDependencyResolver.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/TransitiveDependencyResolver.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/deploymentFilters/EntitlementVersionFilter.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/deploymentFilters/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/diff/PackageComponentDiff.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/diff/PackageDiffImpl.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/diff/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/CreateDataPackageImpl.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/CreateDiffPackageImpl.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/CreatePackage.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/AssignPermissionSetFetcher.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/DestructiveManifestPathFetcher.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/validators/PackageEmptyChecker.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/validators/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/version/Package2VersionFetcher.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/version/PackageVersionUpdater.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/version/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/AssignPermissionSetsImpl.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/PermissionSetFetcher.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/PermissionSetGroupUpdateAwaiter.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/project/ProjectConfig.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/project/UserDefinedExternalDependency.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/project/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/queryHelper/ChunkCollection.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/queryHelper/QueryHelper.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/queryHelper/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/NativeMetricSender.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/SFPStatsSender.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/DataDogMetricSender.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/NewRelicMetricSender.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/SplunkMetricSender.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/AliasList.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/ChunkArray.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/Delay.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/FileSystem.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/Fileutils.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/ObjectCRUDHelper.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/VersionNumberConverter.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/extractDomainFromUrl.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/xml2json.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/CommitUpdater.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/OrgsUpdater.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/WorkItemUpdater.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/dependency/ShrinkImpl.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/dependency/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/parallelBuilder/BuildCollections.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/parallelBuilder/UndirectedGraph.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/parallelBuilder/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/release/ReleaseDefinition.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/release/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/utils/Get18DigitSalesforceId.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/src/utils/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/utils/Get18DigitSalesforceId.ts.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov-report/utils/index.html create mode 100644 packages/sfpowerscripts-cli/coverage/lcov.info create mode 100644 packages/sfpowerscripts-cli/resources/metadatainfo.json create mode 100644 packages/sfpowerscripts-cli/resources/schemas/pooldefinition.schema.json rename packages/sfpowerscripts-cli/src/{SfpowerscriptsCommand.ts => SfpCommand.ts} (78%) create mode 100644 packages/sfpowerscripts-cli/src/core/apex/ApexClassFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/apex/ApexTriggerFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/apex/coverage/ApexCodeCoverageAggregateFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/apex/coverage/IndividualClassCoverage.ts create mode 100644 packages/sfpowerscripts-cli/src/core/apex/parser/ApexTypeFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/apex/parser/listeners/ApexTypeListener.ts create mode 100644 packages/sfpowerscripts-cli/src/core/apextest/ApexTestSuite.ts create mode 100644 packages/sfpowerscripts-cli/src/core/apextest/ClearCodeCoverage.ts create mode 100644 packages/sfpowerscripts-cli/src/core/apextest/ImpactedApexTestClassFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/apextest/JSONReporter.ts create mode 100644 packages/sfpowerscripts-cli/src/core/apextest/TestOptions.ts create mode 100644 packages/sfpowerscripts-cli/src/core/apextest/TestReportDisplayer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/apextest/TriggerApexTests.ts create mode 100644 packages/sfpowerscripts-cli/src/core/artifacts/ArtifactFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/artifacts/generators/ArtifactGenerator.ts create mode 100644 packages/sfpowerscripts-cli/src/core/changelog/GeneratePackageChangelog.ts create mode 100644 packages/sfpowerscripts-cli/src/core/changelog/interfaces/GenericChangelogInterfaces.ts create mode 100644 packages/sfpowerscripts-cli/src/core/dependency/ChangedComponentsFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/dependency/Component.ts create mode 100644 packages/sfpowerscripts-cli/src/core/dependency/DependencyViolation.ts create mode 100644 packages/sfpowerscripts-cli/src/core/dependency/Entrypoint.ts create mode 100644 packages/sfpowerscripts-cli/src/core/deployers/DeploySourceToOrgImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/deployers/DeploymentExecutor.ts create mode 100644 packages/sfpowerscripts-cli/src/core/deployers/DeploymentSettingsService.ts create mode 100644 packages/sfpowerscripts-cli/src/core/display/DependencyViolationDisplayer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/display/DeployErrorDisplayer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/display/DeploymentOptionDisplayer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/display/ExternalDependencyDisplayer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/display/InstalledArtifactsDisplayer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/display/InstalledPackagesDisplayer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/display/PackageComponentPrinter.ts create mode 100644 packages/sfpowerscripts-cli/src/core/display/PackageDependencyDisplayer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/display/PackageMetadataPrinter.ts create mode 100644 packages/sfpowerscripts-cli/src/core/display/PushErrorDisplayer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/display/TableConstants.ts create mode 100644 packages/sfpowerscripts-cli/src/core/git/Git.ts create mode 100644 packages/sfpowerscripts-cli/src/core/git/GitDiffUtil.ts create mode 100644 packages/sfpowerscripts-cli/src/core/git/GitIdentity.ts create mode 100644 packages/sfpowerscripts-cli/src/core/git/GitTags.ts create mode 100644 packages/sfpowerscripts-cli/src/core/ignore/IgnoreFiles.ts create mode 100644 packages/sfpowerscripts-cli/src/core/limits/LimitsFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/metadata/CustomFieldFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/metadata/MetadataFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/metadata/MetadataFiles.ts create mode 100644 packages/sfpowerscripts-cli/src/core/metadata/MetadataInfo.ts create mode 100644 packages/sfpowerscripts-cli/src/core/metadata/SettingsFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/org/OrgDetailsFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/org/OrganizationFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/org/SFPOrg.ts create mode 100644 packages/sfpowerscripts-cli/src/core/org/ScratchOrgInfoFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/org/packageQuery/InstalledPackagesQueryExecutor.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/Package2Detail.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/SfpPackage.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/SfpPackageBuilder.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/SfpPackageInquirer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/SfpPackageInstaller.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/analyser/AnalyzerRegistry.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/analyser/FHTAnalyzer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/analyser/FTAnalyzer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/analyser/PackageAnalyzer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/analyser/PicklistAnalyzer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/components/DeployDestructiveManifestToOrgImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/components/MetadataCount.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/components/PackageManifest.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/components/PackageToComponent.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/components/ReconcileProfileAgainstOrgImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/coverage/PackageTestCoverage.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/coverage/PackageVersionCoverage.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/dependencies/ExternalPackage2DependencyResolver.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/dependencies/PackageDependencyResolver.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/dependencies/TransitiveDependencyResolver.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/DeploymentCustomizer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/FHTEnabler.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/FTEnabler.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/MetadataDeploymentCustomizer.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/PicklistEnabler.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/PostDeployersRegistry.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/PreDeployersRegistry.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/deploymentFilters/DeploymentFilter.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/deploymentFilters/DeploymentFilterRegistry.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/deploymentFilters/EntitlementVersionFilter.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/diff/PackageComponentDiff.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/diff/PackageDiffImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/generators/SfpPackageContentGenerator.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateDataPackageImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateDiffPackageImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageCreators/CreatePackage.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateSourcePackageImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateUnlockedPackageImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageFormatConvertors/SourceToMDAPIConvertor.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallDataPackageImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallPackage.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallSourcePackageImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallUnlockedPackage.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallUnlockedPackageCollection.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallUnlockedPackageImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageInstallers/PackageInstallationResult.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/packageMerger/PackageMergeManager.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/promote/PromoteUnlockedPackageImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/propertyFetchers/AssignPermissionSetFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/propertyFetchers/DestructiveManifestPathFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/propertyFetchers/PropertyFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/validators/PackageEmptyChecker.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/version/Package2VersionFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/version/Package2VersionInstaller.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/version/PackageVersionLister.ts create mode 100644 packages/sfpowerscripts-cli/src/core/package/version/PackageVersionUpdater.ts create mode 100644 packages/sfpowerscripts-cli/src/core/permsets/AssignPermissionSets.ts create mode 100644 packages/sfpowerscripts-cli/src/core/permsets/AssignPermissionSetsImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/permsets/PermissionSetFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/permsets/PermissionSetGroupUpdateAwaiter.ts create mode 100644 packages/sfpowerscripts-cli/src/core/project/ProjectConfig.ts create mode 100644 packages/sfpowerscripts-cli/src/core/project/UserDefinedExternalDependency.ts create mode 100644 packages/sfpowerscripts-cli/src/core/queryHelper/ChunkCollection.ts create mode 100644 packages/sfpowerscripts-cli/src/core/queryHelper/QueryHelper.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/PasswordGenerator.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/ScratchOrg.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/ScratchOrgOperator.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/ClientSourceTracking.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/OrphanedOrgsDeleteImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolBaseImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolConfig.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolCreateImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolDeleteImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolError.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolFetchImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolJobExecutor.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolListImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolOrgDeleteImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/prequisitecheck/IsValidSfdxAuthUrl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/prequisitecheck/PreRequisiteCheck.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/fetchers/GetUserEmail.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/fetchers/ScratchOrgInfoFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/fetchers/ScratchOrgLimitsFetcher.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/updaters/ScratchOrgInfoAssigner.ts create mode 100644 packages/sfpowerscripts-cli/src/core/scriptExecutor/ScriptExecutorHelpers.ts create mode 100644 packages/sfpowerscripts-cli/src/core/sfdmuwrapper/SFDMURunImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/stats/NativeMetricSender.ts create mode 100644 packages/sfpowerscripts-cli/src/core/stats/SFPStatsSender.ts create mode 100644 packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/DataDogMetricSender.ts create mode 100644 packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/NewRelicMetricSender.ts create mode 100644 packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/SplunkMetricSender.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/AliasList.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/ChunkArray.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/DefaultShell.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/Delay.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/FileSystem.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/Fileutils.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/GetFormattedTime.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/ObjectCRUDHelper.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/OnExit.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/RandomId.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/VersionNumberConverter.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/extractDomainFromUrl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/utils/xml2json.ts create mode 100644 packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityInitialInstall.ts create mode 100644 packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityPackDeployImpl.ts create mode 100644 packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityPackUpdateSettings.ts create mode 100644 packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityRefreshBase.ts rename packages/sfpowerscripts-cli/src/errors/{SfpowerscriptsError.ts => SfpError.ts} (86%) create mode 100644 packages/sfpowerscripts-cli/tests/core/apextest/ApexTestSuite.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/artifacts/ArtifactsFromFileSystem.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/coverage/IndividualClassCoverage.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/git/GitTags.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/org/ArtifactsToOrg.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/org/ListAllPackages.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/Package2VersionFetcher.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/PackageDiffImpl.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/PackageManifest.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/SFPackageBuilder.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/analysers/FHTAnalyzer.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/analysers/FTAnalyzer.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/coverage/PackageTestCoverage.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/dependencies/PackageDependencyResolver.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/dependencies/TransitiveDependencyResolver.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/deploymentFilters/EntitlementVersionFilter.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/packageMerger/PackageMergeManager.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/packageMerger/artifacts1/core2_sfpowerscripts_artifact_1.0.4-1.zip create mode 100644 packages/sfpowerscripts-cli/tests/core/package/packageMerger/artifacts1/feature-mgmt3_sfpowerscripts_artifact_1.0.6-1.zip create mode 100644 packages/sfpowerscripts-cli/tests/core/package/propertyFetchers/AssignPermissionSetFetcher.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/propertyFetchers/DestructiveManifestPathFetcher.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/permsets/AssignPermissionSets.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/permsets/PermissionSetFetcher.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/permsets/PermissionSetGroupUpdateAwaiter.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/project/ProjectConfig.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/queryHelper/ChunkCollection.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/utils/ChunkArray.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/utils/FileSystem.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/utils/extractDomainFromUrl.test.ts create mode 100644 packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/b1.file create mode 100644 packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/c/c1.file create mode 100644 packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/c/c2.file create mode 100644 packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/d/d1.file create mode 100644 packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/d/x/x1.file create mode 100644 packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/e/e1.file diff --git a/packages/sfpowerscripts-cli/CHANGELOG.md b/packages/sfpowerscripts-cli/CHANGELOG.md index 375e5c6f2..6c0a4531b 100644 --- a/packages/sfpowerscripts-cli/CHANGELOG.md +++ b/packages/sfpowerscripts-cli/CHANGELOG.md @@ -3,9 +3,9 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -# [20.30.0](https://github.com/dxatscale/sfpowerscripts/compare/@dxatscale/sfpowerscripts@20.29.0...@dxatscale/sfpowerscripts@20.30.0) (2023-03-22) +# [20.30.0](https://github.com/flxblio/sfp/compare/@flxblio/sfp@20.29.0...@flxblio/sfp@20.30.0) (2023-03-22) ### Features -* **publish:** Add new flag to delete Git tags by age and limit ([#1275](https://github.com/dxatscale/sfpowerscripts/issues/1275)) ([aae62d6](https://github.com/dxatscale/sfpowerscripts/commit/aae62d6d3e7eb390dddcf2ca46b99b44ca4cc933)) +* **publish:** Add new flag to delete Git tags by age and limit ([#1275](https://github.com/flxblio/sfp/issues/1275)) ([aae62d6](https://github.com/flxblio/sfp/commit/aae62d6d3e7eb390dddcf2ca46b99b44ca4cc933)) diff --git a/packages/sfpowerscripts-cli/README.md b/packages/sfpowerscripts-cli/README.md index fabe4ea56..0abca1322 100644 --- a/packages/sfpowerscripts-cli/README.md +++ b/packages/sfpowerscripts-cli/README.md @@ -1,19 +1,19 @@

- sfpowerscripts + sfp

-![Version](https://img.shields.io/npm/v/@dxatscale/sfpowerscripts.svg) -[![GitHub stars](https://img.shields.io/github/stars/dxatscale/sfpowerscripts)](https://gitHub.com/dxatscale/sfpowerscripts/stargazers/) -[![GitHub contributors](https://img.shields.io/github/contributors/dxatscale/sfpowerscripts.svg)](https://github.com/forcedotcom/dxatscale/sfpowerscripts/graphs/contributors/) -[![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/dxatscale/sfpowerscripts/blob/master/LICENSE) +![Version](https://img.shields.io/npm/v/@flxblio/sfp.svg) +[![GitHub stars](https://img.shields.io/github/stars/flxblio/sfp)](https://github.com/flxblio/sfp/stargazers/) +[![GitHub contributors](https://img.shields.io/github/contributors/flxblio/sfp.svg)](https://github.com/flxblio/sfp/graphs/contributors/) +[![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/flxblio/sfp/blob/master/LICENSE) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) [![DeepScan grade](https://deepscan.io/api/teams/10234/projects/12959/branches/208838/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=10234&pid=12959&bid=208838) -[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fdxatscale%2Fsfpowerscripts.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fdxatscale%2Fsfpowerscripts?ref=badge_shield) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/5614/badge)](https://bestpractices.coreinfrastructure.org/projects/5614) +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fflxblio%2Fsfp.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fflxblio%2Fsfp?ref=badge_shield) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/5614/badge)](https://bestpractices.coreinfrastructure.org/projects/5614) -[![Join slack](https://i.imgur.com/FZZmA3g.png)](https://launchpass.com/dxatscale) +[![Join slack](https://i.imgur.com/FZZmA3g.png)](https://launchpass.com/flxblio) -A build system for package based development in Salesforce, delivered as a node cli that can be implemented in any CI/CD system of choice.Read more about the cli and details here - https://docs.dxatscale.io +A build system for package based development in Salesforce, delivered as a node cli that can be implemented in any CI/CD system of choice.Read more about the cli and details here - https://docs.flxblio.io #### Features @@ -26,10 +26,10 @@ A build system for package based development in Salesforce, delivered as a node - Integrate with any CI/CD system of choice - All commands are enabled with statsD, for collecting metrics about your pipeline. -There are lot more features to explore. Read more at https://docs.dxatscale.io +There are lot more features to explore. Read more at https://docs.flxblio.io -The project is delivered as a CLI that can be deployed in any CI/CD system, The module is available in [NPM](https://www.npmjs.com/package/@dxatscale/sfpowerscripts) or can be -used by using the [docker image](https://github.com/dxatscale/sfpowerscripts/pkgs/container/sfpowerscripts) +The project is delivered as a CLI that can be deployed in any CI/CD system, The module is available in [NPM](https://www.npmjs.com/package/@flxblio/sfp) or can be +used by using the [docker image](https://github.com/flxblio/sfp/pkgs/container/sfp) @@ -45,29 +45,29 @@ used by using the [docker image](https://github.com/dxatscale/sfpowerscripts/pkg #### CI/CD Reference Implementation -Getting started guides for popular CI/CD platforms along with reference pipelines are available [here](https://docs.dxatscale.io/reference-implementation/github) +Getting started guides for popular CI/CD platforms along with reference pipelines are available [here](https://docs.flxblio.io/implementing-your-ci-cd/github) -#### Installing sfpowerscripts locally +#### Installing sfp locally -sfpowerscripts can be installed on your local device using npm +sfp can be installed on your local device using npm ``` -npm i -g @dxatscale/sfpowerscripts +npm i -g @flxblio/sfp ``` #### Docker -Docker images for sfpowerscripts are available at [GitHub Container Registry](https://github.com/dxatscale/sfpowerscripts/pkgs/container/sfpowerscripts). +Docker images for sfp are available at [GitHub Container Registry](https://github.com/flxblio/sfp/pkgs/container/sfp). -We recommend using the sfpowerscripts docker image to avoid breakages in your CI/CD pipelines due to updates in sfpowerscripts or any of its dependencies such as the SFDX CLI. +We recommend using the sfp docker image to avoid breakages in your CI/CD pipelines due to updates in sfp or any of its dependencies such as the SFDX CLI. #### Build Instructions -To build sfpowerscripts execute the following on the terminal: +To build sfp execute the following on the terminal: ``` npm i -g lerna #Install Lerna Globally -cd # Navigate to the checked out directory -npm i +cd # Navigate to the checked out directory +pnpm i lerna run build ``` @@ -80,19 +80,19 @@ lerna run test To debug and test plugin ``` - cd packages/sfpowerscripts-cli + cd packages/sfp-cli npm link ``` #### Maintainers -List of Maintainers are available in the [link](https://docs.dxatscale.io/about-us) +List of Maintainers are available in the [link](https://docs.flxblio.io/about-us) #### Where do I reach for queries? -Please create an issue in the repo for bugs or utilize GitHub Discussions for other queries. Join our [Slack Community](https://launchpass.com/dxatscale) as well. +Please create an issue in the repo for bugs or utilize GitHub Discussions for other queries. Join our [Slack Community](https://launchpass.com/flxblio) as well. ## License -[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fdxatscale%2Fsfpowerscripts.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fdxatscale%2Fsfpowerscripts?ref=badge_large) +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fflxblio%2Fsfp.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fflxblio%2Fsfp?ref=badge_large) diff --git a/packages/sfpowerscripts-cli/coverage/clover.xml b/packages/sfpowerscripts-cli/coverage/clover.xml new file mode 100644 index 000000000..2f2b77c16 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/clover.xml @@ -0,0 +1,2694 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/sfpowerscripts-cli/coverage/coverage-final.json b/packages/sfpowerscripts-cli/coverage/coverage-final.json new file mode 100644 index 000000000..8fff365e9 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/coverage-final.json @@ -0,0 +1,72 @@ +{"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/ProjectValidation.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/ProjectValidation.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"6":{"start":{"line":14,"column":8},"end":{"line":14,"column":null}},"7":{"start":{"line":15,"column":8},"end":{"line":15,"column":null}},"8":{"start":{"line":16,"column":8},"end":{"line":16,"column":null}},"9":{"start":{"line":20,"column":21},"end":{"line":20,"column":117}},"10":{"start":{"line":21,"column":24},"end":{"line":21,"column":48}},"11":{"start":{"line":22,"column":28},"end":{"line":22,"column":57}},"12":{"start":{"line":24,"column":35},"end":{"line":24,"column":104}},"13":{"start":{"line":26,"column":12},"end":{"line":32,"column":null}},"14":{"start":{"line":27,"column":16},"end":{"line":31,"column":null}},"15":{"start":{"line":35,"column":10},"end":{"line":35,"column":null}},"16":{"start":{"line":36,"column":10},"end":{"line":36,"column":null}},"17":{"start":{"line":41,"column":8},"end":{"line":58,"column":null}},"18":{"start":{"line":42,"column":23},"end":{"line":42,"column":34}},"19":{"start":{"line":44,"column":16},"end":{"line":49,"column":null}},"20":{"start":{"line":51,"column":16},"end":{"line":56,"column":null}},"21":{"start":{"line":63,"column":8},"end":{"line":85,"column":null}},"22":{"start":{"line":64,"column":30},"end":{"line":64,"column":91}},"23":{"start":{"line":66,"column":34},"end":{"line":66,"column":50}},"24":{"start":{"line":71,"column":16},"end":{"line":83,"column":null}},"25":{"start":{"line":8,"column":0},"end":{"line":8,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":13,"column":4},"end":{"line":13,"column":null}},"loc":{"start":{"line":13,"column":4},"end":{"line":17,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":19,"column":11},"end":{"line":19,"column":34}},"loc":{"start":{"line":19,"column":34},"end":{"line":38,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":26,"column":37},"end":{"line":26,"column":38}},"loc":{"start":{"line":26,"column":57},"end":{"line":32,"column":13}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":40,"column":11},"end":{"line":40,"column":31}},"loc":{"start":{"line":40,"column":31},"end":{"line":59,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":41,"column":86},"end":{"line":41,"column":89}},"loc":{"start":{"line":41,"column":93},"end":{"line":58,"column":9}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":62,"column":11},"end":{"line":62,"column":38}},"loc":{"start":{"line":62,"column":38},"end":{"line":86,"column":null}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":63,"column":86},"end":{"line":63,"column":89}},"loc":{"start":{"line":63,"column":93},"end":{"line":85,"column":9}}}},"branchMap":{"0":{"loc":{"start":{"line":68,"column":16},"end":{"line":69,"column":88}},"type":"binary-expr","locations":[{"start":{"line":68,"column":16},"end":{"line":68,"column":48}},{"start":{"line":69,"column":17},"end":{"line":69,"column":51}},{"start":{"line":69,"column":55},"end":{"line":69,"column":87}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":8,"7":8,"8":8,"9":4,"10":4,"11":4,"12":1,"13":1,"14":7,"15":1,"16":1,"17":2,"18":4,"19":1,"20":1,"21":2,"22":4,"23":4,"24":1,"25":1},"f":{"0":8,"1":4,"2":7,"3":2,"4":4,"5":2,"6":4},"b":{"0":[4,2,1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/apex/ApexClassFetcher.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/apex/ApexClassFetcher.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":6,"column":24},"end":{"line":6,"column":40}},"3":{"start":{"line":15,"column":51},"end":{"line":15,"column":53}},"4":{"start":{"line":17,"column":23},"end":{"line":17,"column":50}},"5":{"start":{"line":19,"column":35},"end":{"line":19,"column":76}},"6":{"start":{"line":19,"column":53},"end":{"line":19,"column":64}},"7":{"start":{"line":20,"column":26},"end":{"line":20,"column":92}},"8":{"start":{"line":22,"column":28},"end":{"line":22,"column":106}},"9":{"start":{"line":23,"column":12},"end":{"line":23,"column":null}},"10":{"start":{"line":26,"column":8},"end":{"line":26,"column":null}},"11":{"start":{"line":5,"column":0},"end":{"line":5,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":6,"column":4},"end":{"line":6,"column":24}},"loc":{"start":{"line":6,"column":40},"end":{"line":6,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":14,"column":11},"end":{"line":14,"column":17}},"loc":{"start":{"line":14,"column":58},"end":{"line":27,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":19,"column":45},"end":{"line":19,"column":49}},"loc":{"start":{"line":19,"column":53},"end":{"line":19,"column":64}}}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":1},"f":{"0":1,"1":0,"2":0},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/apex/ApexTriggerFetcher.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/apex/ApexTriggerFetcher.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":6,"column":24},"end":{"line":6,"column":40}},"3":{"start":{"line":15,"column":51},"end":{"line":15,"column":53}},"4":{"start":{"line":17,"column":23},"end":{"line":17,"column":52}},"5":{"start":{"line":19,"column":35},"end":{"line":19,"column":76}},"6":{"start":{"line":19,"column":53},"end":{"line":19,"column":64}},"7":{"start":{"line":20,"column":26},"end":{"line":20,"column":94}},"8":{"start":{"line":22,"column":28},"end":{"line":22,"column":106}},"9":{"start":{"line":23,"column":12},"end":{"line":23,"column":null}},"10":{"start":{"line":26,"column":8},"end":{"line":26,"column":null}},"11":{"start":{"line":5,"column":0},"end":{"line":5,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":6,"column":4},"end":{"line":6,"column":24}},"loc":{"start":{"line":6,"column":40},"end":{"line":6,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":14,"column":11},"end":{"line":14,"column":17}},"loc":{"start":{"line":14,"column":62},"end":{"line":27,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":19,"column":45},"end":{"line":19,"column":49}},"loc":{"start":{"line":19,"column":53},"end":{"line":19,"column":64}}}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":1},"f":{"0":1,"1":0,"2":0},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/apex/coverage/ApexCodeCoverageAggregateFetcher.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/apex/coverage/ApexCodeCoverageAggregateFetcher.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":6,"column":24},"end":{"line":6,"column":40}},"3":{"start":{"line":24,"column":14},"end":{"line":24,"column":16}},"4":{"start":{"line":26,"column":23},"end":{"line":26,"column":66}},"5":{"start":{"line":28,"column":35},"end":{"line":28,"column":76}},"6":{"start":{"line":28,"column":53},"end":{"line":28,"column":64}},"7":{"start":{"line":29,"column":24},"end":{"line":29,"column":180}},"8":{"start":{"line":31,"column":28},"end":{"line":36,"column":38}},"9":{"start":{"line":37,"column":12},"end":{"line":37,"column":null}},"10":{"start":{"line":40,"column":8},"end":{"line":40,"column":null}},"11":{"start":{"line":5,"column":0},"end":{"line":5,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":6,"column":4},"end":{"line":6,"column":24}},"loc":{"start":{"line":6,"column":40},"end":{"line":6,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":13,"column":11},"end":{"line":13,"column":17}},"loc":{"start":{"line":13,"column":67},"end":{"line":41,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":28,"column":45},"end":{"line":28,"column":49}},"loc":{"start":{"line":28,"column":53},"end":{"line":28,"column":64}}}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":1},"f":{"0":1,"1":0,"2":0},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/apex/coverage/IndividualClassCoverage.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/apex/coverage/IndividualClassCoverage.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":4,"column":31},"end":{"line":4,"column":48}},"2":{"start":{"line":4,"column":58},"end":{"line":4,"column":72}},"3":{"start":{"line":10,"column":14},"end":{"line":10,"column":16}},"4":{"start":{"line":13,"column":8},"end":{"line":15,"column":null}},"5":{"start":{"line":14,"column":12},"end":{"line":14,"column":null}},"6":{"start":{"line":18,"column":8},"end":{"line":21,"column":null}},"7":{"start":{"line":19,"column":8},"end":{"line":21,"column":null}},"8":{"start":{"line":20,"column":13},"end":{"line":20,"column":null}},"9":{"start":{"line":24,"column":8},"end":{"line":24,"column":null}},"10":{"start":{"line":37,"column":12},"end":{"line":37,"column":null}},"11":{"start":{"line":38,"column":12},"end":{"line":38,"column":null}},"12":{"start":{"line":41,"column":8},"end":{"line":45,"column":null}},"13":{"start":{"line":46,"column":41},"end":{"line":48,"column":10}},"14":{"start":{"line":47,"column":12},"end":{"line":47,"column":null}},"15":{"start":{"line":50,"column":8},"end":{"line":62,"column":null}},"16":{"start":{"line":51,"column":12},"end":{"line":56,"column":null}},"17":{"start":{"line":58,"column":12},"end":{"line":62,"column":null}},"18":{"start":{"line":3,"column":0},"end":{"line":3,"column":21}}},"fnMap":{"0":{"name":"(anonymous_6)","decl":{"start":{"line":4,"column":4},"end":{"line":4,"column":31}},"loc":{"start":{"line":4,"column":72},"end":{"line":4,"column":null}}},"1":{"name":"(anonymous_7)","decl":{"start":{"line":6,"column":11},"end":{"line":6,"column":37}},"loc":{"start":{"line":6,"column":68},"end":{"line":25,"column":null}}},"2":{"name":"(anonymous_8)","decl":{"start":{"line":13,"column":57},"end":{"line":13,"column":60}},"loc":{"start":{"line":13,"column":64},"end":{"line":15,"column":9}}},"3":{"name":"(anonymous_9)","decl":{"start":{"line":19,"column":66},"end":{"line":19,"column":69}},"loc":{"start":{"line":19,"column":73},"end":{"line":21,"column":9}}},"4":{"name":"(anonymous_10)","decl":{"start":{"line":27,"column":11},"end":{"line":27,"column":42}},"loc":{"start":{"line":29,"column":34},"end":{"line":63,"column":null}}},"5":{"name":"(anonymous_11)","decl":{"start":{"line":46,"column":73},"end":{"line":46,"column":76}},"loc":{"start":{"line":46,"column":80},"end":{"line":48,"column":9}}}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":8},"end":{"line":21,"column":null}},"type":"if","locations":[{"start":{"line":18,"column":8},"end":{"line":21,"column":null}}]},"1":{"loc":{"start":{"line":18,"column":11},"end":{"line":18,"column":66}},"type":"binary-expr","locations":[{"start":{"line":18,"column":11},"end":{"line":18,"column":31}},{"start":{"line":18,"column":35},"end":{"line":18,"column":66}}]},"2":{"loc":{"start":{"line":50,"column":8},"end":{"line":62,"column":null}},"type":"if","locations":[{"start":{"line":50,"column":8},"end":{"line":62,"column":null}},{"start":{"line":58,"column":12},"end":{"line":62,"column":null}}]}},"s":{"0":2,"1":9,"2":9,"3":2,"4":2,"5":8,"6":2,"7":0,"8":0,"9":2,"10":0,"11":0,"12":4,"13":4,"14":13,"15":4,"16":2,"17":2,"18":2},"f":{"0":9,"1":2,"2":8,"3":0,"4":4,"5":13},"b":{"0":[0],"1":[2,0],"2":[2,2]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/apextest/ApexTestSuite.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/apextest/ApexTestSuite.ts","statementMap":{"0":{"start":{"line":2,"column":11},"end":{"line":2,"column":30}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"4":{"start":{"line":8,"column":31},"end":{"line":8,"column":48}},"5":{"start":{"line":8,"column":58},"end":{"line":8,"column":75}},"6":{"start":{"line":11,"column":39},"end":{"line":14,"column":10}},"7":{"start":{"line":16,"column":8},"end":{"line":16,"column":null}},"8":{"start":{"line":18,"column":8},"end":{"line":18,"column":null}},"9":{"start":{"line":18,"column":32},"end":{"line":18,"column":null}},"10":{"start":{"line":20,"column":35},"end":{"line":20,"column":99}},"11":{"start":{"line":23,"column":12},"end":{"line":23,"column":null}},"12":{"start":{"line":25,"column":31},"end":{"line":25,"column":50}},"13":{"start":{"line":26,"column":12},"end":{"line":26,"column":null}},"14":{"start":{"line":27,"column":12},"end":{"line":27,"column":null}},"15":{"start":{"line":7,"column":0},"end":{"line":7,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":8,"column":4},"end":{"line":8,"column":31}},"loc":{"start":{"line":8,"column":75},"end":{"line":8,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":10,"column":11},"end":{"line":10,"column":17}},"loc":{"start":{"line":10,"column":38},"end":{"line":29,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":8},"end":{"line":18,"column":null}},"type":"if","locations":[{"start":{"line":18,"column":8},"end":{"line":18,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":3,"5":3,"6":3,"7":3,"8":3,"9":1,"10":2,"11":1,"12":1,"13":1,"14":1,"15":1},"f":{"0":3,"1":3},"b":{"0":[1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/apextest/ImpactedApexTestClassFetcher.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/apextest/ImpactedApexTestClassFetcher.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"5":{"start":{"line":10,"column":16},"end":{"line":10,"column":38}},"6":{"start":{"line":11,"column":16},"end":{"line":11,"column":46}},"7":{"start":{"line":12,"column":16},"end":{"line":12,"column":30}},"8":{"start":{"line":13,"column":16},"end":{"line":13,"column":38}},"9":{"start":{"line":18,"column":33},"end":{"line":18,"column":35}},"10":{"start":{"line":19,"column":37},"end":{"line":19,"column":39}},"11":{"start":{"line":23,"column":41},"end":{"line":24,"column":null}},"12":{"start":{"line":24,"column":27},"end":{"line":24,"column":75}},"13":{"start":{"line":27,"column":8},"end":{"line":27,"column":null}},"14":{"start":{"line":28,"column":8},"end":{"line":28,"column":null}},"15":{"start":{"line":32,"column":27},"end":{"line":32,"column":144}},"16":{"start":{"line":33,"column":27},"end":{"line":33,"column":70}},"17":{"start":{"line":38,"column":12},"end":{"line":38,"column":null}},"18":{"start":{"line":39,"column":12},"end":{"line":39,"column":null}},"19":{"start":{"line":40,"column":12},"end":{"line":40,"column":null}},"20":{"start":{"line":43,"column":8},"end":{"line":43,"column":null}},"21":{"start":{"line":51,"column":16},"end":{"line":55,"column":null}},"22":{"start":{"line":56,"column":16},"end":{"line":56,"column":null}},"23":{"start":{"line":57,"column":16},"end":{"line":57,"column":null}},"24":{"start":{"line":62,"column":16},"end":{"line":62,"column":null}},"25":{"start":{"line":62,"column":65},"end":{"line":62,"column":null}},"26":{"start":{"line":66,"column":20},"end":{"line":66,"column":null}},"27":{"start":{"line":66,"column":64},"end":{"line":66,"column":null}},"28":{"start":{"line":71,"column":8},"end":{"line":71,"column":null}},"29":{"start":{"line":73,"column":8},"end":{"line":73,"column":null}},"30":{"start":{"line":74,"column":8},"end":{"line":78,"column":null}},"31":{"start":{"line":81,"column":12},"end":{"line":85,"column":null}},"32":{"start":{"line":86,"column":12},"end":{"line":86,"column":null}},"33":{"start":{"line":88,"column":8},"end":{"line":88,"column":null}},"34":{"start":{"line":8,"column":0},"end":{"line":8,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":9,"column":4},"end":{"line":9,"column":null}},"loc":{"start":{"line":13,"column":38},"end":{"line":14,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":16,"column":11},"end":{"line":16,"column":17}},"loc":{"start":{"line":16,"column":39},"end":{"line":89,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":24,"column":13},"end":{"line":24,"column":22}},"loc":{"start":{"line":24,"column":27},"end":{"line":24,"column":75}}}},"branchMap":{"0":{"loc":{"start":{"line":50,"column":16},"end":{"line":50,"column":146}},"type":"binary-expr","locations":[{"start":{"line":50,"column":16},"end":{"line":50,"column":63}},{"start":{"line":50,"column":67},"end":{"line":50,"column":146}}]},"1":{"loc":{"start":{"line":62,"column":16},"end":{"line":62,"column":null}},"type":"if","locations":[{"start":{"line":62,"column":16},"end":{"line":62,"column":null}}]},"2":{"loc":{"start":{"line":66,"column":20},"end":{"line":66,"column":null}},"type":"if","locations":[{"start":{"line":66,"column":20},"end":{"line":66,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":1},"f":{"0":0,"1":0,"2":0},"b":{"0":[0,0],"1":[0],"2":[0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/artifacts/ArtifactFetcher.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/artifacts/ArtifactFetcher.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"7":{"start":{"line":17,"column":33},"end":{"line":17,"column":35}},"8":{"start":{"line":20,"column":12},"end":{"line":20,"column":null}},"9":{"start":{"line":23,"column":34},"end":{"line":23,"column":85}},"10":{"start":{"line":25,"column":8},"end":{"line":25,"column":null}},"11":{"start":{"line":30,"column":16},"end":{"line":30,"column":null}},"12":{"start":{"line":32,"column":16},"end":{"line":32,"column":null}},"13":{"start":{"line":34,"column":16},"end":{"line":34,"column":null}},"14":{"start":{"line":36,"column":12},"end":{"line":36,"column":null}},"15":{"start":{"line":39,"column":8},"end":{"line":39,"column":null}},"16":{"start":{"line":47,"column":30},"end":{"line":47,"column":88}},"17":{"start":{"line":49,"column":32},"end":{"line":49,"column":98}},"18":{"start":{"line":51,"column":42},"end":{"line":55,"column":null}},"19":{"start":{"line":57,"column":8},"end":{"line":57,"column":null}},"20":{"start":{"line":59,"column":8},"end":{"line":59,"column":null}},"21":{"start":{"line":67,"column":49},"end":{"line":67,"column":97}},"22":{"start":{"line":69,"column":8},"end":{"line":69,"column":null}},"23":{"start":{"line":70,"column":18},"end":{"line":70,"column":38}},"24":{"start":{"line":73,"column":8},"end":{"line":73,"column":null}},"25":{"start":{"line":75,"column":35},"end":{"line":75,"column":87}},"26":{"start":{"line":77,"column":12},"end":{"line":77,"column":null}},"27":{"start":{"line":80,"column":38},"end":{"line":80,"column":115}},"28":{"start":{"line":82,"column":30},"end":{"line":82,"column":91}},"29":{"start":{"line":84,"column":32},"end":{"line":84,"column":101}},"30":{"start":{"line":86,"column":42},"end":{"line":90,"column":null}},"31":{"start":{"line":92,"column":8},"end":{"line":92,"column":null}},"32":{"start":{"line":94,"column":8},"end":{"line":94,"column":null}},"33":{"start":{"line":102,"column":49},"end":{"line":102,"column":97}},"34":{"start":{"line":103,"column":8},"end":{"line":103,"column":null}},"35":{"start":{"line":105,"column":8},"end":{"line":109,"column":null}},"36":{"start":{"line":111,"column":38},"end":{"line":111,"column":112}},"37":{"start":{"line":113,"column":30},"end":{"line":113,"column":88}},"38":{"start":{"line":115,"column":32},"end":{"line":115,"column":98}},"39":{"start":{"line":117,"column":42},"end":{"line":121,"column":null}},"40":{"start":{"line":123,"column":8},"end":{"line":123,"column":null}},"41":{"start":{"line":125,"column":8},"end":{"line":125,"column":null}},"42":{"start":{"line":138,"column":12},"end":{"line":138,"column":null}},"43":{"start":{"line":140,"column":12},"end":{"line":140,"column":null}},"44":{"start":{"line":143,"column":34},"end":{"line":146,"column":10}},"45":{"start":{"line":148,"column":8},"end":{"line":153,"column":null}},"46":{"start":{"line":149,"column":12},"end":{"line":149,"column":null}},"47":{"start":{"line":150,"column":41},"end":{"line":150,"column":85}},"48":{"start":{"line":151,"column":12},"end":{"line":151,"column":null}},"49":{"start":{"line":152,"column":12},"end":{"line":152,"column":null}},"50":{"start":{"line":153,"column":15},"end":{"line":153,"column":null}},"51":{"start":{"line":162,"column":8},"end":{"line":165,"column":null}},"52":{"start":{"line":163,"column":30},"end":{"line":163,"column":52}},"53":{"start":{"line":164,"column":12},"end":{"line":164,"column":null}},"54":{"start":{"line":167,"column":22},"end":{"line":167,"column":96}},"55":{"start":{"line":168,"column":33},"end":{"line":174,"column":10}},"56":{"start":{"line":169,"column":42},"end":{"line":169,"column":80}},"57":{"start":{"line":170,"column":26},"end":{"line":170,"column":47}},"58":{"start":{"line":172,"column":12},"end":{"line":173,"column":null}},"59":{"start":{"line":172,"column":25},"end":{"line":172,"column":40}},"60":{"start":{"line":173,"column":17},"end":{"line":173,"column":null}},"61":{"start":{"line":177,"column":39},"end":{"line":177,"column":60}},"62":{"start":{"line":178,"column":36},"end":{"line":178,"column":56}},"63":{"start":{"line":180,"column":8},"end":{"line":180,"column":null}},"64":{"start":{"line":180,"column":44},"end":{"line":180,"column":76}},"65":{"start":{"line":188,"column":8},"end":{"line":190,"column":null}},"66":{"start":{"line":189,"column":12},"end":{"line":189,"column":null}},"67":{"start":{"line":189,"column":42},"end":{"line":189,"column":null}},"68":{"start":{"line":200,"column":12},"end":{"line":200,"column":null}},"69":{"start":{"line":202,"column":12},"end":{"line":204,"column":null}},"70":{"start":{"line":205,"column":12},"end":{"line":205,"column":null}},"71":{"start":{"line":210,"column":21},"end":{"line":210,"column":23}},"72":{"start":{"line":211,"column":25},"end":{"line":211,"column":89}},"73":{"start":{"line":212,"column":31},"end":{"line":212,"column":48}},"74":{"start":{"line":213,"column":21},"end":{"line":213,"column":22}},"75":{"start":{"line":214,"column":12},"end":{"line":214,"column":null}},"76":{"start":{"line":216,"column":8},"end":{"line":216,"column":null}},"77":{"start":{"line":9,"column":0},"end":{"line":9,"column":21}}},"fnMap":{"0":{"name":"(anonymous_6)","decl":{"start":{"line":16,"column":11},"end":{"line":16,"column":18}},"loc":{"start":{"line":16,"column":98},"end":{"line":40,"column":null}}},"1":{"name":"(anonymous_7)","decl":{"start":{"line":46,"column":12},"end":{"line":46,"column":19}},"loc":{"start":{"line":46,"column":83},"end":{"line":60,"column":null}}},"2":{"name":"(anonymous_8)","decl":{"start":{"line":66,"column":12},"end":{"line":66,"column":19}},"loc":{"start":{"line":66,"column":69},"end":{"line":95,"column":null}}},"3":{"name":"(anonymous_9)","decl":{"start":{"line":101,"column":12},"end":{"line":101,"column":19}},"loc":{"start":{"line":101,"column":69},"end":{"line":126,"column":null}}},"4":{"name":"(anonymous_10)","decl":{"start":{"line":135,"column":11},"end":{"line":135,"column":18}},"loc":{"start":{"line":135,"column":80},"end":{"line":154,"column":null}}},"5":{"name":"(anonymous_11)","decl":{"start":{"line":160,"column":12},"end":{"line":160,"column":19}},"loc":{"start":{"line":160,"column":56},"end":{"line":181,"column":null}}},"6":{"name":"(anonymous_12)","decl":{"start":{"line":162,"column":38},"end":{"line":162,"column":46}},"loc":{"start":{"line":162,"column":50},"end":{"line":165,"column":9}}},"7":{"name":"(anonymous_13)","decl":{"start":{"line":168,"column":48},"end":{"line":168,"column":56}},"loc":{"start":{"line":168,"column":60},"end":{"line":174,"column":9}}},"8":{"name":"(anonymous_14)","decl":{"start":{"line":180,"column":31},"end":{"line":180,"column":39}},"loc":{"start":{"line":180,"column":44},"end":{"line":180,"column":76}}},"9":{"name":"(anonymous_15)","decl":{"start":{"line":187,"column":12},"end":{"line":187,"column":19}},"loc":{"start":{"line":187,"column":70},"end":{"line":191,"column":null}}},"10":{"name":"(anonymous_16)","decl":{"start":{"line":188,"column":50},"end":{"line":188,"column":58}},"loc":{"start":{"line":188,"column":62},"end":{"line":190,"column":9}}},"11":{"name":"(anonymous_17)","decl":{"start":{"line":198,"column":11},"end":{"line":198,"column":18}},"loc":{"start":{"line":198,"column":98},"end":{"line":207,"column":null}}},"12":{"name":"(anonymous_18)","decl":{"start":{"line":209,"column":12},"end":{"line":209,"column":19}},"loc":{"start":{"line":209,"column":38},"end":{"line":217,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":148,"column":8},"end":{"line":153,"column":null}},"type":"if","locations":[{"start":{"line":148,"column":8},"end":{"line":153,"column":null}},{"start":{"line":153,"column":15},"end":{"line":153,"column":null}}]},"1":{"loc":{"start":{"line":148,"column":12},"end":{"line":148,"column":48}},"type":"binary-expr","locations":[{"start":{"line":148,"column":12},"end":{"line":148,"column":24}},{"start":{"line":148,"column":28},"end":{"line":148,"column":48}}]},"2":{"loc":{"start":{"line":164,"column":19},"end":{"line":164,"column":51}},"type":"binary-expr","locations":[{"start":{"line":164,"column":19},"end":{"line":164,"column":33}},{"start":{"line":164,"column":37},"end":{"line":164,"column":51}}]},"3":{"loc":{"start":{"line":172,"column":12},"end":{"line":173,"column":null}},"type":"if","locations":[{"start":{"line":172,"column":12},"end":{"line":173,"column":null}},{"start":{"line":173,"column":17},"end":{"line":173,"column":null}}]},"4":{"loc":{"start":{"line":189,"column":12},"end":{"line":189,"column":null}},"type":"if","locations":[{"start":{"line":189,"column":12},"end":{"line":189,"column":null}}]},"5":{"loc":{"start":{"line":199,"column":12},"end":{"line":199,"column":64}},"type":"binary-expr","locations":[{"start":{"line":199,"column":12},"end":{"line":199,"column":34}},{"start":{"line":199,"column":38},"end":{"line":199,"column":64}}]},"6":{"loc":{"start":{"line":201,"column":19},"end":{"line":201,"column":70}},"type":"binary-expr","locations":[{"start":{"line":201,"column":19},"end":{"line":201,"column":41}},{"start":{"line":201,"column":45},"end":{"line":201,"column":70}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":2,"43":1,"44":3,"45":3,"46":1,"47":1,"48":1,"49":1,"50":2,"51":1,"52":4,"53":4,"54":1,"55":1,"56":4,"57":4,"58":4,"59":4,"60":0,"61":1,"62":1,"63":1,"64":4,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":3,"5":1,"6":4,"7":4,"8":4,"9":0,"10":0,"11":0,"12":0},"b":{"0":[1,2],"1":[3,2],"2":[4,1],"3":[4,0],"4":[0],"5":[0,0],"6":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/display/TableConstants.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/display/TableConstants.ts","statementMap":{"0":{"start":{"line":2,"column":13},"end":{"line":18,"column":null}},"1":{"start":{"line":22,"column":13},"end":{"line":38,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":1,"1":1},"f":{},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/git/Git.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/git/Git.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":5,"column":12},"end":{"line":5,"column":26}},"5":{"start":{"line":14,"column":32},"end":{"line":14,"column":51}},"6":{"start":{"line":14,"column":61},"end":{"line":14,"column":76}},"7":{"start":{"line":12,"column":12},"end":{"line":12,"column":null}},"8":{"start":{"line":16,"column":12},"end":{"line":16,"column":null}},"9":{"start":{"line":17,"column":12},"end":{"line":17,"column":null}},"10":{"start":{"line":19,"column":12},"end":{"line":19,"column":null}},"11":{"start":{"line":20,"column":12},"end":{"line":20,"column":null}},"12":{"start":{"line":25,"column":8},"end":{"line":25,"column":null}},"13":{"start":{"line":29,"column":8},"end":{"line":29,"column":null}},"14":{"start":{"line":33,"column":8},"end":{"line":33,"column":null}},"15":{"start":{"line":37,"column":24},"end":{"line":37,"column":52}},"16":{"start":{"line":39,"column":29},"end":{"line":39,"column":50}},"17":{"start":{"line":40,"column":8},"end":{"line":40,"column":null}},"18":{"start":{"line":42,"column":8},"end":{"line":42,"column":null}},"19":{"start":{"line":46,"column":25},"end":{"line":46,"column":54}},"20":{"start":{"line":48,"column":29},"end":{"line":48,"column":51}},"21":{"start":{"line":49,"column":8},"end":{"line":49,"column":null}},"22":{"start":{"line":51,"column":8},"end":{"line":51,"column":null}},"23":{"start":{"line":55,"column":27},"end":{"line":55,"column":55}},"24":{"start":{"line":57,"column":8},"end":{"line":57,"column":null}},"25":{"start":{"line":62,"column":8},"end":{"line":68,"column":null}},"26":{"start":{"line":63,"column":12},"end":{"line":63,"column":null}},"27":{"start":{"line":65,"column":16},"end":{"line":65,"column":null}},"28":{"start":{"line":67,"column":12},"end":{"line":67,"column":null}},"29":{"start":{"line":68,"column":15},"end":{"line":68,"column":null}},"30":{"start":{"line":70,"column":8},"end":{"line":70,"column":null}},"31":{"start":{"line":70,"column":30},"end":{"line":70,"column":null}},"32":{"start":{"line":72,"column":8},"end":{"line":72,"column":null}},"33":{"start":{"line":77,"column":12},"end":{"line":77,"column":null}},"34":{"start":{"line":78,"column":12},"end":{"line":78,"column":null}},"35":{"start":{"line":79,"column":12},"end":{"line":79,"column":null}},"36":{"start":{"line":80,"column":12},"end":{"line":80,"column":null}},"37":{"start":{"line":82,"column":12},"end":{"line":85,"column":null}},"38":{"start":{"line":86,"column":12},"end":{"line":86,"column":null}},"39":{"start":{"line":91,"column":19},"end":{"line":91,"column":46}},"40":{"start":{"line":94,"column":16},"end":{"line":94,"column":null}},"41":{"start":{"line":100,"column":8},"end":{"line":100,"column":null}},"42":{"start":{"line":100,"column":18},"end":{"line":100,"column":null}},"43":{"start":{"line":105,"column":12},"end":{"line":105,"column":null}},"44":{"start":{"line":107,"column":16},"end":{"line":107,"column":null}},"45":{"start":{"line":109,"column":33},"end":{"line":109,"column":77}},"46":{"start":{"line":110,"column":16},"end":{"line":110,"column":null}},"47":{"start":{"line":113,"column":12},"end":{"line":116,"column":null}},"48":{"start":{"line":117,"column":12},"end":{"line":117,"column":null}},"49":{"start":{"line":122,"column":31},"end":{"line":122,"column":62}},"50":{"start":{"line":124,"column":8},"end":{"line":124,"column":null}},"51":{"start":{"line":124,"column":49},"end":{"line":124,"column":70}},"52":{"start":{"line":128,"column":40},"end":{"line":128,"column":76}},"53":{"start":{"line":130,"column":8},"end":{"line":130,"column":null}},"54":{"start":{"line":131,"column":22},"end":{"line":131,"column":52}},"55":{"start":{"line":134,"column":8},"end":{"line":134,"column":null}},"56":{"start":{"line":137,"column":18},"end":{"line":137,"column":42}},"57":{"start":{"line":138,"column":8},"end":{"line":138,"column":null}},"58":{"start":{"line":139,"column":8},"end":{"line":139,"column":null}},"59":{"start":{"line":141,"column":8},"end":{"line":141,"column":null}},"60":{"start":{"line":142,"column":8},"end":{"line":142,"column":null}},"61":{"start":{"line":143,"column":8},"end":{"line":143,"column":null}},"62":{"start":{"line":145,"column":12},"end":{"line":145,"column":null}},"63":{"start":{"line":148,"column":12},"end":{"line":148,"column":null}},"64":{"start":{"line":151,"column":8},"end":{"line":155,"column":null}},"65":{"start":{"line":156,"column":8},"end":{"line":156,"column":null}},"66":{"start":{"line":160,"column":18},"end":{"line":160,"column":45}},"67":{"start":{"line":161,"column":24},"end":{"line":161,"column":60}},"68":{"start":{"line":163,"column":12},"end":{"line":163,"column":null}},"69":{"start":{"line":165,"column":8},"end":{"line":165,"column":null}},"70":{"start":{"line":166,"column":8},"end":{"line":166,"column":null}},"71":{"start":{"line":170,"column":8},"end":{"line":170,"column":null}},"72":{"start":{"line":174,"column":8},"end":{"line":174,"column":null}},"73":{"start":{"line":174,"column":35},"end":{"line":174,"column":null}},"74":{"start":{"line":181,"column":8},"end":{"line":181,"column":null}},"75":{"start":{"line":185,"column":12},"end":{"line":185,"column":null}},"76":{"start":{"line":190,"column":8},"end":{"line":190,"column":null}},"77":{"start":{"line":190,"column":21},"end":{"line":190,"column":null}},"78":{"start":{"line":191,"column":8},"end":{"line":191,"column":null}},"79":{"start":{"line":193,"column":12},"end":{"line":193,"column":null}},"80":{"start":{"line":194,"column":12},"end":{"line":194,"column":null}},"81":{"start":{"line":198,"column":12},"end":{"line":198,"column":null}},"82":{"start":{"line":200,"column":12},"end":{"line":200,"column":null}},"83":{"start":{"line":205,"column":8},"end":{"line":205,"column":null}},"84":{"start":{"line":209,"column":8},"end":{"line":209,"column":null}},"85":{"start":{"line":213,"column":8},"end":{"line":215,"column":null}},"86":{"start":{"line":214,"column":12},"end":{"line":214,"column":null}},"87":{"start":{"line":215,"column":15},"end":{"line":215,"column":null}},"88":{"start":{"line":219,"column":8},"end":{"line":221,"column":null}},"89":{"start":{"line":220,"column":12},"end":{"line":220,"column":null}},"90":{"start":{"line":221,"column":15},"end":{"line":221,"column":null}},"91":{"start":{"line":226,"column":12},"end":{"line":226,"column":null}},"92":{"start":{"line":227,"column":12},"end":{"line":227,"column":null}},"93":{"start":{"line":229,"column":12},"end":{"line":229,"column":null}},"94":{"start":{"line":230,"column":12},"end":{"line":230,"column":null}},"95":{"start":{"line":235,"column":12},"end":{"line":235,"column":null}},"96":{"start":{"line":238,"column":16},"end":{"line":238,"column":null}},"97":{"start":{"line":240,"column":16},"end":{"line":240,"column":null}},"98":{"start":{"line":243,"column":12},"end":{"line":243,"column":null}},"99":{"start":{"line":8,"column":0},"end":{"line":8,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":14,"column":4},"end":{"line":14,"column":32}},"loc":{"start":{"line":14,"column":76},"end":{"line":22,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":24,"column":4},"end":{"line":24,"column":10}},"loc":{"start":{"line":24,"column":15},"end":{"line":26,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":28,"column":4},"end":{"line":28,"column":10}},"loc":{"start":{"line":28,"column":23},"end":{"line":30,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":32,"column":4},"end":{"line":32,"column":10}},"loc":{"start":{"line":32,"column":32},"end":{"line":34,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":36,"column":4},"end":{"line":36,"column":10}},"loc":{"start":{"line":36,"column":31},"end":{"line":43,"column":null}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":45,"column":4},"end":{"line":45,"column":10}},"loc":{"start":{"line":45,"column":32},"end":{"line":52,"column":null}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":54,"column":4},"end":{"line":54,"column":10}},"loc":{"start":{"line":54,"column":31},"end":{"line":58,"column":null}}},"7":{"name":"(anonymous_14)","decl":{"start":{"line":60,"column":11},"end":{"line":60,"column":17}},"loc":{"start":{"line":60,"column":62},"end":{"line":73,"column":null}}},"8":{"name":"(anonymous_15)","decl":{"start":{"line":75,"column":11},"end":{"line":75,"column":17}},"loc":{"start":{"line":75,"column":100},"end":{"line":88,"column":null}}},"9":{"name":"(anonymous_16)","decl":{"start":{"line":90,"column":4},"end":{"line":90,"column":10}},"loc":{"start":{"line":90,"column":34},"end":{"line":97,"column":null}}},"10":{"name":"(anonymous_17)","decl":{"start":{"line":99,"column":4},"end":{"line":99,"column":10}},"loc":{"start":{"line":99,"column":36},"end":{"line":101,"column":null}}},"11":{"name":"(anonymous_18)","decl":{"start":{"line":103,"column":4},"end":{"line":103,"column":10}},"loc":{"start":{"line":103,"column":80},"end":{"line":119,"column":null}}},"12":{"name":"(anonymous_19)","decl":{"start":{"line":121,"column":11},"end":{"line":121,"column":17}},"loc":{"start":{"line":121,"column":46},"end":{"line":125,"column":null}}},"13":{"name":"(anonymous_20)","decl":{"start":{"line":124,"column":40},"end":{"line":124,"column":44}},"loc":{"start":{"line":124,"column":49},"end":{"line":124,"column":70}}},"14":{"name":"(anonymous_21)","decl":{"start":{"line":127,"column":4},"end":{"line":127,"column":17}},"loc":{"start":{"line":127,"column":95},"end":{"line":157,"column":null}}},"15":{"name":"(anonymous_22)","decl":{"start":{"line":159,"column":4},"end":{"line":159,"column":17}},"loc":{"start":{"line":159,"column":66},"end":{"line":167,"column":null}}},"16":{"name":"(anonymous_23)","decl":{"start":{"line":169,"column":11},"end":{"line":169,"column":28}},"loc":{"start":{"line":169,"column":28},"end":{"line":171,"column":null}}},"17":{"name":"(anonymous_24)","decl":{"start":{"line":173,"column":4},"end":{"line":173,"column":10}},"loc":{"start":{"line":173,"column":30},"end":{"line":175,"column":null}}},"18":{"name":"(anonymous_25)","decl":{"start":{"line":177,"column":4},"end":{"line":177,"column":10}},"loc":{"start":{"line":177,"column":39},"end":{"line":187,"column":null}}},"19":{"name":"(anonymous_26)","decl":{"start":{"line":189,"column":4},"end":{"line":189,"column":10}},"loc":{"start":{"line":189,"column":55},"end":{"line":202,"column":null}}},"20":{"name":"(anonymous_27)","decl":{"start":{"line":204,"column":4},"end":{"line":204,"column":20}},"loc":{"start":{"line":204,"column":20},"end":{"line":206,"column":null}}},"21":{"name":"(anonymous_28)","decl":{"start":{"line":208,"column":4},"end":{"line":208,"column":10}},"loc":{"start":{"line":208,"column":28},"end":{"line":210,"column":null}}},"22":{"name":"(anonymous_29)","decl":{"start":{"line":212,"column":4},"end":{"line":212,"column":10}},"loc":{"start":{"line":212,"column":55},"end":{"line":216,"column":null}}},"23":{"name":"(anonymous_30)","decl":{"start":{"line":218,"column":4},"end":{"line":218,"column":10}},"loc":{"start":{"line":218,"column":73},"end":{"line":222,"column":null}}},"24":{"name":"(anonymous_31)","decl":{"start":{"line":224,"column":4},"end":{"line":224,"column":10}},"loc":{"start":{"line":224,"column":40},"end":{"line":232,"column":null}}},"25":{"name":"(anonymous_32)","decl":{"start":{"line":233,"column":4},"end":{"line":233,"column":10}},"loc":{"start":{"line":233,"column":37},"end":{"line":245,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":62,"column":8},"end":{"line":68,"column":null}},"type":"if","locations":[{"start":{"line":62,"column":8},"end":{"line":68,"column":null}},{"start":{"line":68,"column":15},"end":{"line":68,"column":null}}]},"1":{"loc":{"start":{"line":70,"column":8},"end":{"line":70,"column":null}},"type":"if","locations":[{"start":{"line":70,"column":8},"end":{"line":70,"column":null}}]},"2":{"loc":{"start":{"line":75,"column":51},"end":{"line":75,"column":100}},"type":"default-arg","locations":[{"start":{"line":75,"column":61},"end":{"line":75,"column":100}}]},"3":{"loc":{"start":{"line":100,"column":8},"end":{"line":100,"column":null}},"type":"if","locations":[{"start":{"line":100,"column":8},"end":{"line":100,"column":null}}]},"4":{"loc":{"start":{"line":124,"column":15},"end":{"line":124,"column":86}},"type":"cond-expr","locations":[{"start":{"line":124,"column":74},"end":{"line":124,"column":78}},{"start":{"line":124,"column":81},"end":{"line":124,"column":86}}]},"5":{"loc":{"start":{"line":152,"column":83},"end":{"line":152,"column":113}},"type":"cond-expr","locations":[{"start":{"line":152,"column":95},"end":{"line":152,"column":104}},{"start":{"line":152,"column":107},"end":{"line":152,"column":113}}]},"6":{"loc":{"start":{"line":174,"column":8},"end":{"line":174,"column":null}},"type":"if","locations":[{"start":{"line":174,"column":8},"end":{"line":174,"column":null}}]},"7":{"loc":{"start":{"line":190,"column":8},"end":{"line":190,"column":null}},"type":"if","locations":[{"start":{"line":190,"column":8},"end":{"line":190,"column":null}}]},"8":{"loc":{"start":{"line":213,"column":8},"end":{"line":215,"column":null}},"type":"if","locations":[{"start":{"line":213,"column":8},"end":{"line":215,"column":null}},{"start":{"line":215,"column":15},"end":{"line":215,"column":null}}]},"9":{"loc":{"start":{"line":219,"column":8},"end":{"line":221,"column":null}},"type":"if","locations":[{"start":{"line":219,"column":8},"end":{"line":221,"column":null}},{"start":{"line":221,"column":15},"end":{"line":221,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0},"b":{"0":[0,0],"1":[0],"2":[0],"3":[0],"4":[0,0],"5":[0,0],"6":[0],"7":[0],"8":[0,0],"9":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/git/GitDiffUtil.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/git/GitDiffUtil.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"6":{"start":{"line":8,"column":12},"end":{"line":8,"column":19}},"7":{"start":{"line":22,"column":23},"end":{"line":22,"column":34}},"8":{"start":{"line":31,"column":27},"end":{"line":31,"column":75}},"9":{"start":{"line":32,"column":21},"end":{"line":32,"column":51}},"10":{"start":{"line":33,"column":8},"end":{"line":33,"column":null}},"11":{"start":{"line":37,"column":8},"end":{"line":37,"column":null}},"12":{"start":{"line":38,"column":8},"end":{"line":38,"column":null}},"13":{"start":{"line":39,"column":27},"end":{"line":39,"column":71}},"14":{"start":{"line":40,"column":25},"end":{"line":40,"column":32}},"15":{"start":{"line":41,"column":20},"end":{"line":41,"column":48}},"16":{"start":{"line":42,"column":21},"end":{"line":42,"column":22}},"17":{"start":{"line":43,"column":12},"end":{"line":43,"column":null}},"18":{"start":{"line":43,"column":33},"end":{"line":43,"column":null}},"19":{"start":{"line":44,"column":25},"end":{"line":44,"column":45}},"20":{"start":{"line":45,"column":26},"end":{"line":45,"column":35}},"21":{"start":{"line":46,"column":30},"end":{"line":46,"column":54}},"22":{"start":{"line":47,"column":31},"end":{"line":50,"column":null}},"23":{"start":{"line":51,"column":12},"end":{"line":51,"column":null}},"24":{"start":{"line":53,"column":8},"end":{"line":53,"column":null}},"25":{"start":{"line":57,"column":8},"end":{"line":57,"column":null}},"26":{"start":{"line":59,"column":12},"end":{"line":59,"column":null}},"27":{"start":{"line":60,"column":12},"end":{"line":60,"column":null}},"28":{"start":{"line":66,"column":14},"end":{"line":66,"column":16}},"29":{"start":{"line":67,"column":8},"end":{"line":71,"column":null}},"30":{"start":{"line":69,"column":16},"end":{"line":69,"column":null}},"31":{"start":{"line":73,"column":8},"end":{"line":74,"column":null}},"32":{"start":{"line":74,"column":10},"end":{"line":74,"column":null}},"33":{"start":{"line":76,"column":31},"end":{"line":76,"column":43}},"34":{"start":{"line":77,"column":21},"end":{"line":77,"column":22}},"35":{"start":{"line":78,"column":12},"end":{"line":78,"column":null}},"36":{"start":{"line":79,"column":26},"end":{"line":79,"column":37}},"37":{"start":{"line":81,"column":12},"end":{"line":85,"column":null}},"38":{"start":{"line":87,"column":29},"end":{"line":87,"column":66}},"39":{"start":{"line":89,"column":32},"end":{"line":89,"column":55}},"40":{"start":{"line":92,"column":16},"end":{"line":92,"column":null}},"41":{"start":{"line":95,"column":25},"end":{"line":95,"column":26}},"42":{"start":{"line":96,"column":29},"end":{"line":96,"column":62}},"43":{"start":{"line":97,"column":16},"end":{"line":97,"column":null}},"44":{"start":{"line":99,"column":20},"end":{"line":99,"column":null}},"45":{"start":{"line":102,"column":30},"end":{"line":102,"column":79}},"46":{"start":{"line":103,"column":12},"end":{"line":103,"column":null}},"47":{"start":{"line":108,"column":8},"end":{"line":108,"column":null}},"48":{"start":{"line":110,"column":12},"end":{"line":110,"column":null}},"49":{"start":{"line":111,"column":12},"end":{"line":111,"column":null}},"50":{"start":{"line":114,"column":8},"end":{"line":119,"column":null}},"51":{"start":{"line":115,"column":32},"end":{"line":115,"column":41}},"52":{"start":{"line":117,"column":16},"end":{"line":117,"column":null}},"53":{"start":{"line":123,"column":26},"end":{"line":126,"column":null}},"54":{"start":{"line":130,"column":12},"end":{"line":130,"column":null}},"55":{"start":{"line":133,"column":12},"end":{"line":133,"column":null}},"56":{"start":{"line":137,"column":12},"end":{"line":137,"column":null}},"57":{"start":{"line":141,"column":12},"end":{"line":141,"column":null}},"58":{"start":{"line":145,"column":12},"end":{"line":161,"column":null}},"59":{"start":{"line":146,"column":28},"end":{"line":146,"column":33}},"60":{"start":{"line":147,"column":29},"end":{"line":147,"column":30}},"61":{"start":{"line":148,"column":32},"end":{"line":148,"column":40}},"62":{"start":{"line":152,"column":28},"end":{"line":152,"column":null}},"63":{"start":{"line":154,"column":24},"end":{"line":154,"column":null}},"64":{"start":{"line":155,"column":24},"end":{"line":155,"column":null}},"65":{"start":{"line":159,"column":20},"end":{"line":159,"column":null}},"66":{"start":{"line":165,"column":31},"end":{"line":167,"column":14}},"67":{"start":{"line":166,"column":16},"end":{"line":166,"column":null}},"68":{"start":{"line":170,"column":16},"end":{"line":170,"column":null}},"69":{"start":{"line":173,"column":8},"end":{"line":173,"column":null}},"70":{"start":{"line":24,"column":0},"end":{"line":24,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":30,"column":11},"end":{"line":30,"column":17}},"loc":{"start":{"line":30,"column":80},"end":{"line":34,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":36,"column":11},"end":{"line":36,"column":17}},"loc":{"start":{"line":36,"column":75},"end":{"line":54,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":56,"column":11},"end":{"line":56,"column":17}},"loc":{"start":{"line":56,"column":80},"end":{"line":105,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":67,"column":40},"end":{"line":67,"column":44}},"loc":{"start":{"line":67,"column":48},"end":{"line":71,"column":9}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":107,"column":11},"end":{"line":107,"column":17}},"loc":{"start":{"line":107,"column":84},"end":{"line":120,"column":null}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":114,"column":40},"end":{"line":114,"column":44}},"loc":{"start":{"line":114,"column":48},"end":{"line":119,"column":9}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":122,"column":11},"end":{"line":122,"column":28}},"loc":{"start":{"line":122,"column":68},"end":{"line":174,"column":null}}},"7":{"name":"(anonymous_14)","decl":{"start":{"line":145,"column":27},"end":{"line":145,"column":32}},"loc":{"start":{"line":145,"column":36},"end":{"line":161,"column":13}}},"8":{"name":"(anonymous_15)","decl":{"start":{"line":165,"column":62},"end":{"line":165,"column":72}},"loc":{"start":{"line":165,"column":100},"end":{"line":167,"column":13}}}},"branchMap":{"0":{"loc":{"start":{"line":43,"column":12},"end":{"line":43,"column":null}},"type":"if","locations":[{"start":{"line":43,"column":12},"end":{"line":43,"column":null}}]},"1":{"loc":{"start":{"line":73,"column":8},"end":{"line":74,"column":null}},"type":"if","locations":[{"start":{"line":73,"column":8},"end":{"line":74,"column":null}}]},"2":{"loc":{"start":{"line":129,"column":12},"end":{"line":129,"column":52}},"type":"binary-expr","locations":[{"start":{"line":129,"column":12},"end":{"line":129,"column":27}},{"start":{"line":129,"column":31},"end":{"line":129,"column":52}}]},"3":{"loc":{"start":{"line":132,"column":12},"end":{"line":132,"column":52}},"type":"binary-expr","locations":[{"start":{"line":132,"column":12},"end":{"line":132,"column":27}},{"start":{"line":132,"column":31},"end":{"line":132,"column":52}}]},"4":{"loc":{"start":{"line":136,"column":12},"end":{"line":136,"column":65}},"type":"binary-expr","locations":[{"start":{"line":136,"column":12},"end":{"line":136,"column":26}},{"start":{"line":136,"column":30},"end":{"line":136,"column":45}},{"start":{"line":136,"column":49},"end":{"line":136,"column":65}}]},"5":{"loc":{"start":{"line":140,"column":12},"end":{"line":140,"column":65}},"type":"binary-expr","locations":[{"start":{"line":140,"column":12},"end":{"line":140,"column":26}},{"start":{"line":140,"column":30},"end":{"line":140,"column":45}},{"start":{"line":140,"column":49},"end":{"line":140,"column":65}}]},"6":{"loc":{"start":{"line":144,"column":12},"end":{"line":144,"column":46}},"type":"binary-expr","locations":[{"start":{"line":144,"column":12},"end":{"line":144,"column":27}},{"start":{"line":144,"column":31},"end":{"line":144,"column":46}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"b":{"0":[0],"1":[0],"2":[0,0],"3":[0,0],"4":[0,0,0],"5":[0,0,0],"6":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/git/GitIdentity.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/git/GitIdentity.ts","statementMap":{"0":{"start":{"line":4,"column":24},"end":{"line":4,"column":38}},"1":{"start":{"line":7,"column":8},"end":{"line":7,"column":null}},"2":{"start":{"line":8,"column":8},"end":{"line":8,"column":null}},"3":{"start":{"line":15,"column":12},"end":{"line":15,"column":null}},"4":{"start":{"line":17,"column":12},"end":{"line":17,"column":null}},"5":{"start":{"line":20,"column":8},"end":{"line":20,"column":null}},"6":{"start":{"line":27,"column":12},"end":{"line":27,"column":null}},"7":{"start":{"line":29,"column":12},"end":{"line":29,"column":null}},"8":{"start":{"line":32,"column":8},"end":{"line":32,"column":null}},"9":{"start":{"line":3,"column":0},"end":{"line":3,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":4,"column":4},"end":{"line":4,"column":24}},"loc":{"start":{"line":4,"column":38},"end":{"line":4,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":6,"column":4},"end":{"line":6,"column":10}},"loc":{"start":{"line":6,"column":29},"end":{"line":9,"column":null}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":11,"column":12},"end":{"line":11,"column":18}},"loc":{"start":{"line":11,"column":29},"end":{"line":21,"column":null}}},"3":{"name":"(anonymous_3)","decl":{"start":{"line":23,"column":12},"end":{"line":23,"column":18}},"loc":{"start":{"line":23,"column":26},"end":{"line":33,"column":null}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":1},"f":{"0":0,"1":0,"2":0,"3":0},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/git/GitTags.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/git/GitTags.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":5,"column":24},"end":{"line":5,"column":32}},"2":{"start":{"line":5,"column":42},"end":{"line":5,"column":62}},"3":{"start":{"line":14,"column":29},"end":{"line":19,"column":10}},"4":{"start":{"line":21,"column":8},"end":{"line":22,"column":null}},"5":{"start":{"line":21,"column":29},"end":{"line":21,"column":71}},"6":{"start":{"line":22,"column":13},"end":{"line":22,"column":null}},"7":{"start":{"line":27,"column":32},"end":{"line":27,"column":92}},"8":{"start":{"line":32,"column":35},"end":{"line":36,"column":10}},"9":{"start":{"line":38,"column":29},"end":{"line":38,"column":60}},"10":{"start":{"line":40,"column":32},"end":{"line":40,"column":58}},"11":{"start":{"line":41,"column":8},"end":{"line":41,"column":22}},"12":{"start":{"line":45,"column":48},"end":{"line":45,"column":117}},"13":{"start":{"line":45,"column":75},"end":{"line":45,"column":116}},"14":{"start":{"line":48,"column":8},"end":{"line":50,"column":null}},"15":{"start":{"line":49,"column":40},"end":{"line":49,"column":116}},"16":{"start":{"line":53,"column":45},"end":{"line":53,"column":104}},"17":{"start":{"line":53,"column":66},"end":{"line":53,"column":103}},"18":{"start":{"line":55,"column":8},"end":{"line":55,"column":null}},"19":{"start":{"line":61,"column":19},"end":{"line":61,"column":48}},"20":{"start":{"line":62,"column":24},"end":{"line":62,"column":34}},"21":{"start":{"line":63,"column":8},"end":{"line":69,"column":null}},"22":{"start":{"line":64,"column":42},"end":{"line":65,"column":null}},"23":{"start":{"line":67,"column":12},"end":{"line":68,"column":null}},"24":{"start":{"line":67,"column":23},"end":{"line":67,"column":96}},"25":{"start":{"line":68,"column":17},"end":{"line":68,"column":null}},"26":{"start":{"line":69,"column":15},"end":{"line":69,"column":null}},"27":{"start":{"line":71,"column":8},"end":{"line":71,"column":null}},"28":{"start":{"line":75,"column":23},"end":{"line":75,"column":54}},"29":{"start":{"line":78,"column":12},"end":{"line":78,"column":null}},"30":{"start":{"line":82,"column":12},"end":{"line":85,"column":null}},"31":{"start":{"line":83,"column":16},"end":{"line":84,"column":null}},"32":{"start":{"line":83,"column":39},"end":{"line":83,"column":70}},"33":{"start":{"line":84,"column":21},"end":{"line":84,"column":null}},"34":{"start":{"line":88,"column":8},"end":{"line":88,"column":null}},"35":{"start":{"line":93,"column":22},"end":{"line":93,"column":51}},"36":{"start":{"line":96,"column":12},"end":{"line":96,"column":null}},"37":{"start":{"line":99,"column":31},"end":{"line":99,"column":69}},"38":{"start":{"line":100,"column":8},"end":{"line":100,"column":null}},"39":{"start":{"line":105,"column":33},"end":{"line":105,"column":62}},"40":{"start":{"line":109,"column":12},"end":{"line":109,"column":null}},"41":{"start":{"line":111,"column":12},"end":{"line":111,"column":null}},"42":{"start":{"line":115,"column":12},"end":{"line":115,"column":null}},"43":{"start":{"line":118,"column":29},"end":{"line":118,"column":70}},"44":{"start":{"line":120,"column":29},"end":{"line":129,"column":12}},"45":{"start":{"line":122,"column":41},"end":{"line":122,"column":58}},"46":{"start":{"line":123,"column":30},"end":{"line":123,"column":56}},"47":{"start":{"line":124,"column":12},"end":{"line":124,"column":null}},"48":{"start":{"line":127,"column":33},"end":{"line":127,"column":75}},"49":{"start":{"line":128,"column":12},"end":{"line":128,"column":null}},"50":{"start":{"line":131,"column":8},"end":{"line":131,"column":null}},"51":{"start":{"line":131,"column":39},"end":{"line":131,"column":47}},"52":{"start":{"line":135,"column":53},"end":{"line":135,"column":55}},"53":{"start":{"line":138,"column":8},"end":{"line":143,"column":null}},"54":{"start":{"line":139,"column":8},"end":{"line":142,"column":null}},"55":{"start":{"line":141,"column":40},"end":{"line":141,"column":70}},"56":{"start":{"line":146,"column":37},"end":{"line":146,"column":73}},"57":{"start":{"line":147,"column":34},"end":{"line":147,"column":105}},"58":{"start":{"line":147,"column":75},"end":{"line":147,"column":104}},"59":{"start":{"line":148,"column":8},"end":{"line":148,"column":null}},"60":{"start":{"line":4,"column":0},"end":{"line":4,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":4},"end":{"line":5,"column":24}},"loc":{"start":{"line":5,"column":62},"end":{"line":5,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":13,"column":4},"end":{"line":13,"column":10}},"loc":{"start":{"line":13,"column":26},"end":{"line":23,"column":null}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":25,"column":12},"end":{"line":25,"column":18}},"loc":{"start":{"line":25,"column":56},"end":{"line":56,"column":null}}},"3":{"name":"(anonymous_3)","decl":{"start":{"line":45,"column":64},"end":{"line":45,"column":70}},"loc":{"start":{"line":45,"column":75},"end":{"line":45,"column":116}}},"4":{"name":"(anonymous_4)","decl":{"start":{"line":49,"column":13},"end":{"line":49,"column":35}},"loc":{"start":{"line":49,"column":40},"end":{"line":49,"column":116}}},"5":{"name":"(anonymous_5)","decl":{"start":{"line":53,"column":58},"end":{"line":53,"column":61}},"loc":{"start":{"line":53,"column":66},"end":{"line":53,"column":103}}},"6":{"name":"(anonymous_6)","decl":{"start":{"line":58,"column":11},"end":{"line":58,"column":17}},"loc":{"start":{"line":58,"column":40},"end":{"line":72,"column":null}}},"7":{"name":"(anonymous_7)","decl":{"start":{"line":74,"column":12},"end":{"line":74,"column":47}},"loc":{"start":{"line":74,"column":76},"end":{"line":89,"column":null}}},"8":{"name":"(anonymous_8)","decl":{"start":{"line":82,"column":51},"end":{"line":82,"column":52}},"loc":{"start":{"line":82,"column":85},"end":{"line":85,"column":13}}},"9":{"name":"(anonymous_9)","decl":{"start":{"line":92,"column":11},"end":{"line":92,"column":17}},"loc":{"start":{"line":92,"column":40},"end":{"line":101,"column":null}}},"10":{"name":"(anonymous_10)","decl":{"start":{"line":104,"column":11},"end":{"line":104,"column":17}},"loc":{"start":{"line":104,"column":67},"end":{"line":132,"column":null}}},"11":{"name":"(anonymous_11)","decl":{"start":{"line":121,"column":15},"end":{"line":121,"column":21}},"loc":{"start":{"line":121,"column":24},"end":{"line":125,"column":11}}},"12":{"name":"(anonymous_12)","decl":{"start":{"line":126,"column":18},"end":{"line":126,"column":21}},"loc":{"start":{"line":126,"column":24},"end":{"line":129,"column":11}}},"13":{"name":"(anonymous_13)","decl":{"start":{"line":131,"column":32},"end":{"line":131,"column":35}},"loc":{"start":{"line":131,"column":39},"end":{"line":131,"column":47}}},"14":{"name":"(anonymous_14)","decl":{"start":{"line":134,"column":12},"end":{"line":134,"column":18}},"loc":{"start":{"line":134,"column":54},"end":{"line":149,"column":null}}},"15":{"name":"(anonymous_15)","decl":{"start":{"line":138,"column":22},"end":{"line":138,"column":33}},"loc":{"start":{"line":138,"column":37},"end":{"line":143,"column":9}}},"16":{"name":"(anonymous_16)","decl":{"start":{"line":141,"column":19},"end":{"line":141,"column":35}},"loc":{"start":{"line":141,"column":40},"end":{"line":141,"column":70}}},"17":{"name":"(anonymous_17)","decl":{"start":{"line":147,"column":43},"end":{"line":147,"column":44}},"loc":{"start":{"line":147,"column":75},"end":{"line":147,"column":104}}}},"branchMap":{"0":{"loc":{"start":{"line":21,"column":8},"end":{"line":22,"column":null}},"type":"if","locations":[{"start":{"line":21,"column":8},"end":{"line":22,"column":null}},{"start":{"line":22,"column":13},"end":{"line":22,"column":null}}]},"1":{"loc":{"start":{"line":63,"column":8},"end":{"line":69,"column":null}},"type":"if","locations":[{"start":{"line":63,"column":8},"end":{"line":69,"column":null}},{"start":{"line":69,"column":15},"end":{"line":69,"column":null}}]},"2":{"loc":{"start":{"line":67,"column":12},"end":{"line":68,"column":null}},"type":"if","locations":[{"start":{"line":67,"column":12},"end":{"line":68,"column":null}},{"start":{"line":68,"column":17},"end":{"line":68,"column":null}}]},"3":{"loc":{"start":{"line":77,"column":12},"end":{"line":77,"column":65}},"type":"binary-expr","locations":[{"start":{"line":77,"column":12},"end":{"line":77,"column":32}},{"start":{"line":77,"column":36},"end":{"line":77,"column":65}}]},"4":{"loc":{"start":{"line":83,"column":16},"end":{"line":84,"column":null}},"type":"if","locations":[{"start":{"line":83,"column":16},"end":{"line":84,"column":null}},{"start":{"line":84,"column":21},"end":{"line":84,"column":null}}]},"5":{"loc":{"start":{"line":128,"column":19},"end":{"line":128,"column":56}},"type":"binary-expr","locations":[{"start":{"line":128,"column":19},"end":{"line":128,"column":27}},{"start":{"line":128,"column":31},"end":{"line":128,"column":56}}]}},"s":{"0":1,"1":3,"2":3,"3":3,"4":3,"5":2,"6":1,"7":2,"8":2,"9":2,"10":2,"11":2,"12":2,"13":24,"14":2,"15":8,"16":2,"17":8,"18":2,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":1},"f":{"0":3,"1":3,"2":2,"3":24,"4":8,"5":8,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0},"b":{"0":[2,1],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/metadata/MetadataFiles.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/metadata/MetadataFiles.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"6":{"start":{"line":8,"column":0},"end":{"line":8,"column":null}},"7":{"start":{"line":9,"column":0},"end":{"line":9,"column":null}},"8":{"start":{"line":11,"column":12},"end":{"line":11,"column":19}},"9":{"start":{"line":18,"column":12},"end":{"line":18,"column":null}},"10":{"start":{"line":20,"column":12},"end":{"line":20,"column":null}},"11":{"start":{"line":24,"column":23},"end":{"line":24,"column":25}},"12":{"start":{"line":25,"column":27},"end":{"line":25,"column":65}},"13":{"start":{"line":26,"column":28},"end":{"line":26,"column":47}},"14":{"start":{"line":27,"column":28},"end":{"line":27,"column":91}},"15":{"start":{"line":29,"column":29},"end":{"line":29,"column":68}},"16":{"start":{"line":30,"column":28},"end":{"line":30,"column":81}},"17":{"start":{"line":31,"column":12},"end":{"line":31,"column":null}},"18":{"start":{"line":33,"column":12},"end":{"line":33,"column":null}},"19":{"start":{"line":35,"column":8},"end":{"line":35,"column":null}},"20":{"start":{"line":38,"column":23},"end":{"line":38,"column":25}},"21":{"start":{"line":39,"column":27},"end":{"line":39,"column":65}},"22":{"start":{"line":40,"column":28},"end":{"line":40,"column":47}},"23":{"start":{"line":41,"column":28},"end":{"line":41,"column":91}},"24":{"start":{"line":43,"column":29},"end":{"line":43,"column":68}},"25":{"start":{"line":44,"column":28},"end":{"line":44,"column":67}},"26":{"start":{"line":45,"column":12},"end":{"line":45,"column":null}},"27":{"start":{"line":47,"column":12},"end":{"line":47,"column":null}},"28":{"start":{"line":49,"column":8},"end":{"line":49,"column":null}},"29":{"start":{"line":53,"column":21},"end":{"line":53,"column":25}},"30":{"start":{"line":54,"column":28},"end":{"line":54,"column":47}},"31":{"start":{"line":55,"column":28},"end":{"line":55,"column":67}},"32":{"start":{"line":56,"column":8},"end":{"line":56,"column":null}},"33":{"start":{"line":59,"column":12},"end":{"line":59,"column":null}},"34":{"start":{"line":61,"column":8},"end":{"line":61,"column":null}},"35":{"start":{"line":65,"column":28},"end":{"line":65,"column":47}},"36":{"start":{"line":66,"column":24},"end":{"line":66,"column":48}},"37":{"start":{"line":67,"column":28},"end":{"line":67,"column":83}},"38":{"start":{"line":68,"column":49},"end":{"line":68,"column":68}},"39":{"start":{"line":70,"column":29},"end":{"line":70,"column":57}},"40":{"start":{"line":71,"column":28},"end":{"line":71,"column":66}},"41":{"start":{"line":72,"column":12},"end":{"line":72,"column":null}},"42":{"start":{"line":74,"column":27},"end":{"line":74,"column":57}},"43":{"start":{"line":75,"column":28},"end":{"line":75,"column":72}},"44":{"start":{"line":76,"column":26},"end":{"line":76,"column":59}},"45":{"start":{"line":77,"column":12},"end":{"line":77,"column":null}},"46":{"start":{"line":78,"column":12},"end":{"line":78,"column":null}},"47":{"start":{"line":81,"column":16},"end":{"line":81,"column":null}},"48":{"start":{"line":83,"column":35},"end":{"line":83,"column":53}},"49":{"start":{"line":84,"column":34},"end":{"line":84,"column":51}},"50":{"start":{"line":85,"column":45},"end":{"line":85,"column":74}},"51":{"start":{"line":86,"column":47},"end":{"line":86,"column":72}},"52":{"start":{"line":88,"column":20},"end":{"line":88,"column":null}},"53":{"start":{"line":91,"column":35},"end":{"line":91,"column":52}},"54":{"start":{"line":92,"column":36},"end":{"line":92,"column":80}},"55":{"start":{"line":93,"column":34},"end":{"line":93,"column":67}},"56":{"start":{"line":94,"column":20},"end":{"line":94,"column":null}},"57":{"start":{"line":95,"column":36},"end":{"line":95,"column":58}},"58":{"start":{"line":97,"column":20},"end":{"line":97,"column":null}},"59":{"start":{"line":100,"column":35},"end":{"line":100,"column":48}},"60":{"start":{"line":101,"column":36},"end":{"line":101,"column":80}},"61":{"start":{"line":102,"column":34},"end":{"line":102,"column":67}},"62":{"start":{"line":103,"column":20},"end":{"line":103,"column":null}},"63":{"start":{"line":104,"column":36},"end":{"line":104,"column":58}},"64":{"start":{"line":106,"column":20},"end":{"line":106,"column":null}},"65":{"start":{"line":108,"column":36},"end":{"line":108,"column":76}},"66":{"start":{"line":109,"column":20},"end":{"line":109,"column":null}},"67":{"start":{"line":113,"column":8},"end":{"line":113,"column":null}},"68":{"start":{"line":117,"column":38},"end":{"line":117,"column":74}},"69":{"start":{"line":118,"column":19},"end":{"line":118,"column":45}},"70":{"start":{"line":120,"column":12},"end":{"line":174,"column":null}},"71":{"start":{"line":121,"column":28},"end":{"line":121,"column":33}},"72":{"start":{"line":123,"column":29},"end":{"line":123,"column":30}},"73":{"start":{"line":124,"column":32},"end":{"line":124,"column":37}},"74":{"start":{"line":126,"column":24},"end":{"line":126,"column":null}},"75":{"start":{"line":131,"column":24},"end":{"line":131,"column":null}},"76":{"start":{"line":135,"column":28},"end":{"line":135,"column":null}},"77":{"start":{"line":136,"column":28},"end":{"line":136,"column":null}},"78":{"start":{"line":139,"column":28},"end":{"line":139,"column":null}},"79":{"start":{"line":141,"column":39},"end":{"line":143,"column":null}},"80":{"start":{"line":147,"column":48},"end":{"line":147,"column":71}},"81":{"start":{"line":148,"column":49},"end":{"line":148,"column":80}},"82":{"start":{"line":149,"column":32},"end":{"line":149,"column":null}},"83":{"start":{"line":152,"column":28},"end":{"line":152,"column":null}},"84":{"start":{"line":154,"column":24},"end":{"line":154,"column":null}},"85":{"start":{"line":155,"column":24},"end":{"line":155,"column":null}},"86":{"start":{"line":160,"column":39},"end":{"line":160,"column":57}},"87":{"start":{"line":163,"column":28},"end":{"line":163,"column":null}},"88":{"start":{"line":164,"column":28},"end":{"line":164,"column":null}},"89":{"start":{"line":167,"column":28},"end":{"line":167,"column":null}},"90":{"start":{"line":169,"column":39},"end":{"line":169,"column":90}},"91":{"start":{"line":170,"column":28},"end":{"line":170,"column":null}},"92":{"start":{"line":176,"column":12},"end":{"line":181,"column":null}},"93":{"start":{"line":178,"column":20},"end":{"line":178,"column":null}},"94":{"start":{"line":179,"column":20},"end":{"line":179,"column":null}},"95":{"start":{"line":186,"column":8},"end":{"line":186,"column":null}},"96":{"start":{"line":190,"column":35},"end":{"line":190,"column":119}},"97":{"start":{"line":190,"column":109},"end":{"line":190,"column":118}},"98":{"start":{"line":192,"column":12},"end":{"line":192,"column":null}},"99":{"start":{"line":194,"column":29},"end":{"line":197,"column":10}},"100":{"start":{"line":195,"column":42},"end":{"line":195,"column":74}},"101":{"start":{"line":196,"column":12},"end":{"line":196,"column":null}},"102":{"start":{"line":198,"column":8},"end":{"line":198,"column":null}},"103":{"start":{"line":209,"column":8},"end":{"line":209,"column":null}},"104":{"start":{"line":210,"column":33},"end":{"line":210,"column":68}},"105":{"start":{"line":211,"column":34},"end":{"line":211,"column":76}},"106":{"start":{"line":212,"column":40},"end":{"line":212,"column":69}},"107":{"start":{"line":213,"column":32},"end":{"line":213,"column":92}},"108":{"start":{"line":215,"column":31},"end":{"line":215,"column":43}},"109":{"start":{"line":218,"column":12},"end":{"line":218,"column":null}},"110":{"start":{"line":221,"column":21},"end":{"line":221,"column":69}},"111":{"start":{"line":223,"column":12},"end":{"line":223,"column":null}},"112":{"start":{"line":227,"column":24},"end":{"line":227,"column":44}},"113":{"start":{"line":229,"column":16},"end":{"line":229,"column":null}},"114":{"start":{"line":230,"column":16},"end":{"line":230,"column":null}},"115":{"start":{"line":234,"column":23},"end":{"line":234,"column":48}},"116":{"start":{"line":237,"column":12},"end":{"line":237,"column":null}},"117":{"start":{"line":240,"column":28},"end":{"line":240,"column":47}},"118":{"start":{"line":243,"column":12},"end":{"line":243,"column":null}},"119":{"start":{"line":246,"column":21},"end":{"line":246,"column":22}},"120":{"start":{"line":247,"column":25},"end":{"line":247,"column":58}},"121":{"start":{"line":248,"column":12},"end":{"line":248,"column":null}},"122":{"start":{"line":250,"column":16},"end":{"line":250,"column":null}},"123":{"start":{"line":255,"column":36},"end":{"line":255,"column":38}},"124":{"start":{"line":257,"column":12},"end":{"line":257,"column":null}},"125":{"start":{"line":259,"column":28},"end":{"line":259,"column":52}},"126":{"start":{"line":260,"column":12},"end":{"line":260,"column":null}},"127":{"start":{"line":262,"column":20},"end":{"line":262,"column":51}},"128":{"start":{"line":263,"column":21},"end":{"line":263,"column":22}},"129":{"start":{"line":265,"column":34},"end":{"line":265,"column":58}},"130":{"start":{"line":266,"column":39},"end":{"line":266,"column":61}},"131":{"start":{"line":267,"column":16},"end":{"line":267,"column":null}},"132":{"start":{"line":268,"column":33},"end":{"line":268,"column":66}},"133":{"start":{"line":269,"column":16},"end":{"line":269,"column":null}},"134":{"start":{"line":276,"column":31},"end":{"line":276,"column":70}},"135":{"start":{"line":277,"column":36},"end":{"line":277,"column":104}},"136":{"start":{"line":278,"column":29},"end":{"line":278,"column":71}},"137":{"start":{"line":279,"column":29},"end":{"line":279,"column":74}},"138":{"start":{"line":281,"column":16},"end":{"line":281,"column":null}},"139":{"start":{"line":287,"column":12},"end":{"line":287,"column":null}},"140":{"start":{"line":288,"column":28},"end":{"line":288,"column":31}},"141":{"start":{"line":289,"column":37},"end":{"line":289,"column":39}},"142":{"start":{"line":290,"column":31},"end":{"line":290,"column":33}},"143":{"start":{"line":291,"column":25},"end":{"line":291,"column":26}},"144":{"start":{"line":292,"column":16},"end":{"line":292,"column":null}},"145":{"start":{"line":293,"column":16},"end":{"line":293,"column":null}},"146":{"start":{"line":295,"column":40},"end":{"line":295,"column":60}},"147":{"start":{"line":296,"column":45},"end":{"line":296,"column":69}},"148":{"start":{"line":297,"column":20},"end":{"line":297,"column":null}},"149":{"start":{"line":298,"column":20},"end":{"line":298,"column":null}},"150":{"start":{"line":299,"column":20},"end":{"line":299,"column":null}},"151":{"start":{"line":300,"column":20},"end":{"line":300,"column":null}},"152":{"start":{"line":301,"column":20},"end":{"line":301,"column":null}},"153":{"start":{"line":304,"column":28},"end":{"line":304,"column":null}},"154":{"start":{"line":307,"column":20},"end":{"line":307,"column":null}},"155":{"start":{"line":311,"column":16},"end":{"line":311,"column":null}},"156":{"start":{"line":314,"column":16},"end":{"line":314,"column":null}},"157":{"start":{"line":319,"column":12},"end":{"line":319,"column":null}},"158":{"start":{"line":320,"column":28},"end":{"line":320,"column":31}},"159":{"start":{"line":321,"column":25},"end":{"line":321,"column":26}},"160":{"start":{"line":322,"column":16},"end":{"line":322,"column":null}},"161":{"start":{"line":323,"column":16},"end":{"line":323,"column":null}},"162":{"start":{"line":325,"column":40},"end":{"line":325,"column":60}},"163":{"start":{"line":326,"column":45},"end":{"line":326,"column":69}},"164":{"start":{"line":327,"column":20},"end":{"line":327,"column":null}},"165":{"start":{"line":328,"column":20},"end":{"line":328,"column":null}},"166":{"start":{"line":332,"column":28},"end":{"line":332,"column":null}},"167":{"start":{"line":335,"column":20},"end":{"line":335,"column":null}},"168":{"start":{"line":339,"column":16},"end":{"line":339,"column":null}},"169":{"start":{"line":14,"column":18},"end":{"line":14,"column":null}},"170":{"start":{"line":13,"column":21},"end":{"line":13,"column":null}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"loc":{"start":{"line":16,"column":4},"end":{"line":22,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":23,"column":4},"end":{"line":23,"column":11}},"loc":{"start":{"line":23,"column":42},"end":{"line":36,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":37,"column":4},"end":{"line":37,"column":11}},"loc":{"start":{"line":37,"column":55},"end":{"line":50,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":52,"column":11},"end":{"line":52,"column":18}},"loc":{"start":{"line":52,"column":65},"end":{"line":62,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":63,"column":11},"end":{"line":63,"column":18}},"loc":{"start":{"line":63,"column":74},"end":{"line":114,"column":null}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":116,"column":11},"end":{"line":116,"column":25}},"loc":{"start":{"line":116,"column":63},"end":{"line":183,"column":null}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":120,"column":35},"end":{"line":120,"column":47}},"loc":{"start":{"line":120,"column":51},"end":{"line":174,"column":13}}},"7":{"name":"(anonymous_14)","decl":{"start":{"line":176,"column":26},"end":{"line":176,"column":29}},"loc":{"start":{"line":176,"column":33},"end":{"line":181,"column":13}}},"8":{"name":"(anonymous_15)","decl":{"start":{"line":185,"column":11},"end":{"line":185,"column":18}},"loc":{"start":{"line":185,"column":35},"end":{"line":187,"column":null}}},"9":{"name":"(anonymous_16)","decl":{"start":{"line":189,"column":11},"end":{"line":189,"column":17}},"loc":{"start":{"line":189,"column":50},"end":{"line":199,"column":null}}},"10":{"name":"(anonymous_17)","decl":{"start":{"line":190,"column":100},"end":{"line":190,"column":104}},"loc":{"start":{"line":190,"column":109},"end":{"line":190,"column":118}}},"11":{"name":"(anonymous_18)","decl":{"start":{"line":194,"column":54},"end":{"line":194,"column":67}},"loc":{"start":{"line":194,"column":71},"end":{"line":197,"column":9}}},"12":{"name":"(anonymous_19)","decl":{"start":{"line":208,"column":11},"end":{"line":208,"column":18}},"loc":{"start":{"line":208,"column":65},"end":{"line":342,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":57,"column":12},"end":{"line":57,"column":101}},"type":"binary-expr","locations":[{"start":{"line":57,"column":12},"end":{"line":57,"column":54}},{"start":{"line":57,"column":58},"end":{"line":57,"column":101}}]},"1":{"loc":{"start":{"line":59,"column":21},"end":{"line":59,"column":85}},"type":"binary-expr","locations":[{"start":{"line":59,"column":21},"end":{"line":59,"column":50}},{"start":{"line":59,"column":54},"end":{"line":59,"column":85}}]},"2":{"loc":{"start":{"line":87,"column":20},"end":{"line":87,"column":73}},"type":"binary-expr","locations":[{"start":{"line":87,"column":20},"end":{"line":87,"column":45}},{"start":{"line":87,"column":49},"end":{"line":87,"column":73}}]},"3":{"loc":{"start":{"line":116,"column":45},"end":{"line":116,"column":63}},"type":"default-arg","locations":[{"start":{"line":116,"column":59},"end":{"line":116,"column":63}}]},"4":{"loc":{"start":{"line":119,"column":12},"end":{"line":119,"column":68}},"type":"binary-expr","locations":[{"start":{"line":119,"column":12},"end":{"line":119,"column":40}},{"start":{"line":119,"column":44},"end":{"line":119,"column":68}}]},"5":{"loc":{"start":{"line":128,"column":24},"end":{"line":129,"column":85}},"type":"binary-expr","locations":[{"start":{"line":128,"column":24},"end":{"line":128,"column":55}},{"start":{"line":129,"column":24},"end":{"line":129,"column":85}}]},"6":{"loc":{"start":{"line":138,"column":28},"end":{"line":138,"column":87}},"type":"binary-expr","locations":[{"start":{"line":138,"column":28},"end":{"line":138,"column":40}},{"start":{"line":138,"column":45},"end":{"line":138,"column":56}},{"start":{"line":138,"column":60},"end":{"line":138,"column":87}}]},"7":{"loc":{"start":{"line":161,"column":24},"end":{"line":161,"column":98}},"type":"binary-expr","locations":[{"start":{"line":161,"column":24},"end":{"line":161,"column":53}},{"start":{"line":161,"column":57},"end":{"line":161,"column":98}}]},"8":{"loc":{"start":{"line":166,"column":28},"end":{"line":166,"column":87}},"type":"binary-expr","locations":[{"start":{"line":166,"column":28},"end":{"line":166,"column":40}},{"start":{"line":166,"column":45},"end":{"line":166,"column":56}},{"start":{"line":166,"column":60},"end":{"line":166,"column":87}}]},"9":{"loc":{"start":{"line":191,"column":12},"end":{"line":191,"column":65}},"type":"binary-expr","locations":[{"start":{"line":191,"column":12},"end":{"line":191,"column":31}},{"start":{"line":191,"column":35},"end":{"line":191,"column":65}}]},"10":{"loc":{"start":{"line":275,"column":12},"end":{"line":275,"column":95}},"type":"binary-expr","locations":[{"start":{"line":275,"column":12},"end":{"line":275,"column":53}},{"start":{"line":275,"column":57},"end":{"line":275,"column":95}}]},"11":{"loc":{"start":{"line":324,"column":20},"end":{"line":324,"column":77}},"type":"binary-expr","locations":[{"start":{"line":324,"column":20},"end":{"line":324,"column":47}},{"start":{"line":324,"column":51},"end":{"line":324,"column":77}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":0,"114":0,"115":0,"116":0,"117":0,"118":0,"119":0,"120":0,"121":0,"122":0,"123":0,"124":0,"125":0,"126":0,"127":0,"128":0,"129":0,"130":0,"131":0,"132":0,"133":0,"134":0,"135":0,"136":0,"137":0,"138":0,"139":0,"140":0,"141":0,"142":0,"143":0,"144":0,"145":0,"146":0,"147":0,"148":0,"149":0,"150":0,"151":0,"152":0,"153":0,"154":0,"155":0,"156":0,"157":0,"158":0,"159":0,"160":0,"161":0,"162":0,"163":0,"164":0,"165":0,"166":0,"167":0,"168":0,"169":1,"170":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0],"4":[0,0],"5":[0,0],"6":[0,0,0],"7":[0,0],"8":[0,0,0],"9":[0,0],"10":[0,0],"11":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/metadata/MetadataInfo.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/metadata/MetadataInfo.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":6,"column":13},"end":{"line":6,"column":null}},"4":{"start":{"line":7,"column":22},"end":{"line":44,"column":null}},"5":{"start":{"line":103,"column":41},"end":{"line":103,"column":43}},"6":{"start":{"line":104,"column":27},"end":{"line":104,"column":99}},"7":{"start":{"line":105,"column":25},"end":{"line":105,"column":62}},"8":{"start":{"line":106,"column":31},"end":{"line":106,"column":51}},"9":{"start":{"line":107,"column":8},"end":{"line":145,"column":null}},"10":{"start":{"line":108,"column":35},"end":{"line":108,"column":63}},"11":{"start":{"line":111,"column":20},"end":{"line":111,"column":null}},"12":{"start":{"line":112,"column":20},"end":{"line":112,"column":null}},"13":{"start":{"line":114,"column":20},"end":{"line":114,"column":null}},"14":{"start":{"line":115,"column":20},"end":{"line":115,"column":null}},"15":{"start":{"line":118,"column":12},"end":{"line":118,"column":null}},"16":{"start":{"line":120,"column":44},"end":{"line":120,"column":59}},"17":{"start":{"line":122,"column":20},"end":{"line":122,"column":null}},"18":{"start":{"line":124,"column":16},"end":{"line":124,"column":null}},"19":{"start":{"line":129,"column":16},"end":{"line":142,"column":null}},"20":{"start":{"line":130,"column":41},"end":{"line":130,"column":63}},"21":{"start":{"line":132,"column":62},"end":{"line":132,"column":64}},"22":{"start":{"line":133,"column":24},"end":{"line":133,"column":null}},"23":{"start":{"line":134,"column":24},"end":{"line":134,"column":null}},"24":{"start":{"line":135,"column":24},"end":{"line":135,"column":null}},"25":{"start":{"line":136,"column":24},"end":{"line":136,"column":null}},"26":{"start":{"line":137,"column":24},"end":{"line":137,"column":null}},"27":{"start":{"line":138,"column":24},"end":{"line":138,"column":null}},"28":{"start":{"line":139,"column":24},"end":{"line":139,"column":null}},"29":{"start":{"line":140,"column":24},"end":{"line":140,"column":null}},"30":{"start":{"line":144,"column":12},"end":{"line":144,"column":null}},"31":{"start":{"line":146,"column":8},"end":{"line":146,"column":null}},"32":{"start":{"line":150,"column":22},"end":{"line":150,"column":64}},"33":{"start":{"line":151,"column":24},"end":{"line":151,"column":26}},"34":{"start":{"line":153,"column":12},"end":{"line":153,"column":null}},"35":{"start":{"line":155,"column":12},"end":{"line":155,"column":null}},"36":{"start":{"line":158,"column":27},"end":{"line":158,"column":29}},"37":{"start":{"line":160,"column":27},"end":{"line":160,"column":45}},"38":{"start":{"line":161,"column":26},"end":{"line":161,"column":43}},"39":{"start":{"line":162,"column":37},"end":{"line":162,"column":66}},"40":{"start":{"line":163,"column":39},"end":{"line":163,"column":64}},"41":{"start":{"line":164,"column":31},"end":{"line":164,"column":54}},"42":{"start":{"line":166,"column":12},"end":{"line":166,"column":null}},"43":{"start":{"line":171,"column":12},"end":{"line":171,"column":null}},"44":{"start":{"line":176,"column":12},"end":{"line":176,"column":null}},"45":{"start":{"line":181,"column":12},"end":{"line":181,"column":null}},"46":{"start":{"line":186,"column":12},"end":{"line":186,"column":null}},"47":{"start":{"line":188,"column":23},"end":{"line":188,"column":49}},"48":{"start":{"line":189,"column":25},"end":{"line":189,"column":26}},"49":{"start":{"line":190,"column":35},"end":{"line":190,"column":57}},"50":{"start":{"line":196,"column":20},"end":{"line":196,"column":null}},"51":{"start":{"line":197,"column":20},"end":{"line":197,"column":null}},"52":{"start":{"line":201,"column":8},"end":{"line":201,"column":null}},"53":{"start":{"line":101,"column":0},"end":{"line":101,"column":13}},"54":{"start":{"line":205,"column":13},"end":{"line":205,"column":null}},"55":{"start":{"line":206,"column":13},"end":{"line":212,"column":null}},"56":{"start":{"line":214,"column":13},"end":{"line":214,"column":null}}},"fnMap":{"0":{"name":"(anonymous_6)","decl":{"start":{"line":102,"column":4},"end":{"line":102,"column":11}},"loc":{"start":{"line":102,"column":27},"end":{"line":147,"column":null}}},"1":{"name":"(anonymous_7)","decl":{"start":{"line":107,"column":50},"end":{"line":107,"column":58}},"loc":{"start":{"line":107,"column":62},"end":{"line":145,"column":9}}},"2":{"name":"(anonymous_8)","decl":{"start":{"line":129,"column":48},"end":{"line":129,"column":55}},"loc":{"start":{"line":129,"column":59},"end":{"line":142,"column":17}}},"3":{"name":"(anonymous_9)","decl":{"start":{"line":149,"column":4},"end":{"line":149,"column":11}},"loc":{"start":{"line":149,"column":79},"end":{"line":202,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":149,"column":49},"end":{"line":149,"column":79}},"type":"default-arg","locations":[{"start":{"line":149,"column":75},"end":{"line":149,"column":79}}]},"1":{"loc":{"start":{"line":165,"column":12},"end":{"line":165,"column":116}},"type":"binary-expr","locations":[{"start":{"line":165,"column":12},"end":{"line":165,"column":41}},{"start":{"line":165,"column":46},"end":{"line":165,"column":87}},{"start":{"line":165,"column":91},"end":{"line":165,"column":115}}]},"2":{"loc":{"start":{"line":168,"column":12},"end":{"line":169,"column":83}},"type":"binary-expr","locations":[{"start":{"line":168,"column":12},"end":{"line":168,"column":40}},{"start":{"line":169,"column":13},"end":{"line":169,"column":54}},{"start":{"line":169,"column":58},"end":{"line":169,"column":82}}]},"3":{"loc":{"start":{"line":173,"column":12},"end":{"line":174,"column":83}},"type":"binary-expr","locations":[{"start":{"line":173,"column":12},"end":{"line":173,"column":51}},{"start":{"line":174,"column":13},"end":{"line":174,"column":54}},{"start":{"line":174,"column":58},"end":{"line":174,"column":82}}]},"4":{"loc":{"start":{"line":178,"column":12},"end":{"line":179,"column":83}},"type":"binary-expr","locations":[{"start":{"line":178,"column":12},"end":{"line":178,"column":53}},{"start":{"line":179,"column":13},"end":{"line":179,"column":54}},{"start":{"line":179,"column":58},"end":{"line":179,"column":82}}]},"5":{"loc":{"start":{"line":183,"column":12},"end":{"line":184,"column":83}},"type":"binary-expr","locations":[{"start":{"line":183,"column":12},"end":{"line":183,"column":45}},{"start":{"line":184,"column":13},"end":{"line":184,"column":54}},{"start":{"line":184,"column":58},"end":{"line":184,"column":82}}]},"6":{"loc":{"start":{"line":192,"column":20},"end":{"line":194,"column":62}},"type":"binary-expr","locations":[{"start":{"line":192,"column":20},"end":{"line":192,"column":62}},{"start":{"line":193,"column":21},"end":{"line":193,"column":60}},{"start":{"line":193,"column":64},"end":{"line":193,"column":89}},{"start":{"line":194,"column":20},"end":{"line":194,"column":62}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":149,"11":1,"12":1,"13":1,"14":1,"15":149,"16":4,"17":0,"18":4,"19":9,"20":25,"21":9,"22":9,"23":9,"24":9,"25":9,"26":9,"27":9,"28":9,"29":9,"30":149,"31":1,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":1,"54":1,"55":1,"56":1},"f":{"0":1,"1":149,"2":25,"3":0},"b":{"0":[0],"1":[0,0,0],"2":[0,0,0],"3":[0,0,0],"4":[0,0,0],"5":[0,0,0],"6":[0,0,0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/metadata/SettingsFetcher.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/metadata/SettingsFetcher.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":3,"column":11},"end":{"line":3,"column":30}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"4":{"start":{"line":9,"column":8},"end":{"line":9,"column":null}},"5":{"start":{"line":13,"column":8},"end":{"line":13,"column":null}},"6":{"start":{"line":14,"column":30},"end":{"line":16,"column":28}},"7":{"start":{"line":17,"column":25},"end":{"line":17,"column":74}},"8":{"start":{"line":18,"column":23},"end":{"line":18,"column":38}},"9":{"start":{"line":19,"column":29},"end":{"line":19,"column":103}},"10":{"start":{"line":20,"column":8},"end":{"line":20,"column":null}},"11":{"start":{"line":7,"column":0},"end":{"line":7,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":8,"column":4},"end":{"line":8,"column":16}},"loc":{"start":{"line":8,"column":30},"end":{"line":10,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":12,"column":11},"end":{"line":12,"column":17}},"loc":{"start":{"line":12,"column":65},"end":{"line":21,"column":null}}}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":5,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":1},"f":{"0":5,"1":0},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/org/SFPOrg.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/org/SFPOrg.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"3":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"4":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"5":{"start":{"line":8,"column":0},"end":{"line":8,"column":null}},"6":{"start":{"line":15,"column":20},"end":{"line":15,"column":22}},"7":{"start":{"line":17,"column":13},"end":{"line":21,"column":null}},"8":{"start":{"line":22,"column":12},"end":{"line":22,"column":null}},"9":{"start":{"line":24,"column":12},"end":{"line":30,"column":null}},"10":{"start":{"line":32,"column":8},"end":{"line":32,"column":null}},"11":{"start":{"line":43,"column":71},"end":{"line":45,"column":null}},"12":{"start":{"line":47,"column":12},"end":{"line":47,"column":null}},"13":{"start":{"line":48,"column":12},"end":{"line":48,"column":null}},"14":{"start":{"line":49,"column":37},"end":{"line":49,"column":71}},"15":{"start":{"line":50,"column":30},"end":{"line":50,"column":52}},"16":{"start":{"line":53,"column":20},"end":{"line":53,"column":null}},"17":{"start":{"line":55,"column":24},"end":{"line":55,"column":null}},"18":{"start":{"line":56,"column":24},"end":{"line":56,"column":null}},"19":{"start":{"line":61,"column":12},"end":{"line":67,"column":null}},"20":{"start":{"line":69,"column":8},"end":{"line":69,"column":null}},"21":{"start":{"line":77,"column":25},"end":{"line":77,"column":67}},"22":{"start":{"line":79,"column":8},"end":{"line":87,"column":null}},"23":{"start":{"line":89,"column":26},"end":{"line":89,"column":49}},"24":{"start":{"line":92,"column":12},"end":{"line":101,"column":null}},"25":{"start":{"line":103,"column":12},"end":{"line":113,"column":null}},"26":{"start":{"line":116,"column":8},"end":{"line":124,"column":null}},"27":{"start":{"line":125,"column":8},"end":{"line":125,"column":null}},"28":{"start":{"line":129,"column":33},"end":{"line":129,"column":67}},"29":{"start":{"line":131,"column":26},"end":{"line":131,"column":48}},"30":{"start":{"line":134,"column":16},"end":{"line":134,"column":null}},"31":{"start":{"line":137,"column":8},"end":{"line":137,"column":null}},"32":{"start":{"line":143,"column":52},"end":{"line":143,"column":54}},"33":{"start":{"line":145,"column":22},"end":{"line":145,"column":85}},"34":{"start":{"line":147,"column":8},"end":{"line":161,"column":null}},"35":{"start":{"line":148,"column":39},"end":{"line":148,"column":231}},"36":{"start":{"line":150,"column":49},"end":{"line":158,"column":null}},"37":{"start":{"line":160,"column":12},"end":{"line":160,"column":null}},"38":{"start":{"line":163,"column":8},"end":{"line":163,"column":null}},"39":{"start":{"line":170,"column":34},"end":{"line":170,"column":73}},"40":{"start":{"line":171,"column":8},"end":{"line":171,"column":null}},"41":{"start":{"line":171,"column":62},"end":{"line":171,"column":97}},"42":{"start":{"line":177,"column":8},"end":{"line":185,"column":null}},"43":{"start":{"line":178,"column":26},"end":{"line":178,"column":108}},"44":{"start":{"line":179,"column":12},"end":{"line":182,"column":null}},"45":{"start":{"line":180,"column":16},"end":{"line":181,"column":null}},"46":{"start":{"line":184,"column":12},"end":{"line":184,"column":null}},"47":{"start":{"line":185,"column":15},"end":{"line":185,"column":null}},"48":{"start":{"line":189,"column":8},"end":{"line":189,"column":null}},"49":{"start":{"line":196,"column":24},"end":{"line":196,"column":64}},"50":{"start":{"line":197,"column":52},"end":{"line":197,"column":54}},"51":{"start":{"line":198,"column":35},"end":{"line":198,"column":74}},"52":{"start":{"line":200,"column":8},"end":{"line":217,"column":null}},"53":{"start":{"line":201,"column":55},"end":{"line":206,"column":null}},"54":{"start":{"line":207,"column":31},"end":{"line":207,"column":94}},"55":{"start":{"line":207,"column":67},"end":{"line":207,"column":93}},"56":{"start":{"line":209,"column":16},"end":{"line":209,"column":null}},"57":{"start":{"line":210,"column":16},"end":{"line":211,"column":null}},"58":{"start":{"line":210,"column":49},"end":{"line":210,"column":92}},"59":{"start":{"line":211,"column":21},"end":{"line":211,"column":null}},"60":{"start":{"line":213,"column":16},"end":{"line":213,"column":null}},"61":{"start":{"line":214,"column":16},"end":{"line":214,"column":null}},"62":{"start":{"line":216,"column":12},"end":{"line":216,"column":null}},"63":{"start":{"line":219,"column":8},"end":{"line":235,"column":null}},"64":{"start":{"line":220,"column":31},"end":{"line":220,"column":103}},"65":{"start":{"line":220,"column":65},"end":{"line":220,"column":102}},"66":{"start":{"line":222,"column":59},"end":{"line":226,"column":null}},"67":{"start":{"line":227,"column":16},"end":{"line":229,"column":null}},"68":{"start":{"line":227,"column":56},"end":{"line":227,"column":99}},"69":{"start":{"line":228,"column":21},"end":{"line":229,"column":null}},"70":{"start":{"line":228,"column":64},"end":{"line":228,"column":99}},"71":{"start":{"line":229,"column":21},"end":{"line":229,"column":null}},"72":{"start":{"line":231,"column":16},"end":{"line":231,"column":null}},"73":{"start":{"line":232,"column":16},"end":{"line":232,"column":null}},"74":{"start":{"line":233,"column":16},"end":{"line":233,"column":null}},"75":{"start":{"line":236,"column":8},"end":{"line":236,"column":null}},"76":{"start":{"line":10,"column":0},"end":{"line":10,"column":21}},"77":{"start":{"line":241,"column":4},"end":{"line":244,"column":36}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":14,"column":11},"end":{"line":14,"column":17}},"loc":{"start":{"line":14,"column":85},"end":{"line":33,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":39,"column":11},"end":{"line":39,"column":17}},"loc":{"start":{"line":41,"column":30},"end":{"line":70,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":76,"column":11},"end":{"line":76,"column":17}},"loc":{"start":{"line":76,"column":75},"end":{"line":126,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":128,"column":12},"end":{"line":128,"column":18}},"loc":{"start":{"line":128,"column":60},"end":{"line":138,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":142,"column":11},"end":{"line":142,"column":17}},"loc":{"start":{"line":142,"column":43},"end":{"line":164,"column":null}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":147,"column":25},"end":{"line":147,"column":31}},"loc":{"start":{"line":147,"column":35},"end":{"line":161,"column":9}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":169,"column":11},"end":{"line":169,"column":17}},"loc":{"start":{"line":169,"column":47},"end":{"line":172,"column":null}}},"7":{"name":"(anonymous_14)","decl":{"start":{"line":171,"column":41},"end":{"line":171,"column":57}},"loc":{"start":{"line":171,"column":62},"end":{"line":171,"column":97}}},"8":{"name":"(anonymous_15)","decl":{"start":{"line":176,"column":11},"end":{"line":176,"column":17}},"loc":{"start":{"line":176,"column":32},"end":{"line":186,"column":null}}},"9":{"name":"(anonymous_16)","decl":{"start":{"line":179,"column":29},"end":{"line":179,"column":35}},"loc":{"start":{"line":179,"column":39},"end":{"line":182,"column":13}}},"10":{"name":"(anonymous_17)","decl":{"start":{"line":188,"column":11},"end":{"line":188,"column":17}},"loc":{"start":{"line":188,"column":25},"end":{"line":190,"column":null}}},"11":{"name":"(anonymous_18)","decl":{"start":{"line":195,"column":11},"end":{"line":195,"column":17}},"loc":{"start":{"line":195,"column":41},"end":{"line":237,"column":null}}},"12":{"name":"(anonymous_19)","decl":{"start":{"line":200,"column":27},"end":{"line":200,"column":35}},"loc":{"start":{"line":200,"column":39},"end":{"line":217,"column":9}}},"13":{"name":"(anonymous_20)","decl":{"start":{"line":207,"column":58},"end":{"line":207,"column":62}},"loc":{"start":{"line":207,"column":67},"end":{"line":207,"column":93}}},"14":{"name":"(anonymous_21)","decl":{"start":{"line":219,"column":38},"end":{"line":219,"column":57}},"loc":{"start":{"line":219,"column":61},"end":{"line":235,"column":9}}},"15":{"name":"(anonymous_22)","decl":{"start":{"line":220,"column":56},"end":{"line":220,"column":60}},"loc":{"start":{"line":220,"column":65},"end":{"line":220,"column":102}}}},"branchMap":{"0":{"loc":{"start":{"line":14,"column":39},"end":{"line":14,"column":70}},"type":"default-arg","locations":[{"start":{"line":14,"column":57},"end":{"line":14,"column":70}}]},"1":{"loc":{"start":{"line":83,"column":21},"end":{"line":83,"column":52}},"type":"cond-expr","locations":[{"start":{"line":83,"column":34},"end":{"line":83,"column":44}},{"start":{"line":83,"column":47},"end":{"line":83,"column":52}}]},"2":{"loc":{"start":{"line":119,"column":20},"end":{"line":119,"column":null}},"type":"cond-expr","locations":[{"start":{"line":119,"column":33},"end":{"line":119,"column":43}},{"start":{"line":119,"column":46},"end":{"line":119,"column":null}}]},"3":{"loc":{"start":{"line":177,"column":8},"end":{"line":185,"column":null}},"type":"if","locations":[{"start":{"line":177,"column":8},"end":{"line":185,"column":null}},{"start":{"line":185,"column":15},"end":{"line":185,"column":null}}]},"4":{"loc":{"start":{"line":181,"column":20},"end":{"line":181,"column":113}},"type":"cond-expr","locations":[{"start":{"line":181,"column":60},"end":{"line":181,"column":65}},{"start":{"line":181,"column":68},"end":{"line":181,"column":113}}]},"5":{"loc":{"start":{"line":181,"column":68},"end":{"line":181,"column":113}},"type":"cond-expr","locations":[{"start":{"line":181,"column":101},"end":{"line":181,"column":106}},{"start":{"line":181,"column":109},"end":{"line":181,"column":113}}]},"6":{"loc":{"start":{"line":210,"column":16},"end":{"line":211,"column":null}},"type":"if","locations":[{"start":{"line":210,"column":16},"end":{"line":211,"column":null}},{"start":{"line":211,"column":21},"end":{"line":211,"column":null}}]},"7":{"loc":{"start":{"line":227,"column":16},"end":{"line":229,"column":null}},"type":"if","locations":[{"start":{"line":227,"column":16},"end":{"line":229,"column":null}},{"start":{"line":228,"column":21},"end":{"line":229,"column":null}}]},"8":{"loc":{"start":{"line":228,"column":21},"end":{"line":229,"column":null}},"type":"if","locations":[{"start":{"line":228,"column":21},"end":{"line":229,"column":null}},{"start":{"line":229,"column":21},"end":{"line":229,"column":null}}]}},"s":{"0":4,"1":4,"2":4,"3":4,"4":4,"5":4,"6":6,"7":6,"8":5,"9":1,"10":1,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":3,"22":3,"23":3,"24":3,"25":0,"26":2,"27":2,"28":3,"29":3,"30":0,"31":3,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":1,"43":1,"44":1,"45":2,"46":1,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":4,"77":4},"f":{"0":6,"1":0,"2":3,"3":3,"4":0,"5":0,"6":0,"7":0,"8":1,"9":2,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"b":{"0":[6],"1":[0,3],"2":[2,0],"3":[1,0],"4":[0,2],"5":[1,1],"6":[0,0],"7":[0,0],"8":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/org/packageQuery/InstalledPackagesQueryExecutor.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/org/packageQuery/InstalledPackagesQueryExecutor.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":7,"column":12},"end":{"line":10,"column":42}},"2":{"start":{"line":12,"column":8},"end":{"line":12,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":5,"column":4},"end":{"line":5,"column":17}},"loc":{"start":{"line":5,"column":38},"end":{"line":13,"column":null}}}},"branchMap":{},"s":{"0":4,"1":0,"2":0,"3":4},"f":{"0":0},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/SfpPackage.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/SfpPackage.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":65,"column":8},"end":{"line":65,"column":null}},"2":{"start":{"line":69,"column":8},"end":{"line":69,"column":null}},"3":{"start":{"line":74,"column":8},"end":{"line":74,"column":null}},"4":{"start":{"line":78,"column":8},"end":{"line":78,"column":null}},"5":{"start":{"line":82,"column":8},"end":{"line":82,"column":null}},"6":{"start":{"line":90,"column":8},"end":{"line":90,"column":null}},"7":{"start":{"line":61,"column":11},"end":{"line":61,"column":null}},"8":{"start":{"line":94,"column":36},"end":{"line":94,"column":53}},"9":{"start":{"line":95,"column":8},"end":{"line":95,"column":null}},"10":{"start":{"line":96,"column":8},"end":{"line":96,"column":null}},"11":{"start":{"line":97,"column":8},"end":{"line":97,"column":null}},"12":{"start":{"line":98,"column":8},"end":{"line":98,"column":null}},"13":{"start":{"line":99,"column":8},"end":{"line":99,"column":null}},"14":{"start":{"line":100,"column":8},"end":{"line":100,"column":null}},"15":{"start":{"line":101,"column":8},"end":{"line":101,"column":null}},"16":{"start":{"line":102,"column":8},"end":{"line":102,"column":null}},"17":{"start":{"line":54,"column":0},"end":{"line":54,"column":21}},"18":{"start":{"line":107,"column":0},"end":{"line":107,"column":null}},"19":{"start":{"line":108,"column":4},"end":{"line":108,"column":null}},"20":{"start":{"line":109,"column":4},"end":{"line":109,"column":null}},"21":{"start":{"line":110,"column":4},"end":{"line":110,"column":null}},"22":{"start":{"line":111,"column":4},"end":{"line":111,"column":null}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":64,"column":4},"end":{"line":64,"column":15}},"loc":{"start":{"line":64,"column":26},"end":{"line":66,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":68,"column":4},"end":{"line":68,"column":15}},"loc":{"start":{"line":68,"column":28},"end":{"line":70,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":72,"column":4},"end":{"line":72,"column":15}},"loc":{"start":{"line":72,"column":49},"end":{"line":75,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":77,"column":4},"end":{"line":77,"column":15}},"loc":{"start":{"line":77,"column":26},"end":{"line":79,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":81,"column":4},"end":{"line":81,"column":15}},"loc":{"start":{"line":81,"column":46},"end":{"line":83,"column":null}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":89,"column":4},"end":{"line":89,"column":null}},"loc":{"start":{"line":89,"column":4},"end":{"line":91,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":93,"column":4},"end":{"line":93,"column":10}},"loc":{"start":{"line":93,"column":10},"end":{"line":103,"column":null}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":107,"column":0},"end":{"line":107,"column":12}},"loc":{"start":{"line":107,"column":23},"end":{"line":112,"column":1}}}},"branchMap":{"0":{"loc":{"start":{"line":107,"column":12},"end":{"line":107,"column":null}},"type":"binary-expr","locations":[{"start":{"line":107,"column":12},"end":{"line":107,"column":23}},{"start":{"line":107,"column":12},"end":{"line":107,"column":null}}]}},"s":{"0":13,"1":0,"2":0,"3":0,"4":8,"5":7,"6":12,"7":12,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":13,"18":13,"19":13,"20":13,"21":13,"22":13},"f":{"0":0,"1":0,"2":0,"3":8,"4":7,"5":12,"6":0,"7":13},"b":{"0":[13,13]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/SfpPackageBuilder.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/SfpPackageBuilder.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":null}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":null}},"9":{"start":{"line":11,"column":0},"end":{"line":11,"column":null}},"10":{"start":{"line":13,"column":0},"end":{"line":13,"column":null}},"11":{"start":{"line":14,"column":0},"end":{"line":14,"column":null}},"12":{"start":{"line":15,"column":0},"end":{"line":15,"column":null}},"13":{"start":{"line":16,"column":0},"end":{"line":16,"column":null}},"14":{"start":{"line":17,"column":0},"end":{"line":17,"column":null}},"15":{"start":{"line":18,"column":0},"end":{"line":18,"column":null}},"16":{"start":{"line":19,"column":0},"end":{"line":19,"column":null}},"17":{"start":{"line":21,"column":0},"end":{"line":21,"column":null}},"18":{"start":{"line":22,"column":0},"end":{"line":22,"column":null}},"19":{"start":{"line":23,"column":0},"end":{"line":23,"column":null}},"20":{"start":{"line":24,"column":0},"end":{"line":24,"column":null}},"21":{"start":{"line":25,"column":0},"end":{"line":25,"column":null}},"22":{"start":{"line":37,"column":12},"end":{"line":37,"column":null}},"23":{"start":{"line":40,"column":12},"end":{"line":40,"column":null}},"24":{"start":{"line":43,"column":50},"end":{"line":46,"column":null}},"25":{"start":{"line":49,"column":24},"end":{"line":49,"column":32}},"26":{"start":{"line":50,"column":37},"end":{"line":50,"column":53}},"27":{"start":{"line":51,"column":8},"end":{"line":51,"column":null}},"28":{"start":{"line":52,"column":8},"end":{"line":52,"column":null}},"29":{"start":{"line":53,"column":8},"end":{"line":53,"column":null}},"30":{"start":{"line":54,"column":8},"end":{"line":57,"column":null}},"31":{"start":{"line":58,"column":8},"end":{"line":58,"column":null}},"32":{"start":{"line":59,"column":8},"end":{"line":59,"column":null}},"33":{"start":{"line":61,"column":8},"end":{"line":61,"column":null}},"34":{"start":{"line":64,"column":8},"end":{"line":64,"column":null}},"35":{"start":{"line":65,"column":8},"end":{"line":65,"column":null}},"36":{"start":{"line":66,"column":8},"end":{"line":66,"column":null}},"37":{"start":{"line":67,"column":8},"end":{"line":68,"column":null}},"38":{"start":{"line":67,"column":44},"end":{"line":67,"column":106}},"39":{"start":{"line":68,"column":13},"end":{"line":68,"column":null}},"40":{"start":{"line":71,"column":12},"end":{"line":71,"column":null}},"41":{"start":{"line":75,"column":8},"end":{"line":75,"column":null}},"42":{"start":{"line":77,"column":8},"end":{"line":77,"column":null}},"43":{"start":{"line":80,"column":8},"end":{"line":90,"column":null}},"44":{"start":{"line":92,"column":8},"end":{"line":92,"column":null}},"45":{"start":{"line":96,"column":41},"end":{"line":100,"column":null}},"46":{"start":{"line":102,"column":12},"end":{"line":102,"column":null}},"47":{"start":{"line":103,"column":53},"end":{"line":103,"column":102}},"48":{"start":{"line":105,"column":12},"end":{"line":105,"column":null}},"49":{"start":{"line":106,"column":12},"end":{"line":106,"column":null}},"50":{"start":{"line":107,"column":12},"end":{"line":107,"column":null}},"51":{"start":{"line":108,"column":12},"end":{"line":108,"column":null}},"52":{"start":{"line":109,"column":12},"end":{"line":109,"column":null}},"53":{"start":{"line":110,"column":12},"end":{"line":110,"column":null}},"54":{"start":{"line":112,"column":47},"end":{"line":112,"column":87}},"55":{"start":{"line":113,"column":12},"end":{"line":113,"column":null}},"56":{"start":{"line":114,"column":12},"end":{"line":114,"column":null}},"57":{"start":{"line":115,"column":12},"end":{"line":118,"column":null}},"58":{"start":{"line":119,"column":12},"end":{"line":119,"column":null}},"59":{"start":{"line":121,"column":12},"end":{"line":121,"column":null}},"60":{"start":{"line":124,"column":31},"end":{"line":125,"column":null}},"61":{"start":{"line":129,"column":28},"end":{"line":129,"column":59}},"62":{"start":{"line":131,"column":16},"end":{"line":131,"column":null}},"63":{"start":{"line":131,"column":60},"end":{"line":131,"column":null}},"64":{"start":{"line":138,"column":8},"end":{"line":138,"column":null}},"65":{"start":{"line":138,"column":36},"end":{"line":138,"column":null}},"66":{"start":{"line":140,"column":26},"end":{"line":140,"column":49}},"67":{"start":{"line":141,"column":8},"end":{"line":141,"column":null}},"68":{"start":{"line":141,"column":45},"end":{"line":141,"column":null}},"69":{"start":{"line":146,"column":16},"end":{"line":152,"column":null}},"70":{"start":{"line":153,"column":16},"end":{"line":153,"column":null}},"71":{"start":{"line":155,"column":16},"end":{"line":161,"column":null}},"72":{"start":{"line":162,"column":16},"end":{"line":162,"column":null}},"73":{"start":{"line":164,"column":16},"end":{"line":170,"column":null}},"74":{"start":{"line":171,"column":16},"end":{"line":171,"column":null}},"75":{"start":{"line":173,"column":16},"end":{"line":173,"column":null}},"76":{"start":{"line":174,"column":16},"end":{"line":174,"column":null}},"77":{"start":{"line":175,"column":16},"end":{"line":181,"column":null}},"78":{"start":{"line":182,"column":16},"end":{"line":182,"column":null}},"79":{"start":{"line":185,"column":8},"end":{"line":185,"column":null}},"80":{"start":{"line":199,"column":12},"end":{"line":199,"column":null}},"81":{"start":{"line":202,"column":60},"end":{"line":202,"column":87}},"82":{"start":{"line":203,"column":16},"end":{"line":206,"column":null}},"83":{"start":{"line":209,"column":12},"end":{"line":209,"column":null}},"84":{"start":{"line":211,"column":8},"end":{"line":211,"column":null}},"85":{"start":{"line":216,"column":25},"end":{"line":216,"column":41}},"86":{"start":{"line":217,"column":8},"end":{"line":217,"column":null}},"87":{"start":{"line":218,"column":8},"end":{"line":218,"column":null}},"88":{"start":{"line":219,"column":8},"end":{"line":219,"column":null}},"89":{"start":{"line":221,"column":8},"end":{"line":221,"column":null}},"90":{"start":{"line":222,"column":8},"end":{"line":225,"column":null}},"91":{"start":{"line":226,"column":8},"end":{"line":226,"column":null}},"92":{"start":{"line":227,"column":8},"end":{"line":227,"column":null}},"93":{"start":{"line":228,"column":8},"end":{"line":228,"column":null}},"94":{"start":{"line":230,"column":8},"end":{"line":230,"column":null}},"95":{"start":{"line":236,"column":8},"end":{"line":253,"column":null}},"96":{"start":{"line":242,"column":12},"end":{"line":242,"column":null}},"97":{"start":{"line":243,"column":12},"end":{"line":250,"column":null}},"98":{"start":{"line":251,"column":12},"end":{"line":251,"column":null}},"99":{"start":{"line":252,"column":12},"end":{"line":252,"column":null}},"100":{"start":{"line":253,"column":15},"end":{"line":253,"column":null}},"101":{"start":{"line":258,"column":8},"end":{"line":259,"column":null}},"102":{"start":{"line":258,"column":60},"end":{"line":258,"column":72}},"103":{"start":{"line":259,"column":13},"end":{"line":259,"column":null}},"104":{"start":{"line":27,"column":0},"end":{"line":27,"column":21}},"105":{"start":{"line":265,"column":4},"end":{"line":265,"column":null}},"106":{"start":{"line":264,"column":0},"end":{"line":264,"column":13}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":28,"column":11},"end":{"line":28,"column":24}},"loc":{"start":{"line":34,"column":27},"end":{"line":186,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":193,"column":12},"end":{"line":193,"column":19}},"loc":{"start":{"line":196,"column":52},"end":{"line":212,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":214,"column":11},"end":{"line":214,"column":24}},"loc":{"start":{"line":214,"column":83},"end":{"line":231,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":235,"column":12},"end":{"line":235,"column":19}},"loc":{"start":{"line":235,"column":81},"end":{"line":254,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":257,"column":12},"end":{"line":257,"column":19}},"loc":{"start":{"line":257,"column":75},"end":{"line":260,"column":null}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":264,"column":0},"end":{"line":264,"column":13}},"loc":{"start":{"line":264,"column":0},"end":{"line":278,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":58,"column":38},"end":{"line":58,"column":74}},"type":"cond-expr","locations":[{"start":{"line":58,"column":55},"end":{"line":58,"column":71}},{"start":{"line":58,"column":72},"end":{"line":58,"column":74}}]},"1":{"loc":{"start":{"line":67,"column":8},"end":{"line":68,"column":null}},"type":"if","locations":[{"start":{"line":67,"column":8},"end":{"line":68,"column":null}},{"start":{"line":68,"column":13},"end":{"line":68,"column":null}}]},"2":{"loc":{"start":{"line":131,"column":16},"end":{"line":131,"column":null}},"type":"if","locations":[{"start":{"line":131,"column":16},"end":{"line":131,"column":null}}]},"3":{"loc":{"start":{"line":138,"column":8},"end":{"line":138,"column":null}},"type":"if","locations":[{"start":{"line":138,"column":8},"end":{"line":138,"column":null}}]},"4":{"loc":{"start":{"line":141,"column":8},"end":{"line":141,"column":null}},"type":"if","locations":[{"start":{"line":141,"column":8},"end":{"line":141,"column":null}}]},"5":{"loc":{"start":{"line":145,"column":12},"end":{"line":153,"column":null}},"type":"switch","locations":[{"start":{"line":145,"column":12},"end":{"line":153,"column":null}},{"start":{"line":154,"column":12},"end":{"line":162,"column":null}},{"start":{"line":163,"column":12},"end":{"line":171,"column":null}},{"start":{"line":172,"column":12},"end":{"line":182,"column":null}}]},"6":{"loc":{"start":{"line":236,"column":8},"end":{"line":253,"column":null}},"type":"if","locations":[{"start":{"line":236,"column":8},"end":{"line":253,"column":null}},{"start":{"line":253,"column":15},"end":{"line":253,"column":null}}]},"7":{"loc":{"start":{"line":237,"column":12},"end":{"line":240,"column":52}},"type":"binary-expr","locations":[{"start":{"line":237,"column":12},"end":{"line":237,"column":75}},{"start":{"line":238,"column":13},"end":{"line":238,"column":57}},{"start":{"line":239,"column":16},"end":{"line":239,"column":46}},{"start":{"line":240,"column":16},"end":{"line":240,"column":52}}]},"8":{"loc":{"start":{"line":258,"column":8},"end":{"line":259,"column":null}},"type":"if","locations":[{"start":{"line":258,"column":8},"end":{"line":259,"column":null}},{"start":{"line":259,"column":13},"end":{"line":259,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":1,"105":0,"106":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0},"b":{"0":[0,0],"1":[0,0],"2":[0],"3":[0],"4":[0],"5":[0,0,0,0],"6":[0,0],"7":[0,0,0,0],"8":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/analyser/AnalyzerRegistry.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/analyser/AnalyzerRegistry.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":8,"column":50},"end":{"line":8,"column":52}},"4":{"start":{"line":11,"column":26},"end":{"line":11,"column":43}},"5":{"start":{"line":12,"column":25},"end":{"line":12,"column":41}},"6":{"start":{"line":13,"column":31},"end":{"line":13,"column":53}},"7":{"start":{"line":14,"column":8},"end":{"line":14,"column":null}},"8":{"start":{"line":15,"column":8},"end":{"line":15,"column":null}},"9":{"start":{"line":16,"column":8},"end":{"line":16,"column":null}},"10":{"start":{"line":18,"column":8},"end":{"line":18,"column":null}},"11":{"start":{"line":6,"column":0},"end":{"line":6,"column":13}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":7,"column":4},"end":{"line":7,"column":11}},"loc":{"start":{"line":7,"column":23},"end":{"line":19,"column":null}}}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":1},"f":{"0":0},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/analyser/FHTAnalyzer.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/analyser/FHTAnalyzer.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"5":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"6":{"start":{"line":12,"column":7},"end":{"line":12,"column":null}},"7":{"start":{"line":20,"column":62},"end":{"line":20,"column":64}},"8":{"start":{"line":23,"column":30},"end":{"line":27,"column":null}},"9":{"start":{"line":33,"column":16},"end":{"line":33,"column":null}},"10":{"start":{"line":38,"column":12},"end":{"line":38,"column":null}},"11":{"start":{"line":41,"column":16},"end":{"line":41,"column":null}},"12":{"start":{"line":42,"column":16},"end":{"line":42,"column":null}},"13":{"start":{"line":46,"column":12},"end":{"line":46,"column":null}},"14":{"start":{"line":48,"column":8},"end":{"line":48,"column":null}},"15":{"start":{"line":55,"column":31},"end":{"line":55,"column":75}},"16":{"start":{"line":59,"column":16},"end":{"line":59,"column":null}},"17":{"start":{"line":62,"column":30},"end":{"line":62,"column":72}},"18":{"start":{"line":64,"column":30},"end":{"line":64,"column":61}},"19":{"start":{"line":65,"column":16},"end":{"line":65,"column":null}},"20":{"start":{"line":65,"column":41},"end":{"line":65,"column":null}},"21":{"start":{"line":66,"column":16},"end":{"line":66,"column":null}},"22":{"start":{"line":69,"column":8},"end":{"line":69,"column":null}},"23":{"start":{"line":73,"column":8},"end":{"line":74,"column":null}},"24":{"start":{"line":73,"column":56},"end":{"line":73,"column":68}},"25":{"start":{"line":74,"column":13},"end":{"line":74,"column":null}},"26":{"start":{"line":9,"column":0},"end":{"line":9,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":11,"column":10},"end":{"line":11,"column":17}},"loc":{"start":{"line":11,"column":17},"end":{"line":13,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":17,"column":11},"end":{"line":17,"column":17}},"loc":{"start":{"line":17,"column":89},"end":{"line":49,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":51,"column":12},"end":{"line":51,"column":18}},"loc":{"start":{"line":53,"column":34},"end":{"line":70,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":72,"column":11},"end":{"line":72,"column":17}},"loc":{"start":{"line":72,"column":63},"end":{"line":75,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":65,"column":16},"end":{"line":65,"column":null}},"type":"if","locations":[{"start":{"line":65,"column":16},"end":{"line":65,"column":null}}]},"1":{"loc":{"start":{"line":73,"column":8},"end":{"line":74,"column":null}},"type":"if","locations":[{"start":{"line":73,"column":8},"end":{"line":74,"column":null}},{"start":{"line":74,"column":13},"end":{"line":74,"column":null}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":2,"6":0,"7":4,"8":4,"9":3,"10":4,"11":4,"12":4,"13":0,"14":4,"15":4,"16":0,"17":3,"18":2,"19":2,"20":2,"21":2,"22":4,"23":3,"24":2,"25":1,"26":2},"f":{"0":0,"1":4,"2":4,"3":3},"b":{"0":[2],"1":[2,1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/analyser/FTAnalyzer.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/analyser/FTAnalyzer.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"5":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"6":{"start":{"line":12,"column":8},"end":{"line":12,"column":null}},"7":{"start":{"line":18,"column":61},"end":{"line":18,"column":63}},"8":{"start":{"line":21,"column":29},"end":{"line":25,"column":null}},"9":{"start":{"line":31,"column":16},"end":{"line":31,"column":null}},"10":{"start":{"line":36,"column":12},"end":{"line":36,"column":null}},"11":{"start":{"line":39,"column":16},"end":{"line":39,"column":null}},"12":{"start":{"line":40,"column":16},"end":{"line":40,"column":null}},"13":{"start":{"line":44,"column":12},"end":{"line":44,"column":null}},"14":{"start":{"line":46,"column":8},"end":{"line":46,"column":null}},"15":{"start":{"line":53,"column":31},"end":{"line":53,"column":75}},"16":{"start":{"line":57,"column":16},"end":{"line":57,"column":null}},"17":{"start":{"line":60,"column":30},"end":{"line":60,"column":72}},"18":{"start":{"line":62,"column":30},"end":{"line":62,"column":61}},"19":{"start":{"line":63,"column":16},"end":{"line":63,"column":null}},"20":{"start":{"line":63,"column":40},"end":{"line":63,"column":null}},"21":{"start":{"line":64,"column":16},"end":{"line":64,"column":null}},"22":{"start":{"line":67,"column":8},"end":{"line":67,"column":null}},"23":{"start":{"line":71,"column":8},"end":{"line":72,"column":null}},"24":{"start":{"line":71,"column":56},"end":{"line":71,"column":68}},"25":{"start":{"line":72,"column":13},"end":{"line":72,"column":null}},"26":{"start":{"line":9,"column":0},"end":{"line":9,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":11,"column":11},"end":{"line":11,"column":18}},"loc":{"start":{"line":11,"column":18},"end":{"line":13,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":15,"column":11},"end":{"line":15,"column":17}},"loc":{"start":{"line":15,"column":89},"end":{"line":47,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":49,"column":12},"end":{"line":49,"column":18}},"loc":{"start":{"line":51,"column":34},"end":{"line":68,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":70,"column":11},"end":{"line":70,"column":17}},"loc":{"start":{"line":70,"column":63},"end":{"line":73,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":63,"column":16},"end":{"line":63,"column":null}},"type":"if","locations":[{"start":{"line":63,"column":16},"end":{"line":63,"column":null}}]},"1":{"loc":{"start":{"line":71,"column":8},"end":{"line":72,"column":null}},"type":"if","locations":[{"start":{"line":71,"column":8},"end":{"line":72,"column":null}},{"start":{"line":72,"column":13},"end":{"line":72,"column":null}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":2,"6":0,"7":4,"8":4,"9":3,"10":4,"11":4,"12":4,"13":0,"14":4,"15":4,"16":0,"17":3,"18":2,"19":2,"20":2,"21":2,"22":4,"23":3,"24":2,"25":1,"26":2},"f":{"0":0,"1":4,"2":4,"3":3},"b":{"0":[2],"1":[2,1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/analyser/PicklistAnalyzer.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/analyser/PicklistAnalyzer.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":9,"column":8},"end":{"line":9,"column":null}},"4":{"start":{"line":16,"column":35},"end":{"line":16,"column":79}},"5":{"start":{"line":17,"column":29},"end":{"line":17,"column":31}},"6":{"start":{"line":23,"column":20},"end":{"line":23,"column":null}},"7":{"start":{"line":27,"column":20},"end":{"line":27,"column":null}},"8":{"start":{"line":33,"column":38},"end":{"line":33,"column":79}},"9":{"start":{"line":37,"column":24},"end":{"line":37,"column":null}},"10":{"start":{"line":38,"column":24},"end":{"line":38,"column":null}},"11":{"start":{"line":43,"column":12},"end":{"line":43,"column":null}},"12":{"start":{"line":45,"column":8},"end":{"line":45,"column":null}},"13":{"start":{"line":49,"column":8},"end":{"line":50,"column":null}},"14":{"start":{"line":49,"column":56},"end":{"line":49,"column":68}},"15":{"start":{"line":50,"column":13},"end":{"line":50,"column":null}},"16":{"start":{"line":6,"column":0},"end":{"line":6,"column":21}}},"fnMap":{"0":{"name":"(anonymous_6)","decl":{"start":{"line":8,"column":11},"end":{"line":8,"column":18}},"loc":{"start":{"line":8,"column":18},"end":{"line":10,"column":null}}},"1":{"name":"(anonymous_7)","decl":{"start":{"line":14,"column":11},"end":{"line":14,"column":17}},"loc":{"start":{"line":14,"column":89},"end":{"line":46,"column":null}}},"2":{"name":"(anonymous_8)","decl":{"start":{"line":48,"column":11},"end":{"line":48,"column":17}},"loc":{"start":{"line":48,"column":63},"end":{"line":51,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":36,"column":24},"end":{"line":36,"column":72}},"type":"binary-expr","locations":[{"start":{"line":36,"column":24},"end":{"line":36,"column":35}},{"start":{"line":36,"column":39},"end":{"line":36,"column":72}}]},"1":{"loc":{"start":{"line":49,"column":8},"end":{"line":50,"column":null}},"type":"if","locations":[{"start":{"line":49,"column":8},"end":{"line":50,"column":null}},{"start":{"line":50,"column":13},"end":{"line":50,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":1},"f":{"0":0,"1":0,"2":0},"b":{"0":[0,0],"1":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/components/PackageManifest.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/components/PackageManifest.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":15},"end":{"line":5,"column":32}},"4":{"start":{"line":15,"column":8},"end":{"line":15,"column":null}},"5":{"start":{"line":22,"column":8},"end":{"line":22,"column":null}},"6":{"start":{"line":33,"column":32},"end":{"line":33,"column":53}},"7":{"start":{"line":35,"column":35},"end":{"line":35,"column":94}},"8":{"start":{"line":37,"column":8},"end":{"line":37,"column":null}},"9":{"start":{"line":38,"column":8},"end":{"line":38,"column":null}},"10":{"start":{"line":40,"column":8},"end":{"line":40,"column":null}},"11":{"start":{"line":50,"column":32},"end":{"line":50,"column":53}},"12":{"start":{"line":52,"column":28},"end":{"line":56,"column":null}},"13":{"start":{"line":58,"column":8},"end":{"line":71,"column":null}},"14":{"start":{"line":59,"column":25},"end":{"line":59,"column":87}},"15":{"start":{"line":59,"column":58},"end":{"line":59,"column":86}},"16":{"start":{"line":62,"column":16},"end":{"line":62,"column":null}},"17":{"start":{"line":65,"column":32},"end":{"line":68,"column":null}},"18":{"start":{"line":69,"column":16},"end":{"line":69,"column":null}},"19":{"start":{"line":73,"column":24},"end":{"line":75,"column":10}},"20":{"start":{"line":77,"column":25},"end":{"line":79,"column":null}},"21":{"start":{"line":81,"column":8},"end":{"line":81,"column":null}},"22":{"start":{"line":82,"column":8},"end":{"line":82,"column":null}},"23":{"start":{"line":84,"column":8},"end":{"line":84,"column":null}},"24":{"start":{"line":93,"column":32},"end":{"line":93,"column":53}},"25":{"start":{"line":94,"column":8},"end":{"line":94,"column":null}},"26":{"start":{"line":96,"column":24},"end":{"line":98,"column":10}},"27":{"start":{"line":100,"column":8},"end":{"line":100,"column":null}},"28":{"start":{"line":102,"column":8},"end":{"line":102,"column":null}},"29":{"start":{"line":110,"column":30},"end":{"line":110,"column":35}},"30":{"start":{"line":116,"column":24},"end":{"line":116,"column":null}},"31":{"start":{"line":117,"column":24},"end":{"line":117,"column":null}},"32":{"start":{"line":121,"column":16},"end":{"line":121,"column":null}},"33":{"start":{"line":125,"column":8},"end":{"line":125,"column":null}},"34":{"start":{"line":133,"column":35},"end":{"line":133,"column":40}},"35":{"start":{"line":139,"column":24},"end":{"line":139,"column":null}},"36":{"start":{"line":140,"column":24},"end":{"line":140,"column":null}},"37":{"start":{"line":144,"column":16},"end":{"line":144,"column":null}},"38":{"start":{"line":148,"column":8},"end":{"line":148,"column":null}},"39":{"start":{"line":152,"column":40},"end":{"line":152,"column":45}},"40":{"start":{"line":156,"column":20},"end":{"line":156,"column":null}},"41":{"start":{"line":157,"column":20},"end":{"line":157,"column":null}},"42":{"start":{"line":161,"column":12},"end":{"line":161,"column":null}},"43":{"start":{"line":163,"column":8},"end":{"line":163,"column":null}},"44":{"start":{"line":171,"column":26},"end":{"line":171,"column":31}},"45":{"start":{"line":177,"column":24},"end":{"line":177,"column":null}},"46":{"start":{"line":178,"column":24},"end":{"line":178,"column":null}},"47":{"start":{"line":185,"column":16},"end":{"line":185,"column":null}},"48":{"start":{"line":189,"column":8},"end":{"line":189,"column":null}},"49":{"start":{"line":202,"column":16},"end":{"line":202,"column":null}},"50":{"start":{"line":205,"column":16},"end":{"line":205,"column":null}},"51":{"start":{"line":213,"column":24},"end":{"line":213,"column":null}},"52":{"start":{"line":216,"column":24},"end":{"line":216,"column":null}},"53":{"start":{"line":218,"column":20},"end":{"line":218,"column":null}},"54":{"start":{"line":223,"column":8},"end":{"line":223,"column":null}},"55":{"start":{"line":227,"column":27},"end":{"line":227,"column":32}},"56":{"start":{"line":232,"column":24},"end":{"line":232,"column":null}},"57":{"start":{"line":233,"column":24},"end":{"line":233,"column":null}},"58":{"start":{"line":237,"column":16},"end":{"line":237,"column":null}},"59":{"start":{"line":240,"column":8},"end":{"line":240,"column":null}},"60":{"start":{"line":244,"column":46},"end":{"line":253,"column":null}},"61":{"start":{"line":256,"column":43},"end":{"line":256,"column":48}},"62":{"start":{"line":261,"column":24},"end":{"line":261,"column":null}},"63":{"start":{"line":262,"column":24},"end":{"line":262,"column":null}},"64":{"start":{"line":266,"column":16},"end":{"line":266,"column":null}},"65":{"start":{"line":269,"column":8},"end":{"line":269,"column":null}},"66":{"start":{"line":7,"column":0},"end":{"line":7,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":14,"column":4},"end":{"line":14,"column":8}},"loc":{"start":{"line":14,"column":20},"end":{"line":16,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":21,"column":4},"end":{"line":21,"column":8}},"loc":{"start":{"line":21,"column":19},"end":{"line":23,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":25,"column":4},"end":{"line":25,"column":27}},"loc":{"start":{"line":25,"column":4},"end":{"line":25,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":32,"column":4},"end":{"line":32,"column":17}},"loc":{"start":{"line":32,"column":40},"end":{"line":41,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":49,"column":4},"end":{"line":49,"column":11}},"loc":{"start":{"line":49,"column":97},"end":{"line":85,"column":null}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":58,"column":28},"end":{"line":58,"column":37}},"loc":{"start":{"line":58,"column":41},"end":{"line":71,"column":9}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":59,"column":49},"end":{"line":59,"column":53}},"loc":{"start":{"line":59,"column":58},"end":{"line":59,"column":86}}},"7":{"name":"(anonymous_14)","decl":{"start":{"line":92,"column":4},"end":{"line":92,"column":17}},"loc":{"start":{"line":92,"column":53},"end":{"line":103,"column":null}}},"8":{"name":"(anonymous_15)","decl":{"start":{"line":109,"column":11},"end":{"line":109,"column":30}},"loc":{"start":{"line":109,"column":30},"end":{"line":126,"column":null}}},"9":{"name":"(anonymous_16)","decl":{"start":{"line":132,"column":12},"end":{"line":132,"column":37}},"loc":{"start":{"line":132,"column":37},"end":{"line":149,"column":null}}},"10":{"name":"(anonymous_17)","decl":{"start":{"line":151,"column":11},"end":{"line":151,"column":46}},"loc":{"start":{"line":151,"column":46},"end":{"line":164,"column":null}}},"11":{"name":"(anonymous_18)","decl":{"start":{"line":170,"column":11},"end":{"line":170,"column":26}},"loc":{"start":{"line":170,"column":26},"end":{"line":190,"column":null}}},"12":{"name":"(anonymous_19)","decl":{"start":{"line":196,"column":11},"end":{"line":196,"column":24}},"loc":{"start":{"line":196,"column":24},"end":{"line":224,"column":null}}},"13":{"name":"(anonymous_20)","decl":{"start":{"line":226,"column":11},"end":{"line":226,"column":41}},"loc":{"start":{"line":226,"column":62},"end":{"line":241,"column":null}}},"14":{"name":"(anonymous_21)","decl":{"start":{"line":243,"column":11},"end":{"line":243,"column":51}},"loc":{"start":{"line":243,"column":51},"end":{"line":270,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":176,"column":24},"end":{"line":176,"column":80}},"type":"binary-expr","locations":[{"start":{"line":176,"column":24},"end":{"line":176,"column":49}},{"start":{"line":176,"column":53},"end":{"line":176,"column":80}}]},"1":{"loc":{"start":{"line":182,"column":16},"end":{"line":183,"column":71}},"type":"binary-expr","locations":[{"start":{"line":182,"column":16},"end":{"line":182,"column":69}},{"start":{"line":183,"column":16},"end":{"line":183,"column":71}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":3,"5":3,"6":6,"7":6,"8":6,"9":6,"10":6,"11":6,"12":6,"13":6,"14":48,"15":150,"16":6,"17":42,"18":42,"19":6,"20":6,"21":6,"22":6,"23":6,"24":1,"25":1,"26":1,"27":1,"28":1,"29":2,"30":1,"31":1,"32":0,"33":2,"34":0,"35":0,"36":0,"37":0,"38":0,"39":2,"40":1,"41":1,"42":0,"43":2,"44":1,"45":1,"46":1,"47":0,"48":1,"49":2,"50":0,"51":1,"52":0,"53":1,"54":2,"55":1,"56":1,"57":1,"58":0,"59":1,"60":1,"61":1,"62":1,"63":1,"64":0,"65":1,"66":2},"f":{"0":3,"1":3,"2":13,"3":6,"4":6,"5":48,"6":150,"7":1,"8":2,"9":0,"10":2,"11":1,"12":2,"13":1,"14":1},"b":{"0":[2,1],"1":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/components/PackageToComponent.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/components/PackageToComponent.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":6,"column":31},"end":{"line":6,"column":49}},"2":{"start":{"line":6,"column":58},"end":{"line":6,"column":81}},"3":{"start":{"line":9,"column":40},"end":{"line":9,"column":42}},"4":{"start":{"line":11,"column":27},"end":{"line":11,"column":73}},"5":{"start":{"line":13,"column":32},"end":{"line":13,"column":76}},"6":{"start":{"line":16,"column":41},"end":{"line":22,"column":null}},"7":{"start":{"line":23,"column":12},"end":{"line":23,"column":null}},"8":{"start":{"line":26,"column":7},"end":{"line":26,"column":null}},"9":{"start":{"line":5,"column":0},"end":{"line":5,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":4},"end":{"line":6,"column":31}},"loc":{"start":{"line":6,"column":81},"end":{"line":6,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":8,"column":11},"end":{"line":8,"column":29}},"loc":{"start":{"line":8,"column":29},"end":{"line":27,"column":null}}}},"branchMap":{},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":1},"f":{"0":0,"1":0},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/coverage/PackageTestCoverage.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/coverage/PackageTestCoverage.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"6":{"start":{"line":14,"column":16},"end":{"line":14,"column":31}},"7":{"start":{"line":15,"column":16},"end":{"line":15,"column":33}},"8":{"start":{"line":16,"column":16},"end":{"line":16,"column":30}},"9":{"start":{"line":17,"column":25},"end":{"line":17,"column":41}},"10":{"start":{"line":11,"column":12},"end":{"line":11,"column":45}},"11":{"start":{"line":19,"column":8},"end":{"line":19,"column":null}},"12":{"start":{"line":23,"column":39},"end":{"line":23,"column":75}},"13":{"start":{"line":24,"column":33},"end":{"line":24,"column":50}},"14":{"start":{"line":26,"column":35},"end":{"line":29,"column":null}},"15":{"start":{"line":32,"column":33},"end":{"line":32,"column":34}},"16":{"start":{"line":33,"column":35},"end":{"line":33,"column":36}},"17":{"start":{"line":36,"column":16},"end":{"line":36,"column":null}},"18":{"start":{"line":37,"column":16},"end":{"line":37,"column":null}},"19":{"start":{"line":41,"column":51},"end":{"line":41,"column":53}},"20":{"start":{"line":43,"column":43},"end":{"line":43,"column":114}},"21":{"start":{"line":45,"column":31},"end":{"line":47,"column":46}},"22":{"start":{"line":47,"column":33},"end":{"line":47,"column":45}},"23":{"start":{"line":48,"column":12},"end":{"line":48,"column":null}},"24":{"start":{"line":51,"column":44},"end":{"line":51,"column":110}},"25":{"start":{"line":53,"column":29},"end":{"line":55,"column":42}},"26":{"start":{"line":55,"column":31},"end":{"line":55,"column":41}},"27":{"start":{"line":56,"column":12},"end":{"line":56,"column":null}},"28":{"start":{"line":60,"column":53},"end":{"line":62,"column":55}},"29":{"start":{"line":65,"column":48},"end":{"line":65,"column":49}},"30":{"start":{"line":66,"column":16},"end":{"line":68,"column":null}},"31":{"start":{"line":67,"column":20},"end":{"line":67,"column":null}},"32":{"start":{"line":69,"column":16},"end":{"line":69,"column":null}},"33":{"start":{"line":73,"column":27},"end":{"line":73,"column":72}},"34":{"start":{"line":74,"column":8},"end":{"line":74,"column":null}},"35":{"start":{"line":75,"column":8},"end":{"line":75,"column":null}},"36":{"start":{"line":87,"column":8},"end":{"line":89,"column":null}},"37":{"start":{"line":89,"column":12},"end":{"line":89,"column":null}},"38":{"start":{"line":91,"column":29},"end":{"line":91,"column":88}},"39":{"start":{"line":94,"column":12},"end":{"line":94,"column":null}},"40":{"start":{"line":95,"column":12},"end":{"line":95,"column":null}},"41":{"start":{"line":103,"column":16},"end":{"line":110,"column":null}},"42":{"start":{"line":112,"column":16},"end":{"line":117,"column":null}},"43":{"start":{"line":120,"column":12},"end":{"line":120,"column":null}},"44":{"start":{"line":122,"column":51},"end":{"line":124,"column":null}},"45":{"start":{"line":128,"column":16},"end":{"line":134,"column":null}},"46":{"start":{"line":136,"column":16},"end":{"line":142,"column":null}},"47":{"start":{"line":145,"column":12},"end":{"line":145,"column":null}},"48":{"start":{"line":153,"column":14},"end":{"line":153,"column":16}},"49":{"start":{"line":155,"column":39},"end":{"line":155,"column":75}},"50":{"start":{"line":156,"column":33},"end":{"line":156,"column":50}},"51":{"start":{"line":158,"column":8},"end":{"line":162,"column":null}},"52":{"start":{"line":166,"column":16},"end":{"line":169,"column":null}},"53":{"start":{"line":173,"column":50},"end":{"line":175,"column":null}},"54":{"start":{"line":182,"column":18},"end":{"line":184,"column":14}},"55":{"start":{"line":183,"column":16},"end":{"line":183,"column":null}},"56":{"start":{"line":185,"column":12},"end":{"line":185,"column":null}},"57":{"start":{"line":189,"column":51},"end":{"line":189,"column":118}},"58":{"start":{"line":195,"column":18},"end":{"line":197,"column":14}},"59":{"start":{"line":196,"column":16},"end":{"line":196,"column":null}},"60":{"start":{"line":198,"column":12},"end":{"line":198,"column":null}},"61":{"start":{"line":201,"column":8},"end":{"line":201,"column":null}},"62":{"start":{"line":212,"column":8},"end":{"line":222,"column":null}},"63":{"start":{"line":213,"column":12},"end":{"line":221,"column":null}},"64":{"start":{"line":217,"column":24},"end":{"line":217,"column":null}},"65":{"start":{"line":220,"column":16},"end":{"line":220,"column":null}},"66":{"start":{"line":222,"column":15},"end":{"line":222,"column":null}},"67":{"start":{"line":233,"column":8},"end":{"line":243,"column":null}},"68":{"start":{"line":234,"column":12},"end":{"line":242,"column":null}},"69":{"start":{"line":238,"column":24},"end":{"line":238,"column":null}},"70":{"start":{"line":241,"column":16},"end":{"line":241,"column":null}},"71":{"start":{"line":243,"column":15},"end":{"line":243,"column":null}},"72":{"start":{"line":253,"column":35},"end":{"line":269,"column":10}},"73":{"start":{"line":256,"column":20},"end":{"line":256,"column":null}},"74":{"start":{"line":256,"column":64},"end":{"line":256,"column":null}},"75":{"start":{"line":263,"column":24},"end":{"line":263,"column":null}},"76":{"start":{"line":268,"column":12},"end":{"line":268,"column":null}},"77":{"start":{"line":271,"column":8},"end":{"line":271,"column":null}},"78":{"start":{"line":9,"column":0},"end":{"line":9,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":13,"column":4},"end":{"line":13,"column":null}},"loc":{"start":{"line":17,"column":41},"end":{"line":20,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":22,"column":11},"end":{"line":22,"column":17}},"loc":{"start":{"line":22,"column":46},"end":{"line":76,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":47,"column":19},"end":{"line":47,"column":28}},"loc":{"start":{"line":47,"column":33},"end":{"line":47,"column":45}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":55,"column":19},"end":{"line":55,"column":26}},"loc":{"start":{"line":55,"column":31},"end":{"line":55,"column":41}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":66,"column":60},"end":{"line":66,"column":66}},"loc":{"start":{"line":66,"column":70},"end":{"line":68,"column":17}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":78,"column":11},"end":{"line":78,"column":17}},"loc":{"start":{"line":79,"column":34},"end":{"line":147,"column":null}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":149,"column":12},"end":{"line":149,"column":47}},"loc":{"start":{"line":149,"column":71},"end":{"line":202,"column":null}}},"7":{"name":"(anonymous_14)","decl":{"start":{"line":182,"column":49},"end":{"line":182,"column":58}},"loc":{"start":{"line":182,"column":62},"end":{"line":184,"column":13}}},"8":{"name":"(anonymous_15)","decl":{"start":{"line":195,"column":50},"end":{"line":195,"column":61}},"loc":{"start":{"line":195,"column":65},"end":{"line":197,"column":13}}},"9":{"name":"(anonymous_16)","decl":{"start":{"line":211,"column":12},"end":{"line":211,"column":44}},"loc":{"start":{"line":211,"column":88},"end":{"line":223,"column":null}}},"10":{"name":"(anonymous_17)","decl":{"start":{"line":213,"column":36},"end":{"line":213,"column":43}},"loc":{"start":{"line":213,"column":47},"end":{"line":221,"column":13}}},"11":{"name":"(anonymous_18)","decl":{"start":{"line":232,"column":12},"end":{"line":232,"column":43}},"loc":{"start":{"line":232,"column":93},"end":{"line":244,"column":null}}},"12":{"name":"(anonymous_19)","decl":{"start":{"line":234,"column":42},"end":{"line":234,"column":54}},"loc":{"start":{"line":234,"column":58},"end":{"line":242,"column":13}}},"13":{"name":"(anonymous_20)","decl":{"start":{"line":252,"column":12},"end":{"line":252,"column":57}},"loc":{"start":{"line":252,"column":116},"end":{"line":272,"column":null}}},"14":{"name":"(anonymous_21)","decl":{"start":{"line":253,"column":56},"end":{"line":253,"column":69}},"loc":{"start":{"line":253,"column":73},"end":{"line":269,"column":9}}}},"branchMap":{"0":{"loc":{"start":{"line":87,"column":8},"end":{"line":89,"column":null}},"type":"if","locations":[{"start":{"line":87,"column":8},"end":{"line":89,"column":null}}]},"1":{"loc":{"start":{"line":93,"column":12},"end":{"line":93,"column":68}},"type":"binary-expr","locations":[{"start":{"line":93,"column":12},"end":{"line":93,"column":42}},{"start":{"line":93,"column":46},"end":{"line":93,"column":68}}]},"2":{"loc":{"start":{"line":119,"column":19},"end":{"line":119,"column":107}},"type":"binary-expr","locations":[{"start":{"line":119,"column":19},"end":{"line":119,"column":62}},{"start":{"line":119,"column":66},"end":{"line":119,"column":107}}]},"3":{"loc":{"start":{"line":212,"column":8},"end":{"line":222,"column":null}},"type":"if","locations":[{"start":{"line":212,"column":8},"end":{"line":222,"column":null}},{"start":{"line":222,"column":15},"end":{"line":222,"column":null}}]},"4":{"loc":{"start":{"line":233,"column":8},"end":{"line":243,"column":null}},"type":"if","locations":[{"start":{"line":233,"column":8},"end":{"line":243,"column":null}},{"start":{"line":243,"column":15},"end":{"line":243,"column":null}}]},"5":{"loc":{"start":{"line":256,"column":20},"end":{"line":256,"column":null}},"type":"if","locations":[{"start":{"line":256,"column":20},"end":{"line":256,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":7,"7":7,"8":7,"9":7,"10":7,"11":7,"12":7,"13":7,"14":7,"15":7,"16":7,"17":19,"18":19,"19":7,"20":7,"21":1,"22":1,"23":1,"24":7,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":2,"32":1,"33":7,"34":7,"35":7,"36":5,"37":5,"38":5,"39":3,"40":3,"41":0,"42":2,"43":3,"44":3,"45":2,"46":1,"47":0,"48":8,"49":8,"50":8,"51":8,"52":20,"53":8,"54":2,"55":2,"56":2,"57":8,"58":2,"59":2,"60":2,"61":8,"62":15,"63":15,"64":12,"65":3,"66":0,"67":15,"68":15,"69":27,"70":3,"71":0,"72":15,"73":63,"74":27,"75":12,"76":0,"77":15,"78":1},"f":{"0":7,"1":7,"2":1,"3":1,"4":2,"5":5,"6":8,"7":2,"8":2,"9":15,"10":15,"11":15,"12":30,"13":15,"14":39},"b":{"0":[5],"1":[5,2],"2":[3,0],"3":[15,0],"4":[15,0],"5":[27]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/dependencies/ExternalPackage2DependencyResolver.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/dependencies/ExternalPackage2DependencyResolver.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"2":{"start":{"line":12,"column":24},"end":{"line":12,"column":40}},"3":{"start":{"line":12,"column":50},"end":{"line":12,"column":73}},"4":{"start":{"line":12,"column":73},"end":{"line":12,"column":80}},"5":{"start":{"line":19,"column":8},"end":{"line":19,"column":null}},"6":{"start":{"line":19,"column":48},"end":{"line":19,"column":null}},"7":{"start":{"line":22,"column":35},"end":{"line":28,"column":44}},"8":{"start":{"line":30,"column":48},"end":{"line":30,"column":50}},"9":{"start":{"line":31,"column":36},"end":{"line":31,"column":73}},"10":{"start":{"line":35,"column":12},"end":{"line":35,"column":null}},"11":{"start":{"line":41,"column":12},"end":{"line":42,"column":null}},"12":{"start":{"line":42,"column":14},"end":{"line":42,"column":null}},"13":{"start":{"line":45,"column":29},"end":{"line":45,"column":30}},"14":{"start":{"line":46,"column":37},"end":{"line":46,"column":64}},"15":{"start":{"line":50,"column":66},"end":{"line":50,"column":94}},"16":{"start":{"line":51,"column":24},"end":{"line":51,"column":null}},"17":{"start":{"line":52,"column":24},"end":{"line":52,"column":null}},"18":{"start":{"line":55,"column":56},"end":{"line":55,"column":87}},"19":{"start":{"line":56,"column":66},"end":{"line":56,"column":94}},"20":{"start":{"line":58,"column":28},"end":{"line":58,"column":null}},"21":{"start":{"line":59,"column":49},"end":{"line":62,"column":null}},"22":{"start":{"line":64,"column":28},"end":{"line":65,"column":null}},"23":{"start":{"line":67,"column":28},"end":{"line":68,"column":null}},"24":{"start":{"line":71,"column":28},"end":{"line":71,"column":null}},"25":{"start":{"line":73,"column":24},"end":{"line":73,"column":null}},"26":{"start":{"line":78,"column":8},"end":{"line":78,"column":null}},"27":{"start":{"line":87,"column":46},"end":{"line":87,"column":48}},"28":{"start":{"line":89,"column":8},"end":{"line":89,"column":null}},"29":{"start":{"line":90,"column":25},"end":{"line":90,"column":40}},"30":{"start":{"line":93,"column":33},"end":{"line":93,"column":47}},"31":{"start":{"line":95,"column":16},"end":{"line":95,"column":null}},"32":{"start":{"line":98,"column":16},"end":{"line":98,"column":null}},"33":{"start":{"line":101,"column":8},"end":{"line":101,"column":null}},"34":{"start":{"line":10,"column":0},"end":{"line":10,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":24}},"loc":{"start":{"line":12,"column":77},"end":{"line":12,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":14,"column":11},"end":{"line":14,"column":17}},"loc":{"start":{"line":17,"column":39},"end":{"line":79,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":55,"column":47},"end":{"line":55,"column":51}},"loc":{"start":{"line":55,"column":56},"end":{"line":55,"column":87}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":86,"column":12},"end":{"line":86,"column":21}},"loc":{"start":{"line":86,"column":34},"end":{"line":102,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":19,"column":8},"end":{"line":19,"column":null}},"type":"if","locations":[{"start":{"line":19,"column":8},"end":{"line":19,"column":null}}]},"1":{"loc":{"start":{"line":41,"column":12},"end":{"line":42,"column":null}},"type":"if","locations":[{"start":{"line":41,"column":12},"end":{"line":42,"column":null}}]},"2":{"loc":{"start":{"line":41,"column":15},"end":{"line":41,"column":90}},"type":"binary-expr","locations":[{"start":{"line":41,"column":15},"end":{"line":41,"column":35}},{"start":{"line":41,"column":39},"end":{"line":41,"column":90}}]},"3":{"loc":{"start":{"line":44,"column":16},"end":{"line":44,"column":83}},"type":"binary-expr","locations":[{"start":{"line":44,"column":16},"end":{"line":44,"column":40}},{"start":{"line":44,"column":44},"end":{"line":44,"column":83}}]},"4":{"loc":{"start":{"line":48,"column":24},"end":{"line":48,"column":95}},"type":"binary-expr","locations":[{"start":{"line":48,"column":24},"end":{"line":48,"column":43}},{"start":{"line":48,"column":47},"end":{"line":48,"column":95}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":2,"6":2,"7":2,"8":2,"9":2,"10":0,"11":12,"12":0,"13":10,"14":4,"15":0,"16":0,"17":0,"18":2,"19":2,"20":0,"21":0,"22":0,"23":2,"24":0,"25":2,"26":2,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":2},"f":{"0":2,"1":2,"2":2,"3":0},"b":{"0":[2],"1":[0],"2":[12,0],"3":[12,10],"4":[4,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/dependencies/PackageDependencyResolver.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/dependencies/PackageDependencyResolver.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"5":{"start":{"line":16,"column":16},"end":{"line":16,"column":32}},"6":{"start":{"line":17,"column":16},"end":{"line":17,"column":null}},"7":{"start":{"line":18,"column":16},"end":{"line":18,"column":46}},"8":{"start":{"line":19,"column":16},"end":{"line":19,"column":47}},"9":{"start":{"line":20,"column":16},"end":{"line":20,"column":54}},"10":{"start":{"line":13,"column":12},"end":{"line":13,"column":null}},"11":{"start":{"line":23,"column":8},"end":{"line":23,"column":null}},"12":{"start":{"line":34,"column":16},"end":{"line":34,"column":null}},"13":{"start":{"line":38,"column":16},"end":{"line":38,"column":null}},"14":{"start":{"line":42,"column":16},"end":{"line":42,"column":null}},"15":{"start":{"line":45,"column":29},"end":{"line":45,"column":30}},"16":{"start":{"line":46,"column":37},"end":{"line":46,"column":69}},"17":{"start":{"line":49,"column":24},"end":{"line":49,"column":null}},"18":{"start":{"line":52,"column":43},"end":{"line":52,"column":169}},"19":{"start":{"line":56,"column":24},"end":{"line":56,"column":null}},"20":{"start":{"line":61,"column":24},"end":{"line":61,"column":null}},"21":{"start":{"line":63,"column":60},"end":{"line":63,"column":62}},"22":{"start":{"line":65,"column":24},"end":{"line":65,"column":null}},"23":{"start":{"line":66,"column":24},"end":{"line":71,"column":null}},"24":{"start":{"line":72,"column":24},"end":{"line":76,"column":null}},"25":{"start":{"line":78,"column":51},"end":{"line":83,"column":75}},"26":{"start":{"line":84,"column":24},"end":{"line":84,"column":null}},"27":{"start":{"line":85,"column":24},"end":{"line":85,"column":null}},"28":{"start":{"line":86,"column":24},"end":{"line":86,"column":null}},"29":{"start":{"line":87,"column":24},"end":{"line":87,"column":null}},"30":{"start":{"line":88,"column":24},"end":{"line":88,"column":null}},"31":{"start":{"line":91,"column":24},"end":{"line":95,"column":null}},"32":{"start":{"line":99,"column":20},"end":{"line":103,"column":null}},"33":{"start":{"line":100,"column":24},"end":{"line":100,"column":null}},"34":{"start":{"line":101,"column":24},"end":{"line":101,"column":null}},"35":{"start":{"line":103,"column":24},"end":{"line":103,"column":null}},"36":{"start":{"line":107,"column":8},"end":{"line":107,"column":null}},"37":{"start":{"line":124,"column":64},"end":{"line":124,"column":98}},"38":{"start":{"line":125,"column":12},"end":{"line":125,"column":null}},"39":{"start":{"line":125,"column":51},"end":{"line":125,"column":null}},"40":{"start":{"line":130,"column":36},"end":{"line":130,"column":60}},"41":{"start":{"line":131,"column":29},"end":{"line":131,"column":53}},"42":{"start":{"line":133,"column":12},"end":{"line":133,"column":null}},"43":{"start":{"line":138,"column":12},"end":{"line":141,"column":null}},"44":{"start":{"line":143,"column":43},"end":{"line":143,"column":75}},"45":{"start":{"line":146,"column":16},"end":{"line":150,"column":null}},"46":{"start":{"line":152,"column":16},"end":{"line":156,"column":null}},"47":{"start":{"line":159,"column":12},"end":{"line":163,"column":null}},"48":{"start":{"line":164,"column":12},"end":{"line":167,"column":null}},"49":{"start":{"line":171,"column":12},"end":{"line":173,"column":null}},"50":{"start":{"line":176,"column":64},"end":{"line":176,"column":109}},"51":{"start":{"line":177,"column":12},"end":{"line":177,"column":null}},"52":{"start":{"line":180,"column":12},"end":{"line":180,"column":null}},"53":{"start":{"line":182,"column":8},"end":{"line":182,"column":null}},"54":{"start":{"line":197,"column":20},"end":{"line":197,"column":44}},"55":{"start":{"line":198,"column":24},"end":{"line":198,"column":60}},"56":{"start":{"line":199,"column":21},"end":{"line":199,"column":53}},"57":{"start":{"line":202,"column":28},"end":{"line":202,"column":156}},"58":{"start":{"line":205,"column":20},"end":{"line":205,"column":null}},"59":{"start":{"line":206,"column":20},"end":{"line":206,"column":null}},"60":{"start":{"line":209,"column":12},"end":{"line":209,"column":null}},"61":{"start":{"line":209,"column":48},"end":{"line":209,"column":null}},"62":{"start":{"line":213,"column":12},"end":{"line":215,"column":null}},"63":{"start":{"line":218,"column":8},"end":{"line":218,"column":null}},"64":{"start":{"line":222,"column":49},"end":{"line":222,"column":54}},"65":{"start":{"line":223,"column":8},"end":{"line":223,"column":null}},"66":{"start":{"line":12,"column":0},"end":{"line":12,"column":21}},"67":{"start":{"line":228,"column":12},"end":{"line":228,"column":null}},"68":{"start":{"line":237,"column":20},"end":{"line":237,"column":51}},"69":{"start":{"line":238,"column":8},"end":{"line":239,"column":null}},"70":{"start":{"line":238,"column":29},"end":{"line":238,"column":41}},"71":{"start":{"line":239,"column":13},"end":{"line":239,"column":null}},"72":{"start":{"line":254,"column":20},"end":{"line":254,"column":51}},"73":{"start":{"line":255,"column":8},"end":{"line":255,"column":null}},"74":{"start":{"line":256,"column":8},"end":{"line":256,"column":null}},"75":{"start":{"line":266,"column":20},"end":{"line":266,"column":51}},"76":{"start":{"line":267,"column":8},"end":{"line":267,"column":null}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"loc":{"start":{"line":20,"column":54},"end":{"line":24,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":31,"column":11},"end":{"line":31,"column":17}},"loc":{"start":{"line":31,"column":49},"end":{"line":108,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":116,"column":12},"end":{"line":116,"column":18}},"loc":{"start":{"line":120,"column":23},"end":{"line":183,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":124,"column":56},"end":{"line":124,"column":59}},"loc":{"start":{"line":124,"column":64},"end":{"line":124,"column":98}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":176,"column":56},"end":{"line":176,"column":59}},"loc":{"start":{"line":176,"column":64},"end":{"line":176,"column":109}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":191,"column":12},"end":{"line":191,"column":18}},"loc":{"start":{"line":193,"column":62},"end":{"line":219,"column":null}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":221,"column":12},"end":{"line":221,"column":40}},"loc":{"start":{"line":221,"column":61},"end":{"line":224,"column":null}}},"7":{"name":"(anonymous_14)","decl":{"start":{"line":227,"column":0},"end":{"line":227,"column":6}},"loc":{"start":{"line":227,"column":0},"end":{"line":269,"column":null}}},"8":{"name":"(anonymous_15)","decl":{"start":{"line":236,"column":4},"end":{"line":236,"column":7}},"loc":{"start":{"line":236,"column":48},"end":{"line":240,"column":null}}},"9":{"name":"(anonymous_16)","decl":{"start":{"line":249,"column":4},"end":{"line":249,"column":7}},"loc":{"start":{"line":252,"column":43},"end":{"line":257,"column":null}}},"10":{"name":"(anonymous_17)","decl":{"start":{"line":265,"column":4},"end":{"line":265,"column":7}},"loc":{"start":{"line":265,"column":48},"end":{"line":268,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":33,"column":16},"end":{"line":33,"column":93}},"type":"binary-expr","locations":[{"start":{"line":33,"column":16},"end":{"line":33,"column":53}},{"start":{"line":33,"column":57},"end":{"line":33,"column":93}}]},"1":{"loc":{"start":{"line":37,"column":16},"end":{"line":37,"column":104}},"type":"binary-expr","locations":[{"start":{"line":37,"column":16},"end":{"line":37,"column":40}},{"start":{"line":37,"column":44},"end":{"line":37,"column":104}}]},"2":{"loc":{"start":{"line":41,"column":16},"end":{"line":41,"column":106}},"type":"binary-expr","locations":[{"start":{"line":41,"column":16},"end":{"line":41,"column":41}},{"start":{"line":41,"column":45},"end":{"line":41,"column":106}}]},"3":{"loc":{"start":{"line":44,"column":16},"end":{"line":44,"column":93}},"type":"binary-expr","locations":[{"start":{"line":44,"column":16},"end":{"line":44,"column":45}},{"start":{"line":44,"column":49},"end":{"line":44,"column":93}}]},"4":{"loc":{"start":{"line":47,"column":24},"end":{"line":47,"column":149}},"type":"binary-expr","locations":[{"start":{"line":47,"column":24},"end":{"line":47,"column":91}},{"start":{"line":47,"column":95},"end":{"line":47,"column":149}}]},"5":{"loc":{"start":{"line":52,"column":43},"end":{"line":52,"column":169}},"type":"cond-expr","locations":[{"start":{"line":52,"column":97},"end":{"line":52,"column":115}},{"start":{"line":52,"column":116},"end":{"line":52,"column":169}}]},"6":{"loc":{"start":{"line":59,"column":24},"end":{"line":59,"column":127}},"type":"binary-expr","locations":[{"start":{"line":59,"column":24},"end":{"line":59,"column":48}},{"start":{"line":59,"column":52},"end":{"line":59,"column":105}},{"start":{"line":59,"column":109},"end":{"line":59,"column":127}}]},"7":{"loc":{"start":{"line":64,"column":25},"end":{"line":64,"column":70}},"type":"binary-expr","locations":[{"start":{"line":64,"column":25},"end":{"line":64,"column":42}},{"start":{"line":64,"column":46},"end":{"line":64,"column":70}}]},"8":{"loc":{"start":{"line":99,"column":20},"end":{"line":103,"column":null}},"type":"if","locations":[{"start":{"line":99,"column":20},"end":{"line":103,"column":null}},{"start":{"line":103,"column":24},"end":{"line":103,"column":null}}]},"9":{"loc":{"start":{"line":125,"column":12},"end":{"line":125,"column":null}},"type":"if","locations":[{"start":{"line":125,"column":12},"end":{"line":125,"column":null}}]},"10":{"loc":{"start":{"line":132,"column":12},"end":{"line":132,"column":53}},"type":"binary-expr","locations":[{"start":{"line":132,"column":12},"end":{"line":132,"column":29}},{"start":{"line":132,"column":33},"end":{"line":132,"column":53}}]},"11":{"loc":{"start":{"line":176,"column":64},"end":{"line":176,"column":109}},"type":"binary-expr","locations":[{"start":{"line":176,"column":64},"end":{"line":176,"column":98}},{"start":{"line":176,"column":102},"end":{"line":176,"column":109}}]},"12":{"loc":{"start":{"line":209,"column":12},"end":{"line":209,"column":null}},"type":"if","locations":[{"start":{"line":209,"column":12},"end":{"line":209,"column":null}}]},"13":{"loc":{"start":{"line":238,"column":8},"end":{"line":239,"column":null}},"type":"if","locations":[{"start":{"line":238,"column":8},"end":{"line":239,"column":null}},{"start":{"line":239,"column":13},"end":{"line":239,"column":null}}]}},"s":{"0":3,"1":3,"2":3,"3":3,"4":3,"5":10,"6":10,"7":10,"8":10,"9":10,"10":10,"11":10,"12":0,"13":24,"14":0,"15":19,"16":28,"17":1,"18":27,"19":9,"20":2,"21":16,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":15,"32":13,"33":10,"34":10,"35":3,"36":7,"37":38,"38":16,"39":10,"40":6,"41":6,"42":6,"43":1,"44":5,"45":1,"46":4,"47":5,"48":5,"49":1,"50":13,"51":4,"52":1,"53":4,"54":4,"55":4,"56":4,"57":9,"58":3,"59":3,"60":9,"61":3,"62":1,"63":3,"64":55,"65":55,"66":3,"67":10,"68":6,"69":6,"70":1,"71":5,"72":5,"73":5,"74":5,"75":6,"76":6},"f":{"0":10,"1":10,"2":16,"3":38,"4":13,"5":4,"6":55,"7":10,"8":6,"9":5,"10":6},"b":{"0":[47,0],"1":[47,35],"2":[23,0],"3":[23,19],"4":[28,1],"5":[0,27],"6":[18,8,2],"7":[16,1],"8":[10,3],"9":[10],"10":[6,6],"11":[13,5],"12":[3],"13":[1,5]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/dependencies/TransitiveDependencyResolver.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/dependencies/TransitiveDependencyResolver.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"6":{"start":{"line":8,"column":0},"end":{"line":8,"column":null}},"7":{"start":{"line":11,"column":24},"end":{"line":11,"column":46}},"8":{"start":{"line":11,"column":56},"end":{"line":11,"column":71}},"9":{"start":{"line":14,"column":8},"end":{"line":14,"column":null}},"10":{"start":{"line":16,"column":34},"end":{"line":16,"column":75}},"11":{"start":{"line":17,"column":8},"end":{"line":17,"column":null}},"12":{"start":{"line":18,"column":34},"end":{"line":18,"column":101}},"13":{"start":{"line":19,"column":8},"end":{"line":22,"column":null}},"14":{"start":{"line":23,"column":8},"end":{"line":23,"column":null}},"15":{"start":{"line":25,"column":8},"end":{"line":25,"column":null}},"16":{"start":{"line":34,"column":16},"end":{"line":34,"column":null}},"17":{"start":{"line":37,"column":8},"end":{"line":37,"column":null}},"18":{"start":{"line":43,"column":19},"end":{"line":43,"column":51}},"19":{"start":{"line":45,"column":12},"end":{"line":49,"column":null}},"20":{"start":{"line":50,"column":80},"end":{"line":50,"column":82}},"21":{"start":{"line":54,"column":20},"end":{"line":54,"column":null}},"22":{"start":{"line":55,"column":20},"end":{"line":61,"column":null}},"23":{"start":{"line":64,"column":16},"end":{"line":64,"column":null}},"24":{"start":{"line":67,"column":37},"end":{"line":69,"column":55}},"25":{"start":{"line":68,"column":59},"end":{"line":68,"column":82}},"26":{"start":{"line":69,"column":33},"end":{"line":69,"column":54}},"27":{"start":{"line":70,"column":25},"end":{"line":70,"column":26}},"28":{"start":{"line":72,"column":34},"end":{"line":72,"column":102}},"29":{"start":{"line":74,"column":33},"end":{"line":74,"column":38}},"30":{"start":{"line":76,"column":51},"end":{"line":76,"column":119}},"31":{"start":{"line":79,"column":31},"end":{"line":79,"column":null}},"32":{"start":{"line":82,"column":32},"end":{"line":82,"column":null}},"33":{"start":{"line":83,"column":32},"end":{"line":83,"column":null}},"34":{"start":{"line":89,"column":16},"end":{"line":91,"column":null}},"35":{"start":{"line":90,"column":67},"end":{"line":90,"column":90}},"36":{"start":{"line":91,"column":37},"end":{"line":91,"column":58}},"37":{"start":{"line":93,"column":12},"end":{"line":93,"column":null}},"38":{"start":{"line":95,"column":8},"end":{"line":95,"column":null}},"39":{"start":{"line":100,"column":10},"end":{"line":100,"column":null}},"40":{"start":{"line":103,"column":21},"end":{"line":103,"column":29}},"41":{"start":{"line":104,"column":8},"end":{"line":104,"column":null}},"42":{"start":{"line":105,"column":8},"end":{"line":105,"column":null}},"43":{"start":{"line":10,"column":0},"end":{"line":10,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":11,"column":4},"end":{"line":11,"column":24}},"loc":{"start":{"line":11,"column":71},"end":{"line":11,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":13,"column":11},"end":{"line":13,"column":17}},"loc":{"start":{"line":13,"column":46},"end":{"line":26,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":28,"column":12},"end":{"line":28,"column":56}},"loc":{"start":{"line":30,"column":34},"end":{"line":38,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":40,"column":12},"end":{"line":40,"column":32}},"loc":{"start":{"line":41,"column":81},"end":{"line":96,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":68,"column":47},"end":{"line":68,"column":54}},"loc":{"start":{"line":68,"column":59},"end":{"line":68,"column":82}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":69,"column":19},"end":{"line":69,"column":28}},"loc":{"start":{"line":69,"column":33},"end":{"line":69,"column":54}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":90,"column":55},"end":{"line":90,"column":62}},"loc":{"start":{"line":90,"column":67},"end":{"line":90,"column":90}}},"7":{"name":"(anonymous_14)","decl":{"start":{"line":91,"column":23},"end":{"line":91,"column":32}},"loc":{"start":{"line":91,"column":37},"end":{"line":91,"column":58}}},"8":{"name":"(anonymous_15)","decl":{"start":{"line":98,"column":12},"end":{"line":98,"column":35}},"loc":{"start":{"line":98,"column":69},"end":{"line":106,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":99,"column":12},"end":{"line":99,"column":64}},"type":"binary-expr","locations":[{"start":{"line":99,"column":12},"end":{"line":99,"column":17}},{"start":{"line":99,"column":21},"end":{"line":99,"column":36}},{"start":{"line":99,"column":40},"end":{"line":99,"column":45}},{"start":{"line":99,"column":49},"end":{"line":99,"column":64}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":2,"6":2,"7":7,"8":7,"9":7,"10":7,"11":7,"12":7,"13":7,"14":7,"15":7,"16":7,"17":7,"18":7,"19":42,"20":42,"21":65,"22":65,"23":87,"24":42,"25":221,"26":152,"27":42,"28":96,"29":96,"30":7,"31":0,"32":7,"33":7,"34":145,"35":727,"36":727,"37":42,"38":7,"39":0,"40":0,"41":0,"42":0,"43":2},"f":{"0":7,"1":7,"2":7,"3":7,"4":221,"5":152,"6":727,"7":727,"8":0},"b":{"0":[0,0,0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/deploymentFilters/EntitlementVersionFilter.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/deploymentFilters/EntitlementVersionFilter.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"4":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"5":{"start":{"line":8,"column":0},"end":{"line":8,"column":null}},"6":{"start":{"line":9,"column":23},"end":{"line":9,"column":49}},"7":{"start":{"line":11,"column":35},"end":{"line":11,"column":131}},"8":{"start":{"line":12,"column":49},"end":{"line":12,"column":88}},"9":{"start":{"line":18,"column":31},"end":{"line":18,"column":75}},"10":{"start":{"line":19,"column":42},"end":{"line":19,"column":47}},"11":{"start":{"line":22,"column":16},"end":{"line":22,"column":null}},"12":{"start":{"line":23,"column":16},"end":{"line":23,"column":null}},"13":{"start":{"line":26,"column":8},"end":{"line":26,"column":null}},"14":{"start":{"line":26,"column":33},"end":{"line":26,"column":null}},"15":{"start":{"line":29,"column":38},"end":{"line":29,"column":111}},"16":{"start":{"line":33,"column":16},"end":{"line":33,"column":null}},"17":{"start":{"line":34,"column":16},"end":{"line":34,"column":null}},"18":{"start":{"line":36,"column":16},"end":{"line":36,"column":null}},"19":{"start":{"line":37,"column":16},"end":{"line":37,"column":null}},"20":{"start":{"line":40,"column":12},"end":{"line":40,"column":null}},"21":{"start":{"line":42,"column":36},"end":{"line":42,"column":106}},"22":{"start":{"line":43,"column":39},"end":{"line":43,"column":57}},"23":{"start":{"line":48,"column":42},"end":{"line":48,"column":72}},"24":{"start":{"line":50,"column":62},"end":{"line":51,"column":null}},"25":{"start":{"line":51,"column":49},"end":{"line":51,"column":110}},"26":{"start":{"line":61,"column":24},"end":{"line":61,"column":null}},"27":{"start":{"line":62,"column":38},"end":{"line":66,"column":26}},"28":{"start":{"line":67,"column":41},"end":{"line":67,"column":71}},"29":{"start":{"line":68,"column":24},"end":{"line":68,"column":null}},"30":{"start":{"line":69,"column":24},"end":{"line":69,"column":null}},"31":{"start":{"line":71,"column":24},"end":{"line":75,"column":null}},"32":{"start":{"line":78,"column":24},"end":{"line":78,"column":null}},"33":{"start":{"line":81,"column":20},"end":{"line":81,"column":null}},"34":{"start":{"line":85,"column":12},"end":{"line":85,"column":null}},"35":{"start":{"line":86,"column":12},"end":{"line":86,"column":null}},"36":{"start":{"line":88,"column":12},"end":{"line":88,"column":null}},"37":{"start":{"line":89,"column":12},"end":{"line":89,"column":null}},"38":{"start":{"line":94,"column":8},"end":{"line":94,"column":null}},"39":{"start":{"line":94,"column":47},"end":{"line":94,"column":null}},"40":{"start":{"line":96,"column":8},"end":{"line":97,"column":null}},"41":{"start":{"line":96,"column":67},"end":{"line":96,"column":80}},"42":{"start":{"line":97,"column":13},"end":{"line":97,"column":null}},"43":{"start":{"line":14,"column":0},"end":{"line":14,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":16,"column":11},"end":{"line":16,"column":17}},"loc":{"start":{"line":16,"column":78},"end":{"line":91,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":51,"column":25},"end":{"line":51,"column":44}},"loc":{"start":{"line":51,"column":49},"end":{"line":51,"column":110}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":93,"column":11},"end":{"line":93,"column":20}},"loc":{"start":{"line":93,"column":60},"end":{"line":98,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":26,"column":8},"end":{"line":26,"column":null}},"type":"if","locations":[{"start":{"line":26,"column":8},"end":{"line":26,"column":null}}]},"1":{"loc":{"start":{"line":55,"column":24},"end":{"line":57,"column":118}},"type":"binary-expr","locations":[{"start":{"line":55,"column":24},"end":{"line":55,"column":47}},{"start":{"line":56,"column":24},"end":{"line":56,"column":71}},{"start":{"line":57,"column":24},"end":{"line":57,"column":118}}]},"2":{"loc":{"start":{"line":94,"column":8},"end":{"line":94,"column":null}},"type":"if","locations":[{"start":{"line":94,"column":8},"end":{"line":94,"column":null}}]},"3":{"loc":{"start":{"line":96,"column":8},"end":{"line":97,"column":null}},"type":"if","locations":[{"start":{"line":96,"column":8},"end":{"line":97,"column":null}},{"start":{"line":97,"column":13},"end":{"line":97,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":5,"10":5,"11":5,"12":5,"13":5,"14":0,"15":5,"16":3,"17":3,"18":1,"19":1,"20":4,"21":4,"22":4,"23":8,"24":8,"25":7,"26":1,"27":1,"28":1,"29":1,"30":1,"31":3,"32":4,"33":0,"34":4,"35":4,"36":1,"37":1,"38":0,"39":0,"40":0,"41":0,"42":0,"43":1},"f":{"0":5,"1":7,"2":0},"b":{"0":[0],"1":[8,4,4],"2":[0],"3":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/diff/PackageComponentDiff.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/diff/PackageComponentDiff.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":null}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":null}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":null}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":null}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":null}},"12":{"start":{"line":14,"column":27},"end":{"line":14,"column":41}},"13":{"start":{"line":15,"column":12},"end":{"line":15,"column":23}},"14":{"start":{"line":31,"column":16},"end":{"line":31,"column":30}},"15":{"start":{"line":32,"column":16},"end":{"line":32,"column":35}},"16":{"start":{"line":33,"column":16},"end":{"line":33,"column":37}},"17":{"start":{"line":34,"column":16},"end":{"line":34,"column":35}},"18":{"start":{"line":35,"column":16},"end":{"line":35,"column":39}},"19":{"start":{"line":38,"column":12},"end":{"line":38,"column":null}},"20":{"start":{"line":41,"column":12},"end":{"line":41,"column":null}},"21":{"start":{"line":43,"column":8},"end":{"line":43,"column":null}},"22":{"start":{"line":44,"column":8},"end":{"line":44,"column":null}},"23":{"start":{"line":45,"column":8},"end":{"line":45,"column":null}},"24":{"start":{"line":47,"column":8},"end":{"line":47,"column":null}},"25":{"start":{"line":48,"column":8},"end":{"line":48,"column":null}},"26":{"start":{"line":52,"column":8},"end":{"line":52,"column":null}},"27":{"start":{"line":54,"column":25},"end":{"line":54,"column":32}},"28":{"start":{"line":55,"column":19},"end":{"line":55,"column":21}},"29":{"start":{"line":58,"column":27},"end":{"line":58,"column":84}},"30":{"start":{"line":59,"column":25},"end":{"line":59,"column":80}},"31":{"start":{"line":61,"column":12},"end":{"line":61,"column":null}},"32":{"start":{"line":64,"column":8},"end":{"line":70,"column":null}},"33":{"start":{"line":72,"column":22},"end":{"line":72,"column":42}},"34":{"start":{"line":73,"column":33},"end":{"line":73,"column":65}},"35":{"start":{"line":74,"column":8},"end":{"line":74,"column":null}},"36":{"start":{"line":76,"column":26},"end":{"line":76,"column":46}},"37":{"start":{"line":77,"column":27},"end":{"line":77,"column":43}},"38":{"start":{"line":79,"column":8},"end":{"line":90,"column":null}},"39":{"start":{"line":80,"column":24},"end":{"line":80,"column":29}},"40":{"start":{"line":81,"column":34},"end":{"line":81,"column":89}},"41":{"start":{"line":82,"column":25},"end":{"line":82,"column":26}},"42":{"start":{"line":83,"column":36},"end":{"line":83,"column":98}},"43":{"start":{"line":85,"column":20},"end":{"line":85,"column":null}},"44":{"start":{"line":86,"column":20},"end":{"line":86,"column":null}},"45":{"start":{"line":89,"column":12},"end":{"line":89,"column":null}},"46":{"start":{"line":93,"column":12},"end":{"line":93,"column":null}},"47":{"start":{"line":96,"column":25},"end":{"line":96,"column":47}},"48":{"start":{"line":99,"column":25},"end":{"line":99,"column":26}},"49":{"start":{"line":102,"column":35},"end":{"line":102,"column":54}},"50":{"start":{"line":104,"column":43},"end":{"line":104,"column":83}},"51":{"start":{"line":107,"column":28},"end":{"line":107,"column":null}},"52":{"start":{"line":108,"column":28},"end":{"line":108,"column":null}},"53":{"start":{"line":110,"column":28},"end":{"line":110,"column":null}},"54":{"start":{"line":111,"column":28},"end":{"line":114,"column":null}},"55":{"start":{"line":112,"column":30},"end":{"line":112,"column":115}},"56":{"start":{"line":114,"column":30},"end":{"line":114,"column":null}},"57":{"start":{"line":116,"column":28},"end":{"line":116,"column":null}},"58":{"start":{"line":118,"column":28},"end":{"line":118,"column":null}},"59":{"start":{"line":120,"column":28},"end":{"line":120,"column":null}},"60":{"start":{"line":125,"column":19},"end":{"line":126,"column":null}},"61":{"start":{"line":126,"column":20},"end":{"line":126,"column":null}},"62":{"start":{"line":130,"column":20},"end":{"line":134,"column":null}},"63":{"start":{"line":140,"column":12},"end":{"line":140,"column":null}},"64":{"start":{"line":141,"column":12},"end":{"line":141,"column":null}},"65":{"start":{"line":146,"column":12},"end":{"line":146,"column":null}},"66":{"start":{"line":147,"column":12},"end":{"line":147,"column":null}},"67":{"start":{"line":150,"column":8},"end":{"line":150,"column":null}},"68":{"start":{"line":152,"column":8},"end":{"line":152,"column":null}},"69":{"start":{"line":157,"column":8},"end":{"line":157,"column":null}},"70":{"start":{"line":159,"column":12},"end":{"line":159,"column":null}},"71":{"start":{"line":162,"column":24},"end":{"line":162,"column":28}},"72":{"start":{"line":163,"column":8},"end":{"line":170,"column":null}},"73":{"start":{"line":168,"column":16},"end":{"line":168,"column":null}},"74":{"start":{"line":171,"column":8},"end":{"line":171,"column":null}},"75":{"start":{"line":176,"column":12},"end":{"line":176,"column":null}},"76":{"start":{"line":178,"column":12},"end":{"line":180,"column":null}},"77":{"start":{"line":179,"column":16},"end":{"line":179,"column":null}},"78":{"start":{"line":182,"column":8},"end":{"line":182,"column":null}},"79":{"start":{"line":184,"column":21},"end":{"line":184,"column":22}},"80":{"start":{"line":185,"column":27},"end":{"line":185,"column":44}},"81":{"start":{"line":187,"column":30},"end":{"line":187,"column":68}},"82":{"start":{"line":188,"column":32},"end":{"line":188,"column":34}},"83":{"start":{"line":190,"column":20},"end":{"line":190,"column":null}},"84":{"start":{"line":192,"column":20},"end":{"line":192,"column":null}},"85":{"start":{"line":195,"column":27},"end":{"line":195,"column":65}},"86":{"start":{"line":202,"column":24},"end":{"line":208,"column":null}},"87":{"start":{"line":210,"column":24},"end":{"line":210,"column":null}},"88":{"start":{"line":212,"column":33},"end":{"line":212,"column":88}},"89":{"start":{"line":214,"column":41},"end":{"line":214,"column":113}},"90":{"start":{"line":216,"column":28},"end":{"line":220,"column":null}},"91":{"start":{"line":222,"column":28},"end":{"line":226,"column":null}},"92":{"start":{"line":228,"column":28},"end":{"line":234,"column":null}},"93":{"start":{"line":236,"column":28},"end":{"line":240,"column":null}},"94":{"start":{"line":242,"column":24},"end":{"line":246,"column":null}},"95":{"start":{"line":248,"column":24},"end":{"line":254,"column":null}},"96":{"start":{"line":256,"column":24},"end":{"line":271,"column":null}},"97":{"start":{"line":257,"column":28},"end":{"line":261,"column":null}},"98":{"start":{"line":262,"column":28},"end":{"line":268,"column":null}},"99":{"start":{"line":276,"column":16},"end":{"line":282,"column":null}},"100":{"start":{"line":286,"column":8},"end":{"line":286,"column":null}},"101":{"start":{"line":291,"column":21},"end":{"line":291,"column":22}},"102":{"start":{"line":292,"column":12},"end":{"line":292,"column":null}},"103":{"start":{"line":294,"column":8},"end":{"line":296,"column":null}},"104":{"start":{"line":295,"column":12},"end":{"line":295,"column":null}},"105":{"start":{"line":299,"column":23},"end":{"line":306,"column":null}},"106":{"start":{"line":308,"column":41},"end":{"line":308,"column":49}},"107":{"start":{"line":309,"column":27},"end":{"line":309,"column":74}},"108":{"start":{"line":310,"column":26},"end":{"line":310,"column":46}},"109":{"start":{"line":311,"column":22},"end":{"line":311,"column":47}},"110":{"start":{"line":312,"column":12},"end":{"line":312,"column":null}},"111":{"start":{"line":317,"column":28},"end":{"line":317,"column":33}},"112":{"start":{"line":318,"column":21},"end":{"line":318,"column":22}},"113":{"start":{"line":320,"column":16},"end":{"line":320,"column":null}},"114":{"start":{"line":321,"column":16},"end":{"line":321,"column":null}},"115":{"start":{"line":322,"column":16},"end":{"line":322,"column":null}},"116":{"start":{"line":327,"column":12},"end":{"line":330,"column":null}},"117":{"start":{"line":331,"column":12},"end":{"line":331,"column":null}},"118":{"start":{"line":333,"column":8},"end":{"line":333,"column":null}},"119":{"start":{"line":337,"column":28},"end":{"line":337,"column":47}},"120":{"start":{"line":338,"column":29},"end":{"line":338,"column":58}},"121":{"start":{"line":339,"column":25},"end":{"line":339,"column":29}},"122":{"start":{"line":340,"column":33},"end":{"line":340,"column":52}},"123":{"start":{"line":341,"column":31},"end":{"line":341,"column":46}},"124":{"start":{"line":343,"column":28},"end":{"line":343,"column":47}},"125":{"start":{"line":345,"column":33},"end":{"line":348,"column":null}},"126":{"start":{"line":350,"column":21},"end":{"line":350,"column":22}},"127":{"start":{"line":352,"column":32},"end":{"line":352,"column":66}},"128":{"start":{"line":354,"column":32},"end":{"line":354,"column":88}},"129":{"start":{"line":355,"column":16},"end":{"line":355,"column":null}},"130":{"start":{"line":356,"column":16},"end":{"line":356,"column":null}},"131":{"start":{"line":359,"column":20},"end":{"line":359,"column":null}},"132":{"start":{"line":363,"column":20},"end":{"line":363,"column":null}},"133":{"start":{"line":366,"column":35},"end":{"line":366,"column":62}},"134":{"start":{"line":370,"column":20},"end":{"line":374,"column":null}},"135":{"start":{"line":377,"column":20},"end":{"line":381,"column":null}},"136":{"start":{"line":384,"column":32},"end":{"line":384,"column":67}},"137":{"start":{"line":386,"column":28},"end":{"line":386,"column":63}},"138":{"start":{"line":388,"column":32},"end":{"line":388,"column":63}},"139":{"start":{"line":389,"column":16},"end":{"line":389,"column":null}},"140":{"start":{"line":390,"column":35},"end":{"line":390,"column":62}},"141":{"start":{"line":393,"column":20},"end":{"line":393,"column":null}},"142":{"start":{"line":397,"column":20},"end":{"line":397,"column":null}},"143":{"start":{"line":400,"column":16},"end":{"line":405,"column":null}},"144":{"start":{"line":408,"column":16},"end":{"line":412,"column":null}},"145":{"start":{"line":415,"column":8},"end":{"line":415,"column":null}},"146":{"start":{"line":18,"column":0},"end":{"line":18,"column":21}},"147":{"start":{"line":418,"column":0},"end":{"line":418,"column":null}},"148":{"start":{"line":419,"column":4},"end":{"line":419,"column":null}},"149":{"start":{"line":420,"column":4},"end":{"line":420,"column":null}},"150":{"start":{"line":421,"column":4},"end":{"line":421,"column":null}},"151":{"start":{"line":422,"column":4},"end":{"line":422,"column":null}},"152":{"start":{"line":423,"column":4},"end":{"line":423,"column":null}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":30,"column":4},"end":{"line":30,"column":null}},"loc":{"start":{"line":35,"column":39},"end":{"line":49,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":51,"column":11},"end":{"line":51,"column":17}},"loc":{"start":{"line":51,"column":43},"end":{"line":153,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":79,"column":44},"end":{"line":79,"column":51}},"loc":{"start":{"line":79,"column":55},"end":{"line":90,"column":9}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":156,"column":12},"end":{"line":156,"column":26}},"loc":{"start":{"line":156,"column":64},"end":{"line":172,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":163,"column":30},"end":{"line":163,"column":36}},"loc":{"start":{"line":163,"column":40},"end":{"line":170,"column":9}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":174,"column":12},"end":{"line":174,"column":18}},"loc":{"start":{"line":174,"column":92},"end":{"line":287,"column":null}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":178,"column":84},"end":{"line":178,"column":92}},"loc":{"start":{"line":178,"column":96},"end":{"line":180,"column":13}}},"7":{"name":"(anonymous_14)","decl":{"start":{"line":289,"column":12},"end":{"line":289,"column":35}},"loc":{"start":{"line":289,"column":98},"end":{"line":314,"column":null}}},"8":{"name":"(anonymous_15)","decl":{"start":{"line":294,"column":40},"end":{"line":294,"column":48}},"loc":{"start":{"line":294,"column":52},"end":{"line":296,"column":9}}},"9":{"name":"(anonymous_16)","decl":{"start":{"line":316,"column":12},"end":{"line":316,"column":35}},"loc":{"start":{"line":316,"column":64},"end":{"line":334,"column":null}}},"10":{"name":"(anonymous_17)","decl":{"start":{"line":336,"column":12},"end":{"line":336,"column":18}},"loc":{"start":{"line":336,"column":43},"end":{"line":416,"column":null}}},"11":{"name":"(anonymous_18)","decl":{"start":{"line":418,"column":0},"end":{"line":418,"column":5}},"loc":{"start":{"line":418,"column":14},"end":{"line":424,"column":1}}}},"branchMap":{"0":{"loc":{"start":{"line":37,"column":12},"end":{"line":37,"column":68}},"type":"binary-expr","locations":[{"start":{"line":37,"column":12},"end":{"line":37,"column":35}},{"start":{"line":37,"column":39},"end":{"line":37,"column":68}}]},"1":{"loc":{"start":{"line":98,"column":12},"end":{"line":98,"column":49}},"type":"binary-expr","locations":[{"start":{"line":98,"column":12},"end":{"line":98,"column":23}},{"start":{"line":98,"column":27},"end":{"line":98,"column":49}}]},"2":{"loc":{"start":{"line":111,"column":28},"end":{"line":114,"column":null}},"type":"if","locations":[{"start":{"line":111,"column":28},"end":{"line":114,"column":null}},{"start":{"line":114,"column":30},"end":{"line":114,"column":null}}]},"3":{"loc":{"start":{"line":125,"column":19},"end":{"line":126,"column":null}},"type":"if","locations":[{"start":{"line":125,"column":19},"end":{"line":126,"column":null}}]},"4":{"loc":{"start":{"line":157,"column":23},"end":{"line":157,"column":41}},"type":"binary-expr","locations":[{"start":{"line":157,"column":23},"end":{"line":157,"column":35}},{"start":{"line":157,"column":39},"end":{"line":157,"column":41}}]},"5":{"loc":{"start":{"line":165,"column":16},"end":{"line":166,"column":69}},"type":"binary-expr","locations":[{"start":{"line":165,"column":16},"end":{"line":165,"column":63}},{"start":{"line":166,"column":16},"end":{"line":166,"column":69}}]},"6":{"loc":{"start":{"line":179,"column":23},"end":{"line":179,"column":80}},"type":"binary-expr","locations":[{"start":{"line":179,"column":23},"end":{"line":179,"column":49}},{"start":{"line":179,"column":53},"end":{"line":179,"column":80}}]},"7":{"loc":{"start":{"line":256,"column":24},"end":{"line":271,"column":null}},"type":"if","locations":[{"start":{"line":256,"column":24},"end":{"line":271,"column":null}},{"start":{"line":269,"column":31},"end":{"line":271,"column":null}}]},"8":{"loc":{"start":{"line":295,"column":19},"end":{"line":295,"column":66}},"type":"binary-expr","locations":[{"start":{"line":295,"column":19},"end":{"line":295,"column":35}},{"start":{"line":295,"column":39},"end":{"line":295,"column":66}}]},"9":{"loc":{"start":{"line":418,"column":5},"end":{"line":418,"column":null}},"type":"binary-expr","locations":[{"start":{"line":418,"column":5},"end":{"line":418,"column":14}},{"start":{"line":418,"column":5},"end":{"line":418,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":0,"114":0,"115":0,"116":0,"117":0,"118":0,"119":0,"120":0,"121":0,"122":0,"123":0,"124":0,"125":0,"126":0,"127":0,"128":0,"129":0,"130":0,"131":0,"132":0,"133":0,"134":0,"135":0,"136":0,"137":0,"138":0,"139":0,"140":0,"141":0,"142":0,"143":0,"144":0,"145":0,"146":1,"147":1,"148":1,"149":1,"150":1,"151":1,"152":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":1},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[1,1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/diff/PackageDiffImpl.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/diff/PackageDiffImpl.ts","statementMap":{"0":{"start":{"line":1,"column":11},"end":{"line":1,"column":24}},"1":{"start":{"line":2,"column":13},"end":{"line":2,"column":28}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":null}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":null}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":null}},"10":{"start":{"line":13,"column":4},"end":{"line":13,"column":null}},"11":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"12":{"start":{"line":12,"column":0},"end":{"line":12,"column":13}},"13":{"start":{"line":22,"column":16},"end":{"line":22,"column":30}},"14":{"start":{"line":23,"column":16},"end":{"line":23,"column":36}},"15":{"start":{"line":24,"column":16},"end":{"line":24,"column":46}},"16":{"start":{"line":25,"column":16},"end":{"line":25,"column":48}},"17":{"start":{"line":29,"column":23},"end":{"line":29,"column":81}},"18":{"start":{"line":31,"column":28},"end":{"line":31,"column":86}},"19":{"start":{"line":32,"column":28},"end":{"line":32,"column":106}},"20":{"start":{"line":34,"column":8},"end":{"line":40,"column":null}},"21":{"start":{"line":44,"column":12},"end":{"line":44,"column":null}},"22":{"start":{"line":46,"column":12},"end":{"line":46,"column":null}},"23":{"start":{"line":50,"column":12},"end":{"line":50,"column":null}},"24":{"start":{"line":55,"column":16},"end":{"line":55,"column":null}},"25":{"start":{"line":57,"column":16},"end":{"line":57,"column":null}},"26":{"start":{"line":58,"column":16},"end":{"line":58,"column":null}},"27":{"start":{"line":59,"column":16},"end":{"line":59,"column":null}},"28":{"start":{"line":60,"column":16},"end":{"line":60,"column":null}},"29":{"start":{"line":61,"column":16},"end":{"line":61,"column":null}},"30":{"start":{"line":64,"column":38},"end":{"line":64,"column":100}},"31":{"start":{"line":66,"column":12},"end":{"line":66,"column":null}},"32":{"start":{"line":66,"column":50},"end":{"line":66,"column":null}},"33":{"start":{"line":68,"column":12},"end":{"line":72,"column":null}},"34":{"start":{"line":77,"column":40},"end":{"line":77,"column":74}},"35":{"start":{"line":78,"column":41},"end":{"line":78,"column":65}},"36":{"start":{"line":80,"column":35},"end":{"line":80,"column":87}},"37":{"start":{"line":83,"column":20},"end":{"line":83,"column":null}},"38":{"start":{"line":84,"column":20},"end":{"line":84,"column":null}},"39":{"start":{"line":88,"column":12},"end":{"line":92,"column":null}},"40":{"start":{"line":93,"column":45},"end":{"line":93,"column":107}},"41":{"start":{"line":95,"column":16},"end":{"line":95,"column":null}},"42":{"start":{"line":98,"column":12},"end":{"line":98,"column":null}},"43":{"start":{"line":100,"column":12},"end":{"line":104,"column":null}},"44":{"start":{"line":105,"column":12},"end":{"line":105,"column":null}},"45":{"start":{"line":111,"column":8},"end":{"line":113,"column":null}},"46":{"start":{"line":111,"column":60},"end":{"line":111,"column":125}},"47":{"start":{"line":112,"column":13},"end":{"line":113,"column":null}},"48":{"start":{"line":112,"column":49},"end":{"line":112,"column":117}},"49":{"start":{"line":113,"column":13},"end":{"line":113,"column":null}},"50":{"start":{"line":115,"column":39},"end":{"line":115,"column":99}},"51":{"start":{"line":118,"column":8},"end":{"line":118,"column":null}},"52":{"start":{"line":120,"column":8},"end":{"line":120,"column":null}},"53":{"start":{"line":124,"column":33},"end":{"line":124,"column":63}},"54":{"start":{"line":125,"column":29},"end":{"line":125,"column":61}},"55":{"start":{"line":127,"column":8},"end":{"line":127,"column":null}},"56":{"start":{"line":129,"column":12},"end":{"line":129,"column":null}},"57":{"start":{"line":131,"column":12},"end":{"line":131,"column":null}},"58":{"start":{"line":134,"column":8},"end":{"line":134,"column":null}},"59":{"start":{"line":138,"column":40},"end":{"line":138,"column":90}},"60":{"start":{"line":139,"column":28},"end":{"line":139,"column":57}},"61":{"start":{"line":144,"column":16},"end":{"line":144,"column":null}},"62":{"start":{"line":148,"column":8},"end":{"line":156,"column":null}},"63":{"start":{"line":149,"column":12},"end":{"line":149,"column":null}},"64":{"start":{"line":152,"column":12},"end":{"line":155,"column":null}},"65":{"start":{"line":153,"column":16},"end":{"line":153,"column":null}},"66":{"start":{"line":154,"column":16},"end":{"line":154,"column":null}},"67":{"start":{"line":155,"column":19},"end":{"line":155,"column":null}},"68":{"start":{"line":156,"column":15},"end":{"line":156,"column":null}},"69":{"start":{"line":161,"column":12},"end":{"line":161,"column":null}},"70":{"start":{"line":163,"column":12},"end":{"line":163,"column":null}},"71":{"start":{"line":20,"column":0},"end":{"line":20,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":12,"column":0},"end":{"line":12,"column":13}},"loc":{"start":{"line":12,"column":0},"end":{"line":18,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":21,"column":4},"end":{"line":21,"column":null}},"loc":{"start":{"line":25,"column":48},"end":{"line":26,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":28,"column":11},"end":{"line":28,"column":17}},"loc":{"start":{"line":28,"column":21},"end":{"line":107,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":109,"column":12},"end":{"line":109,"column":43}},"loc":{"start":{"line":109,"column":68},"end":{"line":121,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":123,"column":12},"end":{"line":123,"column":18}},"loc":{"start":{"line":123,"column":68},"end":{"line":135,"column":null}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":137,"column":12},"end":{"line":137,"column":18}},"loc":{"start":{"line":137,"column":96},"end":{"line":157,"column":null}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":159,"column":12},"end":{"line":159,"column":34}},"loc":{"start":{"line":159,"column":99},"end":{"line":165,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":43,"column":12},"end":{"line":43,"column":110}},"type":"binary-expr","locations":[{"start":{"line":43,"column":12},"end":{"line":43,"column":47}},{"start":{"line":43,"column":51},"end":{"line":43,"column":110}}]},"1":{"loc":{"start":{"line":66,"column":12},"end":{"line":66,"column":null}},"type":"if","locations":[{"start":{"line":66,"column":12},"end":{"line":66,"column":null}}]},"2":{"loc":{"start":{"line":111,"column":8},"end":{"line":113,"column":null}},"type":"if","locations":[{"start":{"line":111,"column":8},"end":{"line":113,"column":null}},{"start":{"line":112,"column":13},"end":{"line":113,"column":null}}]},"3":{"loc":{"start":{"line":112,"column":13},"end":{"line":113,"column":null}},"type":"if","locations":[{"start":{"line":112,"column":13},"end":{"line":113,"column":null}},{"start":{"line":113,"column":13},"end":{"line":113,"column":null}}]},"4":{"loc":{"start":{"line":148,"column":8},"end":{"line":156,"column":null}},"type":"if","locations":[{"start":{"line":148,"column":8},"end":{"line":156,"column":null}},{"start":{"line":156,"column":15},"end":{"line":156,"column":null}}]},"5":{"loc":{"start":{"line":152,"column":12},"end":{"line":155,"column":null}},"type":"if","locations":[{"start":{"line":152,"column":12},"end":{"line":155,"column":null}},{"start":{"line":155,"column":19},"end":{"line":155,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":9,"14":9,"15":9,"16":9,"17":9,"18":9,"19":9,"20":8,"21":0,"22":8,"23":6,"24":6,"25":0,"26":0,"27":0,"28":0,"29":0,"30":6,"31":6,"32":6,"33":6,"34":7,"35":7,"36":7,"37":1,"38":1,"39":5,"40":5,"41":1,"42":4,"43":2,"44":2,"45":6,"46":0,"47":6,"48":0,"49":6,"50":6,"51":6,"52":6,"53":8,"54":8,"55":8,"56":0,"57":8,"58":8,"59":5,"60":5,"61":5,"62":5,"63":2,"64":2,"65":1,"66":1,"67":1,"68":3,"69":0,"70":0,"71":1},"f":{"0":1,"1":9,"2":9,"3":6,"4":8,"5":5,"6":0},"b":{"0":[8,7],"1":[6],"2":[0,6],"3":[0,6],"4":[2,3],"5":[1,1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateDataPackageImpl.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateDataPackageImpl.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"5":{"start":{"line":8,"column":21},"end":{"line":8,"column":34}},"6":{"start":{"line":9,"column":23},"end":{"line":9,"column":47}},"7":{"start":{"line":19,"column":8},"end":{"line":19,"column":null}},"8":{"start":{"line":13,"column":18},"end":{"line":13,"column":42}},"9":{"start":{"line":14,"column":18},"end":{"line":14,"column":40}},"10":{"start":{"line":15,"column":18},"end":{"line":15,"column":62}},"11":{"start":{"line":16,"column":18},"end":{"line":16,"column":33}},"12":{"start":{"line":17,"column":18},"end":{"line":17,"column":43}},"13":{"start":{"line":23,"column":8},"end":{"line":23,"column":null}},"14":{"start":{"line":27,"column":30},"end":{"line":27,"column":104}},"15":{"start":{"line":29,"column":28},"end":{"line":29,"column":87}},"16":{"start":{"line":29,"column":49},"end":{"line":29,"column":86}},"17":{"start":{"line":31,"column":25},"end":{"line":31,"column":76}},"18":{"start":{"line":31,"column":46},"end":{"line":31,"column":75}},"19":{"start":{"line":33,"column":26},"end":{"line":33,"column":78}},"20":{"start":{"line":33,"column":47},"end":{"line":33,"column":77}},"21":{"start":{"line":35,"column":8},"end":{"line":35,"column":null}},"22":{"start":{"line":35,"column":24},"end":{"line":35,"column":null}},"23":{"start":{"line":37,"column":8},"end":{"line":38,"column":null}},"24":{"start":{"line":37,"column":43},"end":{"line":37,"column":55}},"25":{"start":{"line":38,"column":13},"end":{"line":38,"column":null}},"26":{"start":{"line":42,"column":8},"end":{"line":42,"column":null}},"27":{"start":{"line":55,"column":22},"end":{"line":55,"column":67}},"28":{"start":{"line":60,"column":12},"end":{"line":60,"column":null}},"29":{"start":{"line":60,"column":54},"end":{"line":60,"column":null}},"30":{"start":{"line":61,"column":12},"end":{"line":61,"column":null}},"31":{"start":{"line":61,"column":56},"end":{"line":61,"column":null}},"32":{"start":{"line":65,"column":12},"end":{"line":67,"column":null}},"33":{"start":{"line":69,"column":12},"end":{"line":73,"column":null}},"34":{"start":{"line":75,"column":12},"end":{"line":79,"column":null}},"35":{"start":{"line":81,"column":12},"end":{"line":83,"column":null}},"36":{"start":{"line":11,"column":0},"end":{"line":11,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":null}},"loc":{"start":{"line":17,"column":43},"end":{"line":20,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":22,"column":4},"end":{"line":22,"column":20}},"loc":{"start":{"line":22,"column":20},"end":{"line":24,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":26,"column":4},"end":{"line":26,"column":18}},"loc":{"start":{"line":26,"column":69},"end":{"line":39,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":29,"column":40},"end":{"line":29,"column":44}},"loc":{"start":{"line":29,"column":49},"end":{"line":29,"column":86}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":31,"column":37},"end":{"line":31,"column":41}},"loc":{"start":{"line":31,"column":46},"end":{"line":31,"column":75}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":33,"column":38},"end":{"line":33,"column":42}},"loc":{"start":{"line":33,"column":47},"end":{"line":33,"column":77}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":41,"column":4},"end":{"line":41,"column":20}},"loc":{"start":{"line":41,"column":31},"end":{"line":43,"column":null}}},"7":{"name":"(anonymous_14)","decl":{"start":{"line":45,"column":4},"end":{"line":45,"column":17}},"loc":{"start":{"line":45,"column":40},"end":{"line":46,"column":null}}},"8":{"name":"(anonymous_15)","decl":{"start":{"line":49,"column":4},"end":{"line":49,"column":21}},"loc":{"start":{"line":49,"column":44},"end":{"line":49,"column":null}}},"9":{"name":"(anonymous_16)","decl":{"start":{"line":51,"column":4},"end":{"line":51,"column":41}},"loc":{"start":{"line":51,"column":41},"end":{"line":51,"column":null}}},"10":{"name":"(anonymous_17)","decl":{"start":{"line":54,"column":12},"end":{"line":54,"column":31}},"loc":{"start":{"line":54,"column":56},"end":{"line":85,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":35,"column":8},"end":{"line":35,"column":null}},"type":"if","locations":[{"start":{"line":35,"column":8},"end":{"line":35,"column":null}}]},"1":{"loc":{"start":{"line":37,"column":8},"end":{"line":38,"column":null}},"type":"if","locations":[{"start":{"line":37,"column":8},"end":{"line":38,"column":null}},{"start":{"line":38,"column":13},"end":{"line":38,"column":null}}]},"2":{"loc":{"start":{"line":37,"column":12},"end":{"line":37,"column":41}},"type":"binary-expr","locations":[{"start":{"line":37,"column":12},"end":{"line":37,"column":26}},{"start":{"line":37,"column":30},"end":{"line":37,"column":41}}]},"3":{"loc":{"start":{"line":60,"column":12},"end":{"line":60,"column":null}},"type":"if","locations":[{"start":{"line":60,"column":12},"end":{"line":60,"column":null}}]},"4":{"loc":{"start":{"line":61,"column":12},"end":{"line":61,"column":null}},"type":"if","locations":[{"start":{"line":61,"column":12},"end":{"line":61,"column":null}}]},"5":{"loc":{"start":{"line":64,"column":12},"end":{"line":64,"column":32}},"type":"binary-expr","locations":[{"start":{"line":64,"column":12},"end":{"line":64,"column":19}},{"start":{"line":64,"column":23},"end":{"line":64,"column":32}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0},"b":{"0":[0],"1":[0,0],"2":[0,0],"3":[0],"4":[0],"5":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateDiffPackageImpl.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateDiffPackageImpl.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"4":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"5":{"start":{"line":8,"column":0},"end":{"line":8,"column":null}},"6":{"start":{"line":9,"column":0},"end":{"line":9,"column":null}},"7":{"start":{"line":10,"column":0},"end":{"line":10,"column":null}},"8":{"start":{"line":11,"column":0},"end":{"line":11,"column":null}},"9":{"start":{"line":12,"column":0},"end":{"line":12,"column":null}},"10":{"start":{"line":13,"column":0},"end":{"line":13,"column":null}},"11":{"start":{"line":14,"column":0},"end":{"line":14,"column":null}},"12":{"start":{"line":15,"column":0},"end":{"line":15,"column":null}},"13":{"start":{"line":16,"column":0},"end":{"line":16,"column":null}},"14":{"start":{"line":18,"column":0},"end":{"line":18,"column":null}},"15":{"start":{"line":28,"column":8},"end":{"line":28,"column":null}},"16":{"start":{"line":22,"column":18},"end":{"line":22,"column":42}},"17":{"start":{"line":23,"column":18},"end":{"line":23,"column":40}},"18":{"start":{"line":24,"column":18},"end":{"line":24,"column":62}},"19":{"start":{"line":25,"column":18},"end":{"line":25,"column":33}},"20":{"start":{"line":26,"column":18},"end":{"line":26,"column":43}},"21":{"start":{"line":32,"column":8},"end":{"line":32,"column":null}},"22":{"start":{"line":38,"column":8},"end":{"line":38,"column":null}},"23":{"start":{"line":42,"column":26},"end":{"line":42,"column":101}},"24":{"start":{"line":45,"column":49},"end":{"line":45,"column":108}},"25":{"start":{"line":48,"column":12},"end":{"line":48,"column":null}},"26":{"start":{"line":50,"column":12},"end":{"line":50,"column":null}},"27":{"start":{"line":52,"column":12},"end":{"line":52,"column":null}},"28":{"start":{"line":56,"column":12},"end":{"line":56,"column":null}},"29":{"start":{"line":58,"column":12},"end":{"line":58,"column":null}},"30":{"start":{"line":63,"column":33},"end":{"line":63,"column":79}},"31":{"start":{"line":64,"column":52},"end":{"line":64,"column":119}},"32":{"start":{"line":65,"column":8},"end":{"line":65,"column":null}},"33":{"start":{"line":72,"column":67},"end":{"line":77,"column":null}},"34":{"start":{"line":79,"column":16},"end":{"line":79,"column":null}},"35":{"start":{"line":82,"column":16},"end":{"line":91,"column":null}},"36":{"start":{"line":83,"column":20},"end":{"line":87,"column":null}},"37":{"start":{"line":89,"column":39},"end":{"line":89,"column":116}},"38":{"start":{"line":90,"column":20},"end":{"line":90,"column":null}},"39":{"start":{"line":91,"column":23},"end":{"line":91,"column":null}},"40":{"start":{"line":94,"column":12},"end":{"line":94,"column":null}},"41":{"start":{"line":96,"column":12},"end":{"line":100,"column":null}},"42":{"start":{"line":102,"column":12},"end":{"line":105,"column":null}},"43":{"start":{"line":116,"column":23},"end":{"line":116,"column":68}},"44":{"start":{"line":117,"column":27},"end":{"line":117,"column":76}},"45":{"start":{"line":120,"column":8},"end":{"line":120,"column":null}},"46":{"start":{"line":120,"column":39},"end":{"line":120,"column":null}},"47":{"start":{"line":123,"column":43},"end":{"line":123,"column":77}},"48":{"start":{"line":126,"column":12},"end":{"line":126,"column":null}},"49":{"start":{"line":127,"column":12},"end":{"line":127,"column":null}},"50":{"start":{"line":131,"column":39},"end":{"line":131,"column":100}},"51":{"start":{"line":134,"column":12},"end":{"line":134,"column":null}},"52":{"start":{"line":137,"column":8},"end":{"line":138,"column":null}},"53":{"start":{"line":138,"column":12},"end":{"line":138,"column":null}},"54":{"start":{"line":142,"column":73},"end":{"line":142,"column":75}},"55":{"start":{"line":143,"column":8},"end":{"line":143,"column":null}},"56":{"start":{"line":145,"column":8},"end":{"line":145,"column":null}},"57":{"start":{"line":148,"column":63},"end":{"line":148,"column":65}},"58":{"start":{"line":149,"column":32},"end":{"line":149,"column":34}},"59":{"start":{"line":154,"column":33},"end":{"line":154,"column":87}},"60":{"start":{"line":155,"column":16},"end":{"line":155,"column":null}},"61":{"start":{"line":157,"column":16},"end":{"line":157,"column":null}},"62":{"start":{"line":162,"column":16},"end":{"line":164,"column":null}},"63":{"start":{"line":163,"column":20},"end":{"line":163,"column":null}},"64":{"start":{"line":168,"column":12},"end":{"line":172,"column":null}},"65":{"start":{"line":170,"column":20},"end":{"line":170,"column":null}},"66":{"start":{"line":174,"column":12},"end":{"line":174,"column":null}},"67":{"start":{"line":174,"column":49},"end":{"line":174,"column":null}},"68":{"start":{"line":176,"column":12},"end":{"line":176,"column":null}},"69":{"start":{"line":185,"column":31},"end":{"line":185,"column":77}},"70":{"start":{"line":187,"column":36},"end":{"line":190,"column":34}},"71":{"start":{"line":192,"column":77},"end":{"line":195,"column":null}},"72":{"start":{"line":197,"column":38},"end":{"line":197,"column":97}},"73":{"start":{"line":200,"column":41},"end":{"line":204,"column":null}},"74":{"start":{"line":207,"column":31},"end":{"line":207,"column":83}},"75":{"start":{"line":209,"column":53},"end":{"line":209,"column":95}},"76":{"start":{"line":211,"column":12},"end":{"line":211,"column":null}},"77":{"start":{"line":212,"column":12},"end":{"line":212,"column":null}},"78":{"start":{"line":213,"column":12},"end":{"line":216,"column":null}},"79":{"start":{"line":217,"column":12},"end":{"line":217,"column":null}},"80":{"start":{"line":218,"column":12},"end":{"line":218,"column":null}},"81":{"start":{"line":219,"column":12},"end":{"line":219,"column":null}},"82":{"start":{"line":220,"column":12},"end":{"line":220,"column":null}},"83":{"start":{"line":222,"column":12},"end":{"line":225,"column":null}},"84":{"start":{"line":226,"column":12},"end":{"line":226,"column":null}},"85":{"start":{"line":229,"column":12},"end":{"line":229,"column":null}},"86":{"start":{"line":230,"column":12},"end":{"line":230,"column":null}},"87":{"start":{"line":231,"column":12},"end":{"line":231,"column":null}},"88":{"start":{"line":232,"column":12},"end":{"line":232,"column":null}},"89":{"start":{"line":233,"column":12},"end":{"line":233,"column":null}},"90":{"start":{"line":234,"column":12},"end":{"line":234,"column":null}},"91":{"start":{"line":235,"column":12},"end":{"line":235,"column":null}},"92":{"start":{"line":236,"column":12},"end":{"line":236,"column":null}},"93":{"start":{"line":245,"column":16},"end":{"line":245,"column":null}},"94":{"start":{"line":250,"column":16},"end":{"line":250,"column":null}},"95":{"start":{"line":254,"column":33},"end":{"line":254,"column":86}},"96":{"start":{"line":254,"column":77},"end":{"line":254,"column":85}},"97":{"start":{"line":255,"column":29},"end":{"line":255,"column":86}},"98":{"start":{"line":255,"column":77},"end":{"line":255,"column":85}},"99":{"start":{"line":256,"column":39},"end":{"line":256,"column":96}},"100":{"start":{"line":256,"column":87},"end":{"line":256,"column":95}},"101":{"start":{"line":257,"column":12},"end":{"line":257,"column":null}},"102":{"start":{"line":257,"column":61},"end":{"line":257,"column":95}},"103":{"start":{"line":258,"column":12},"end":{"line":258,"column":null}},"104":{"start":{"line":258,"column":61},"end":{"line":258,"column":87}},"105":{"start":{"line":261,"column":39},"end":{"line":262,"column":null}},"106":{"start":{"line":262,"column":31},"end":{"line":262,"column":108}},"107":{"start":{"line":266,"column":43},"end":{"line":266,"column":100}},"108":{"start":{"line":266,"column":81},"end":{"line":266,"column":99}},"109":{"start":{"line":268,"column":12},"end":{"line":268,"column":null}},"110":{"start":{"line":20,"column":0},"end":{"line":20,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":21,"column":4},"end":{"line":21,"column":null}},"loc":{"start":{"line":26,"column":43},"end":{"line":29,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":31,"column":4},"end":{"line":31,"column":20}},"loc":{"start":{"line":31,"column":20},"end":{"line":33,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":35,"column":4},"end":{"line":35,"column":41}},"loc":{"start":{"line":35,"column":41},"end":{"line":35,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":37,"column":4},"end":{"line":37,"column":18}},"loc":{"start":{"line":37,"column":69},"end":{"line":39,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":41,"column":4},"end":{"line":41,"column":10}},"loc":{"start":{"line":41,"column":49},"end":{"line":60,"column":null}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":62,"column":12},"end":{"line":62,"column":18}},"loc":{"start":{"line":62,"column":80},"end":{"line":66,"column":null}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":68,"column":11},"end":{"line":68,"column":17}},"loc":{"start":{"line":68,"column":53},"end":{"line":107,"column":null}}},"7":{"name":"(anonymous_14)","decl":{"start":{"line":109,"column":4},"end":{"line":109,"column":21}},"loc":{"start":{"line":109,"column":32},"end":{"line":109,"column":null}}},"8":{"name":"(anonymous_15)","decl":{"start":{"line":111,"column":12},"end":{"line":111,"column":18}},"loc":{"start":{"line":114,"column":36},"end":{"line":139,"column":null}}},"9":{"name":"(anonymous_16)","decl":{"start":{"line":141,"column":4},"end":{"line":141,"column":10}},"loc":{"start":{"line":141,"column":70},"end":{"line":178,"column":null}}},"10":{"name":"getPackagesToCommits","decl":{"start":{"line":147,"column":23},"end":{"line":147,"column":43}},"loc":{"start":{"line":147,"column":67},"end":{"line":177,"column":null}}},"11":{"name":"(anonymous_18)","decl":{"start":{"line":162,"column":44},"end":{"line":162,"column":52}},"loc":{"start":{"line":162,"column":56},"end":{"line":164,"column":17}}},"12":{"name":"(anonymous_19)","decl":{"start":{"line":168,"column":48},"end":{"line":168,"column":55}},"loc":{"start":{"line":168,"column":59},"end":{"line":172,"column":13}}},"13":{"name":"(anonymous_20)","decl":{"start":{"line":180,"column":12},"end":{"line":180,"column":18}},"loc":{"start":{"line":183,"column":22},"end":{"line":270,"column":null}}},"14":{"name":"getOnlyChangedClassesFromPackage","decl":{"start":{"line":239,"column":17},"end":{"line":239,"column":49}},"loc":{"start":{"line":241,"column":54},"end":{"line":269,"column":null}}},"15":{"name":"(anonymous_22)","decl":{"start":{"line":254,"column":69},"end":{"line":254,"column":72}},"loc":{"start":{"line":254,"column":77},"end":{"line":254,"column":85}}},"16":{"name":"(anonymous_23)","decl":{"start":{"line":255,"column":69},"end":{"line":255,"column":72}},"loc":{"start":{"line":255,"column":77},"end":{"line":255,"column":85}}},"17":{"name":"(anonymous_24)","decl":{"start":{"line":256,"column":79},"end":{"line":256,"column":82}},"loc":{"start":{"line":256,"column":87},"end":{"line":256,"column":95}}},"18":{"name":"(anonymous_25)","decl":{"start":{"line":257,"column":52},"end":{"line":257,"column":56}},"loc":{"start":{"line":257,"column":61},"end":{"line":257,"column":95}}},"19":{"name":"(anonymous_26)","decl":{"start":{"line":258,"column":52},"end":{"line":258,"column":56}},"loc":{"start":{"line":258,"column":61},"end":{"line":258,"column":87}}},"20":{"name":"(anonymous_27)","decl":{"start":{"line":262,"column":17},"end":{"line":262,"column":26}},"loc":{"start":{"line":262,"column":31},"end":{"line":262,"column":108}}},"21":{"name":"(anonymous_28)","decl":{"start":{"line":266,"column":67},"end":{"line":266,"column":76}},"loc":{"start":{"line":266,"column":81},"end":{"line":266,"column":99}}}},"branchMap":{"0":{"loc":{"start":{"line":82,"column":16},"end":{"line":91,"column":null}},"type":"if","locations":[{"start":{"line":82,"column":16},"end":{"line":91,"column":null}},{"start":{"line":91,"column":23},"end":{"line":91,"column":null}}]},"1":{"loc":{"start":{"line":120,"column":8},"end":{"line":120,"column":null}},"type":"if","locations":[{"start":{"line":120,"column":8},"end":{"line":120,"column":null}}]},"2":{"loc":{"start":{"line":137,"column":8},"end":{"line":138,"column":null}},"type":"if","locations":[{"start":{"line":137,"column":8},"end":{"line":138,"column":null}}]},"3":{"loc":{"start":{"line":174,"column":12},"end":{"line":174,"column":null}},"type":"if","locations":[{"start":{"line":174,"column":12},"end":{"line":174,"column":null}}]},"4":{"loc":{"start":{"line":244,"column":16},"end":{"line":244,"column":63}},"type":"binary-expr","locations":[{"start":{"line":244,"column":16},"end":{"line":244,"column":34}},{"start":{"line":244,"column":38},"end":{"line":244,"column":63}}]},"5":{"loc":{"start":{"line":262,"column":31},"end":{"line":262,"column":108}},"type":"binary-expr","locations":[{"start":{"line":262,"column":31},"end":{"line":262,"column":74}},{"start":{"line":262,"column":78},"end":{"line":262,"column":108}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"109":0,"110":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0},"b":{"0":[0,0],"1":[0],"2":[0],"3":[0],"4":[0,0],"5":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreatePackage.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreatePackage.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":10,"column":18},"end":{"line":10,"column":42}},"4":{"start":{"line":11,"column":18},"end":{"line":11,"column":40}},"5":{"start":{"line":12,"column":18},"end":{"line":12,"column":63}},"6":{"start":{"line":13,"column":18},"end":{"line":13,"column":33}},"7":{"start":{"line":14,"column":18},"end":{"line":14,"column":43}},"8":{"start":{"line":17,"column":8},"end":{"line":17,"column":null}},"9":{"start":{"line":17,"column":33},"end":{"line":17,"column":null}},"10":{"start":{"line":22,"column":8},"end":{"line":22,"column":null}},"11":{"start":{"line":25,"column":8},"end":{"line":25,"column":null}},"12":{"start":{"line":28,"column":8},"end":{"line":28,"column":null}},"13":{"start":{"line":30,"column":8},"end":{"line":30,"column":null}},"14":{"start":{"line":31,"column":8},"end":{"line":31,"column":null}},"15":{"start":{"line":32,"column":8},"end":{"line":32,"column":null}},"16":{"start":{"line":35,"column":8},"end":{"line":35,"column":null}},"17":{"start":{"line":38,"column":8},"end":{"line":38,"column":null}},"18":{"start":{"line":40,"column":8},"end":{"line":40,"column":null}},"19":{"start":{"line":50,"column":26},"end":{"line":50,"column":53}},"20":{"start":{"line":52,"column":8},"end":{"line":55,"column":null}},"21":{"start":{"line":57,"column":8},"end":{"line":61,"column":null}},"22":{"start":{"line":58,"column":12},"end":{"line":61,"column":null}},"23":{"start":{"line":63,"column":8},"end":{"line":67,"column":null}},"24":{"start":{"line":69,"column":8},"end":{"line":73,"column":null}},"25":{"start":{"line":74,"column":8},"end":{"line":78,"column":null}},"26":{"start":{"line":83,"column":12},"end":{"line":85,"column":null}},"27":{"start":{"line":84,"column":16},"end":{"line":84,"column":108}},"28":{"start":{"line":85,"column":17},"end":{"line":85,"column":null}},"29":{"start":{"line":89,"column":12},"end":{"line":91,"column":null}},"30":{"start":{"line":90,"column":16},"end":{"line":90,"column":110}},"31":{"start":{"line":91,"column":17},"end":{"line":91,"column":null}},"32":{"start":{"line":97,"column":12},"end":{"line":99,"column":null}},"33":{"start":{"line":98,"column":16},"end":{"line":98,"column":82}},"34":{"start":{"line":99,"column":17},"end":{"line":99,"column":null}},"35":{"start":{"line":106,"column":8},"end":{"line":111,"column":null}},"36":{"start":{"line":112,"column":8},"end":{"line":116,"column":null}},"37":{"start":{"line":117,"column":8},"end":{"line":117,"column":null}},"38":{"start":{"line":118,"column":8},"end":{"line":118,"column":null}},"39":{"start":{"line":122,"column":8},"end":{"line":122,"column":null}},"40":{"start":{"line":123,"column":8},"end":{"line":127,"column":null}},"41":{"start":{"line":128,"column":8},"end":{"line":132,"column":null}},"42":{"start":{"line":134,"column":8},"end":{"line":138,"column":null}},"43":{"start":{"line":140,"column":8},"end":{"line":140,"column":null}},"44":{"start":{"line":142,"column":8},"end":{"line":142,"column":null}},"45":{"start":{"line":6,"column":0},"end":{"line":6,"column":22}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":9,"column":4},"end":{"line":9,"column":null}},"loc":{"start":{"line":14,"column":43},"end":{"line":18,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":20,"column":11},"end":{"line":20,"column":17}},"loc":{"start":{"line":20,"column":21},"end":{"line":41,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":49,"column":12},"end":{"line":49,"column":46}},"loc":{"start":{"line":49,"column":46},"end":{"line":79,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":81,"column":12},"end":{"line":81,"column":42}},"loc":{"start":{"line":81,"column":65},"end":{"line":93,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":95,"column":12},"end":{"line":95,"column":18}},"loc":{"start":{"line":95,"column":77},"end":{"line":101,"column":null}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":105,"column":14},"end":{"line":105,"column":39}},"loc":{"start":{"line":105,"column":39},"end":{"line":119,"column":null}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":121,"column":12},"end":{"line":121,"column":23}},"loc":{"start":{"line":121,"column":23},"end":{"line":143,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":17,"column":8},"end":{"line":17,"column":null}},"type":"if","locations":[{"start":{"line":17,"column":8},"end":{"line":17,"column":null}}]},"1":{"loc":{"start":{"line":57,"column":8},"end":{"line":61,"column":null}},"type":"if","locations":[{"start":{"line":57,"column":8},"end":{"line":61,"column":null}}]},"2":{"loc":{"start":{"line":57,"column":12},"end":{"line":57,"column":110}},"type":"binary-expr","locations":[{"start":{"line":57,"column":12},"end":{"line":57,"column":58}},{"start":{"line":57,"column":62},"end":{"line":57,"column":110}}]},"3":{"loc":{"start":{"line":83,"column":12},"end":{"line":85,"column":null}},"type":"if","locations":[{"start":{"line":83,"column":12},"end":{"line":85,"column":null}},{"start":{"line":85,"column":17},"end":{"line":85,"column":null}}]},"4":{"loc":{"start":{"line":89,"column":12},"end":{"line":91,"column":null}},"type":"if","locations":[{"start":{"line":89,"column":12},"end":{"line":91,"column":null}},{"start":{"line":91,"column":17},"end":{"line":91,"column":null}}]},"5":{"loc":{"start":{"line":97,"column":12},"end":{"line":99,"column":null}},"type":"if","locations":[{"start":{"line":97,"column":12},"end":{"line":99,"column":null}},{"start":{"line":99,"column":17},"end":{"line":99,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0},"b":{"0":[0],"1":[0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/AssignPermissionSetFetcher.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/AssignPermissionSetFetcher.ts","statementMap":{"0":{"start":{"line":8,"column":12},"end":{"line":11,"column":null}},"1":{"start":{"line":9,"column":16},"end":{"line":10,"column":null}},"2":{"start":{"line":11,"column":19},"end":{"line":11,"column":null}},"3":{"start":{"line":15,"column":12},"end":{"line":18,"column":null}},"4":{"start":{"line":16,"column":16},"end":{"line":17,"column":null}},"5":{"start":{"line":18,"column":19},"end":{"line":18,"column":null}},"6":{"start":{"line":21,"column":8},"end":{"line":21,"column":null}},"7":{"start":{"line":5,"column":0},"end":{"line":5,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":11},"end":{"line":6,"column":27}},"loc":{"start":{"line":6,"column":79},"end":{"line":22,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":8,"column":12},"end":{"line":11,"column":null}},"type":"if","locations":[{"start":{"line":8,"column":12},"end":{"line":11,"column":null}},{"start":{"line":11,"column":19},"end":{"line":11,"column":null}}]},"1":{"loc":{"start":{"line":15,"column":12},"end":{"line":18,"column":null}},"type":"if","locations":[{"start":{"line":15,"column":12},"end":{"line":18,"column":null}},{"start":{"line":18,"column":19},"end":{"line":18,"column":null}}]}},"s":{"0":4,"1":4,"2":0,"3":4,"4":4,"5":0,"6":4,"7":2},"f":{"0":4},"b":{"0":[4,0],"1":[4,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/DestructiveManifestPathFetcher.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/DestructiveManifestPathFetcher.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"2":{"start":{"line":12,"column":12},"end":{"line":12,"column":null}},"3":{"start":{"line":16,"column":12},"end":{"line":16,"column":null}},"4":{"start":{"line":17,"column":12},"end":{"line":17,"column":null}},"5":{"start":{"line":22,"column":16},"end":{"line":22,"column":null}},"6":{"start":{"line":25,"column":12},"end":{"line":25,"column":null}},"7":{"start":{"line":27,"column":8},"end":{"line":27,"column":null}},"8":{"start":{"line":7,"column":0},"end":{"line":7,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":8,"column":11},"end":{"line":8,"column":17}},"loc":{"start":{"line":8,"column":85},"end":{"line":28,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":11,"column":12},"end":{"line":11,"column":105}},"type":"binary-expr","locations":[{"start":{"line":11,"column":12},"end":{"line":11,"column":54}},{"start":{"line":11,"column":58},"end":{"line":11,"column":105}}]}},"s":{"0":2,"1":2,"2":0,"3":4,"4":4,"5":4,"6":0,"7":4,"8":2},"f":{"0":4},"b":{"0":[4,4]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.ts","statementMap":{"0":{"start":{"line":7,"column":12},"end":{"line":7,"column":null}},"1":{"start":{"line":4,"column":0},"end":{"line":4,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":4},"end":{"line":5,"column":20}},"loc":{"start":{"line":5,"column":69},"end":{"line":9,"column":null}}}},"branchMap":{},"s":{"0":2,"1":2},"f":{"0":2},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/validators/PackageEmptyChecker.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/validators/PackageEmptyChecker.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":16,"column":58},"end":{"line":19,"column":null}},"5":{"start":{"line":21,"column":8},"end":{"line":23,"column":null}},"6":{"start":{"line":22,"column":12},"end":{"line":22,"column":null}},"7":{"start":{"line":23,"column":15},"end":{"line":23,"column":null}},"8":{"start":{"line":29,"column":20},"end":{"line":29,"column":null}},"9":{"start":{"line":30,"column":20},"end":{"line":30,"column":null}},"10":{"start":{"line":32,"column":20},"end":{"line":32,"column":null}},"11":{"start":{"line":33,"column":20},"end":{"line":33,"column":null}},"12":{"start":{"line":35,"column":16},"end":{"line":35,"column":null}},"13":{"start":{"line":38,"column":20},"end":{"line":38,"column":null}},"14":{"start":{"line":39,"column":20},"end":{"line":39,"column":null}},"15":{"start":{"line":41,"column":20},"end":{"line":41,"column":null}},"16":{"start":{"line":42,"column":20},"end":{"line":42,"column":null}},"17":{"start":{"line":44,"column":16},"end":{"line":44,"column":null}},"18":{"start":{"line":46,"column":16},"end":{"line":46,"column":null}},"19":{"start":{"line":47,"column":16},"end":{"line":47,"column":null}},"20":{"start":{"line":50,"column":12},"end":{"line":56,"column":null}},"21":{"start":{"line":51,"column":16},"end":{"line":51,"column":73}},"22":{"start":{"line":52,"column":19},"end":{"line":56,"column":null}},"23":{"start":{"line":53,"column":16},"end":{"line":53,"column":null}},"24":{"start":{"line":54,"column":16},"end":{"line":54,"column":null}},"25":{"start":{"line":55,"column":16},"end":{"line":55,"column":null}},"26":{"start":{"line":56,"column":19},"end":{"line":56,"column":null}},"27":{"start":{"line":64,"column":12},"end":{"line":64,"column":null}},"28":{"start":{"line":66,"column":12},"end":{"line":66,"column":null}},"29":{"start":{"line":69,"column":30},"end":{"line":69,"column":83}},"30":{"start":{"line":71,"column":8},"end":{"line":71,"column":null}},"31":{"start":{"line":71,"column":36},"end":{"line":71,"column":68}},"32":{"start":{"line":74,"column":8},"end":{"line":75,"column":null}},"33":{"start":{"line":74,"column":38},"end":{"line":74,"column":100}},"34":{"start":{"line":75,"column":13},"end":{"line":75,"column":null}},"35":{"start":{"line":78,"column":8},"end":{"line":80,"column":null}},"36":{"start":{"line":82,"column":8},"end":{"line":83,"column":null}},"37":{"start":{"line":82,"column":49},"end":{"line":82,"column":61}},"38":{"start":{"line":83,"column":13},"end":{"line":83,"column":null}},"39":{"start":{"line":6,"column":0},"end":{"line":6,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":7,"column":11},"end":{"line":7,"column":18}},"loc":{"start":{"line":10,"column":38},"end":{"line":58,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":60,"column":11},"end":{"line":60,"column":18}},"loc":{"start":{"line":60,"column":81},"end":{"line":84,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":71,"column":27},"end":{"line":71,"column":31}},"loc":{"start":{"line":71,"column":36},"end":{"line":71,"column":68}}}},"branchMap":{"0":{"loc":{"start":{"line":21,"column":8},"end":{"line":23,"column":null}},"type":"if","locations":[{"start":{"line":21,"column":8},"end":{"line":23,"column":null}},{"start":{"line":23,"column":15},"end":{"line":23,"column":null}}]},"1":{"loc":{"start":{"line":50,"column":12},"end":{"line":56,"column":null}},"type":"if","locations":[{"start":{"line":50,"column":12},"end":{"line":56,"column":null}},{"start":{"line":52,"column":19},"end":{"line":56,"column":null}}]},"2":{"loc":{"start":{"line":52,"column":19},"end":{"line":56,"column":null}},"type":"if","locations":[{"start":{"line":52,"column":19},"end":{"line":56,"column":null}},{"start":{"line":56,"column":19},"end":{"line":56,"column":null}}]},"3":{"loc":{"start":{"line":74,"column":8},"end":{"line":75,"column":null}},"type":"if","locations":[{"start":{"line":74,"column":8},"end":{"line":75,"column":null}},{"start":{"line":75,"column":13},"end":{"line":75,"column":null}}]},"4":{"loc":{"start":{"line":82,"column":8},"end":{"line":83,"column":null}},"type":"if","locations":[{"start":{"line":82,"column":8},"end":{"line":83,"column":null}},{"start":{"line":83,"column":13},"end":{"line":83,"column":null}}]},"5":{"loc":{"start":{"line":82,"column":12},"end":{"line":82,"column":47}},"type":"binary-expr","locations":[{"start":{"line":82,"column":12},"end":{"line":82,"column":25}},{"start":{"line":82,"column":29},"end":{"line":82,"column":47}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":1},"f":{"0":0,"1":0,"2":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/version/Package2VersionFetcher.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/version/Package2VersionFetcher.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":12,"column":24},"end":{"line":12,"column":40}},"3":{"start":{"line":9,"column":21},"end":{"line":10,"column":null}},"4":{"start":{"line":27,"column":20},"end":{"line":27,"column":30}},"5":{"start":{"line":29,"column":34},"end":{"line":29,"column":70}},"6":{"start":{"line":33,"column":29},"end":{"line":33,"column":53}},"7":{"start":{"line":35,"column":12},"end":{"line":35,"column":null}},"8":{"start":{"line":35,"column":29},"end":{"line":35,"column":null}},"9":{"start":{"line":36,"column":12},"end":{"line":36,"column":null}},"10":{"start":{"line":36,"column":29},"end":{"line":36,"column":null}},"11":{"start":{"line":37,"column":12},"end":{"line":37,"column":null}},"12":{"start":{"line":37,"column":29},"end":{"line":37,"column":null}},"13":{"start":{"line":38,"column":12},"end":{"line":38,"column":null}},"14":{"start":{"line":38,"column":29},"end":{"line":38,"column":null}},"15":{"start":{"line":41,"column":8},"end":{"line":41,"column":null}},"16":{"start":{"line":41,"column":33},"end":{"line":41,"column":null}},"17":{"start":{"line":43,"column":8},"end":{"line":43,"column":null}},"18":{"start":{"line":44,"column":8},"end":{"line":44,"column":null}},"19":{"start":{"line":47,"column":24},"end":{"line":47,"column":88}},"20":{"start":{"line":50,"column":8},"end":{"line":56,"column":null}},"21":{"start":{"line":51,"column":12},"end":{"line":55,"column":null}},"22":{"start":{"line":52,"column":27},"end":{"line":52,"column":99}},"23":{"start":{"line":53,"column":27},"end":{"line":53,"column":99}},"24":{"start":{"line":54,"column":16},"end":{"line":54,"column":null}},"25":{"start":{"line":56,"column":15},"end":{"line":56,"column":null}},"26":{"start":{"line":60,"column":20},"end":{"line":60,"column":30}},"27":{"start":{"line":62,"column":34},"end":{"line":62,"column":100}},"28":{"start":{"line":63,"column":8},"end":{"line":63,"column":null}},"29":{"start":{"line":65,"column":24},"end":{"line":65,"column":88}},"30":{"start":{"line":66,"column":8},"end":{"line":66,"column":null}},"31":{"start":{"line":75,"column":20},"end":{"line":75,"column":30}},"32":{"start":{"line":77,"column":34},"end":{"line":77,"column":104}},"33":{"start":{"line":80,"column":29},"end":{"line":80,"column":53}},"34":{"start":{"line":81,"column":12},"end":{"line":81,"column":null}},"35":{"start":{"line":81,"column":29},"end":{"line":81,"column":null}},"36":{"start":{"line":82,"column":12},"end":{"line":82,"column":null}},"37":{"start":{"line":82,"column":29},"end":{"line":82,"column":null}},"38":{"start":{"line":83,"column":12},"end":{"line":83,"column":null}},"39":{"start":{"line":83,"column":29},"end":{"line":83,"column":null}},"40":{"start":{"line":85,"column":8},"end":{"line":85,"column":null}},"41":{"start":{"line":87,"column":36},"end":{"line":87,"column":63}},"42":{"start":{"line":88,"column":8},"end":{"line":88,"column":null}},"43":{"start":{"line":90,"column":24},"end":{"line":90,"column":88}},"44":{"start":{"line":91,"column":8},"end":{"line":91,"column":null}},"45":{"start":{"line":8,"column":0},"end":{"line":8,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":24}},"loc":{"start":{"line":12,"column":40},"end":{"line":12,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":22,"column":4},"end":{"line":22,"column":10}},"loc":{"start":{"line":25,"column":37},"end":{"line":57,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":51,"column":32},"end":{"line":51,"column":33}},"loc":{"start":{"line":51,"column":41},"end":{"line":55,"column":13}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":59,"column":4},"end":{"line":59,"column":10}},"loc":{"start":{"line":59,"column":78},"end":{"line":67,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":69,"column":4},"end":{"line":69,"column":10}},"loc":{"start":{"line":72,"column":30},"end":{"line":93,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":35,"column":12},"end":{"line":35,"column":null}},"type":"if","locations":[{"start":{"line":35,"column":12},"end":{"line":35,"column":null}}]},"1":{"loc":{"start":{"line":36,"column":12},"end":{"line":36,"column":null}},"type":"if","locations":[{"start":{"line":36,"column":12},"end":{"line":36,"column":null}}]},"2":{"loc":{"start":{"line":37,"column":12},"end":{"line":37,"column":null}},"type":"if","locations":[{"start":{"line":37,"column":12},"end":{"line":37,"column":null}}]},"3":{"loc":{"start":{"line":38,"column":12},"end":{"line":38,"column":null}},"type":"if","locations":[{"start":{"line":38,"column":12},"end":{"line":38,"column":null}}]},"4":{"loc":{"start":{"line":41,"column":8},"end":{"line":41,"column":null}},"type":"if","locations":[{"start":{"line":41,"column":8},"end":{"line":41,"column":null}}]},"5":{"loc":{"start":{"line":50,"column":8},"end":{"line":56,"column":null}},"type":"if","locations":[{"start":{"line":50,"column":8},"end":{"line":56,"column":null}},{"start":{"line":56,"column":15},"end":{"line":56,"column":null}}]},"6":{"loc":{"start":{"line":81,"column":12},"end":{"line":81,"column":null}},"type":"if","locations":[{"start":{"line":81,"column":12},"end":{"line":81,"column":null}}]},"7":{"loc":{"start":{"line":82,"column":12},"end":{"line":82,"column":null}},"type":"if","locations":[{"start":{"line":82,"column":12},"end":{"line":82,"column":null}}]},"8":{"loc":{"start":{"line":83,"column":12},"end":{"line":83,"column":null}},"type":"if","locations":[{"start":{"line":83,"column":12},"end":{"line":83,"column":null}}]}},"s":{"0":4,"1":4,"2":8,"3":8,"4":4,"5":4,"6":4,"7":4,"8":4,"9":4,"10":4,"11":4,"12":4,"13":4,"14":0,"15":4,"16":4,"17":4,"18":4,"19":4,"20":4,"21":3,"22":6,"23":6,"24":6,"25":1,"26":0,"27":0,"28":0,"29":0,"30":0,"31":2,"32":2,"33":1,"34":1,"35":1,"36":1,"37":1,"38":1,"39":1,"40":2,"41":2,"42":2,"43":2,"44":2,"45":4},"f":{"0":8,"1":4,"2":6,"3":0,"4":2},"b":{"0":[4],"1":[4],"2":[4],"3":[0],"4":[4],"5":[3,1],"6":[1],"7":[1],"8":[1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/version/PackageVersionUpdater.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/package/version/PackageVersionUpdater.ts","statementMap":{"0":{"start":{"line":8,"column":12},"end":{"line":8,"column":null}},"1":{"start":{"line":10,"column":27},"end":{"line":10,"column":62}},"2":{"start":{"line":11,"column":37},"end":{"line":11,"column":58}},"3":{"start":{"line":13,"column":12},"end":{"line":14,"column":null}},"4":{"start":{"line":13,"column":43},"end":{"line":13,"column":93}},"5":{"start":{"line":14,"column":17},"end":{"line":14,"column":null}},"6":{"start":{"line":15,"column":12},"end":{"line":15,"column":null}},"7":{"start":{"line":3,"column":0},"end":{"line":3,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":4,"column":4},"end":{"line":4,"column":26}},"loc":{"start":{"line":4,"column":4},"end":{"line":4,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":6,"column":11},"end":{"line":6,"column":32}},"loc":{"start":{"line":6,"column":76},"end":{"line":17,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":12},"end":{"line":14,"column":null}},"type":"if","locations":[{"start":{"line":13,"column":12},"end":{"line":14,"column":null}},{"start":{"line":14,"column":17},"end":{"line":14,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":1},"f":{"0":0,"1":0},"b":{"0":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/permsets/AssignPermissionSetsImpl.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/permsets/AssignPermissionSetsImpl.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":14},"end":{"line":6,"column":34}},"5":{"start":{"line":10,"column":16},"end":{"line":10,"column":32}},"6":{"start":{"line":11,"column":16},"end":{"line":11,"column":34}},"7":{"start":{"line":12,"column":16},"end":{"line":12,"column":41}},"8":{"start":{"line":13,"column":16},"end":{"line":13,"column":37}},"9":{"start":{"line":26,"column":52},"end":{"line":26,"column":112}},"10":{"start":{"line":27,"column":31},"end":{"line":27,"column":80}},"11":{"start":{"line":32,"column":14},"end":{"line":32,"column":16}},"12":{"start":{"line":36,"column":14},"end":{"line":36,"column":16}},"13":{"start":{"line":39,"column":41},"end":{"line":41,"column":14}},"14":{"start":{"line":40,"column":16},"end":{"line":40,"column":null}},"15":{"start":{"line":45,"column":16},"end":{"line":45,"column":null}},"16":{"start":{"line":46,"column":16},"end":{"line":46,"column":null}},"17":{"start":{"line":50,"column":52},"end":{"line":56,"column":null}},"18":{"start":{"line":59,"column":40},"end":{"line":59,"column":73}},"19":{"start":{"line":60,"column":16},"end":{"line":62,"column":null}},"20":{"start":{"line":61,"column":20},"end":{"line":61,"column":105}},"21":{"start":{"line":62,"column":21},"end":{"line":62,"column":null}},"22":{"start":{"line":64,"column":16},"end":{"line":64,"column":null}},"23":{"start":{"line":69,"column":12},"end":{"line":69,"column":null}},"24":{"start":{"line":70,"column":12},"end":{"line":70,"column":null}},"25":{"start":{"line":74,"column":12},"end":{"line":74,"column":null}},"26":{"start":{"line":75,"column":12},"end":{"line":75,"column":null}},"27":{"start":{"line":78,"column":8},"end":{"line":78,"column":null}},"28":{"start":{"line":82,"column":20},"end":{"line":85,"column":10}},"29":{"start":{"line":87,"column":8},"end":{"line":89,"column":null}},"30":{"start":{"line":88,"column":12},"end":{"line":88,"column":null}},"31":{"start":{"line":91,"column":8},"end":{"line":91,"column":null}},"32":{"start":{"line":8,"column":0},"end":{"line":8,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":9,"column":4},"end":{"line":9,"column":null}},"loc":{"start":{"line":13,"column":37},"end":{"line":14,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":16,"column":11},"end":{"line":16,"column":17}},"loc":{"start":{"line":16,"column":21},"end":{"line":79,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":39,"column":64},"end":{"line":39,"column":70}},"loc":{"start":{"line":39,"column":74},"end":{"line":41,"column":13}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":81,"column":12},"end":{"line":81,"column":35}},"loc":{"start":{"line":81,"column":88},"end":{"line":92,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":87,"column":29},"end":{"line":87,"column":39}},"loc":{"start":{"line":87,"column":43},"end":{"line":89,"column":9}}}},"branchMap":{"0":{"loc":{"start":{"line":60,"column":16},"end":{"line":62,"column":null}},"type":"if","locations":[{"start":{"line":60,"column":16},"end":{"line":62,"column":null}},{"start":{"line":62,"column":21},"end":{"line":62,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":3,"6":3,"7":3,"8":3,"9":3,"10":3,"11":3,"12":3,"13":6,"14":12,"15":0,"16":0,"17":6,"18":6,"19":6,"20":3,"21":3,"22":0,"23":2,"24":2,"25":2,"26":2,"27":3,"28":4,"29":4,"30":6,"31":4,"32":1},"f":{"0":3,"1":3,"2":12,"3":4,"4":6},"b":{"0":[3,3]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/permsets/PermissionSetFetcher.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/permsets/PermissionSetFetcher.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":8,"column":24},"end":{"line":8,"column":40}},"2":{"start":{"line":8,"column":50},"end":{"line":8,"column":66}},"3":{"start":{"line":11,"column":22},"end":{"line":11,"column":146}},"4":{"start":{"line":13,"column":8},"end":{"line":13,"column":null}},"5":{"start":{"line":7,"column":0},"end":{"line":7,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":8,"column":4},"end":{"line":8,"column":24}},"loc":{"start":{"line":8,"column":66},"end":{"line":8,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":10,"column":11},"end":{"line":10,"column":17}},"loc":{"start":{"line":10,"column":42},"end":{"line":14,"column":null}}}},"branchMap":{},"s":{"0":1,"1":3,"2":3,"3":3,"4":3,"5":1},"f":{"0":3,"1":3},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/permsets/PermissionSetGroupUpdateAwaiter.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/permsets/PermissionSetGroupUpdateAwaiter.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":6,"column":21},"end":{"line":6,"column":101}},"4":{"start":{"line":9,"column":24},"end":{"line":9,"column":46}},"5":{"start":{"line":9,"column":56},"end":{"line":9,"column":70}},"6":{"start":{"line":9,"column":80},"end":{"line":9,"column":105}},"7":{"start":{"line":12,"column":8},"end":{"line":16,"column":null}},"8":{"start":{"line":19,"column":30},"end":{"line":19,"column":91}},"9":{"start":{"line":21,"column":20},"end":{"line":25,"column":null}},"10":{"start":{"line":26,"column":20},"end":{"line":30,"column":null}},"11":{"start":{"line":31,"column":20},"end":{"line":31,"column":null}},"12":{"start":{"line":33,"column":20},"end":{"line":37,"column":null}},"13":{"start":{"line":38,"column":20},"end":{"line":38,"column":null}},"14":{"start":{"line":41,"column":16},"end":{"line":41,"column":null}},"15":{"start":{"line":42,"column":16},"end":{"line":42,"column":null}},"16":{"start":{"line":8,"column":0},"end":{"line":8,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":9,"column":4},"end":{"line":9,"column":24}},"loc":{"start":{"line":9,"column":110},"end":{"line":9,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":11,"column":4},"end":{"line":11,"column":10}},"loc":{"start":{"line":11,"column":48},"end":{"line":45,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":9,"column":80},"end":{"line":9,"column":110}},"type":"default-arg","locations":[{"start":{"line":9,"column":105},"end":{"line":9,"column":110}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":0,"10":0,"11":0,"12":1,"13":1,"14":0,"15":0,"16":1},"f":{"0":1,"1":1},"b":{"0":[1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/project/ProjectConfig.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/project/ProjectConfig.ts","statementMap":{"0":{"start":{"line":1,"column":11},"end":{"line":1,"column":30}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":11},"end":{"line":5,"column":26}},"4":{"start":{"line":18,"column":12},"end":{"line":18,"column":null}},"5":{"start":{"line":20,"column":12},"end":{"line":20,"column":null}},"6":{"start":{"line":29,"column":28},"end":{"line":29,"column":80}},"7":{"start":{"line":30,"column":27},"end":{"line":30,"column":29}},"8":{"start":{"line":31,"column":8},"end":{"line":34,"column":null}},"9":{"start":{"line":33,"column":12},"end":{"line":33,"column":null}},"10":{"start":{"line":33,"column":50},"end":{"line":33,"column":null}},"11":{"start":{"line":35,"column":8},"end":{"line":35,"column":null}},"12":{"start":{"line":41,"column":100},"end":{"line":41,"column":102}},"13":{"start":{"line":42,"column":41},"end":{"line":42,"column":104}},"14":{"start":{"line":43,"column":31},"end":{"line":43,"column":65}},"15":{"start":{"line":44,"column":8},"end":{"line":55,"column":null}},"16":{"start":{"line":45,"column":12},"end":{"line":54,"column":null}},"17":{"start":{"line":49,"column":24},"end":{"line":49,"column":null}},"18":{"start":{"line":54,"column":16},"end":{"line":54,"column":null}},"19":{"start":{"line":56,"column":8},"end":{"line":56,"column":null}},"20":{"start":{"line":64,"column":27},"end":{"line":64,"column":29}},"21":{"start":{"line":65,"column":8},"end":{"line":68,"column":null}},"22":{"start":{"line":67,"column":12},"end":{"line":67,"column":null}},"23":{"start":{"line":67,"column":50},"end":{"line":67,"column":null}},"24":{"start":{"line":69,"column":8},"end":{"line":69,"column":null}},"25":{"start":{"line":75,"column":34},"end":{"line":75,"column":98}},"26":{"start":{"line":76,"column":23},"end":{"line":76,"column":86}},"27":{"start":{"line":79,"column":16},"end":{"line":79,"column":null}},"28":{"start":{"line":82,"column":8},"end":{"line":82,"column":null}},"29":{"start":{"line":86,"column":28},"end":{"line":86,"column":80}},"30":{"start":{"line":87,"column":27},"end":{"line":87,"column":29}},"31":{"start":{"line":88,"column":8},"end":{"line":91,"column":null}},"32":{"start":{"line":90,"column":12},"end":{"line":90,"column":null}},"33":{"start":{"line":90,"column":50},"end":{"line":90,"column":null}},"34":{"start":{"line":92,"column":8},"end":{"line":92,"column":null}},"35":{"start":{"line":96,"column":27},"end":{"line":96,"column":29}},"36":{"start":{"line":97,"column":8},"end":{"line":100,"column":null}},"37":{"start":{"line":99,"column":12},"end":{"line":99,"column":null}},"38":{"start":{"line":99,"column":50},"end":{"line":99,"column":null}},"39":{"start":{"line":101,"column":8},"end":{"line":101,"column":null}},"40":{"start":{"line":112,"column":12},"end":{"line":112,"column":null}},"41":{"start":{"line":114,"column":12},"end":{"line":114,"column":null}},"42":{"start":{"line":118,"column":12},"end":{"line":118,"column":null}},"43":{"start":{"line":120,"column":12},"end":{"line":120,"column":null}},"44":{"start":{"line":133,"column":32},"end":{"line":133,"column":104}},"45":{"start":{"line":136,"column":12},"end":{"line":136,"column":null}},"46":{"start":{"line":138,"column":12},"end":{"line":141,"column":null}},"47":{"start":{"line":138,"column":76},"end":{"line":138,"column":100}},"48":{"start":{"line":139,"column":17},"end":{"line":141,"column":null}},"49":{"start":{"line":139,"column":80},"end":{"line":139,"column":null}},"50":{"start":{"line":141,"column":13},"end":{"line":141,"column":null}},"51":{"start":{"line":151,"column":28},"end":{"line":151,"column":80}},"52":{"start":{"line":153,"column":36},"end":{"line":153,"column":108}},"53":{"start":{"line":155,"column":8},"end":{"line":155,"column":null}},"54":{"start":{"line":167,"column":12},"end":{"line":171,"column":null}},"55":{"start":{"line":169,"column":20},"end":{"line":169,"column":null}},"56":{"start":{"line":174,"column":8},"end":{"line":174,"column":null}},"57":{"start":{"line":174,"column":43},"end":{"line":174,"column":null}},"58":{"start":{"line":176,"column":8},"end":{"line":176,"column":null}},"59":{"start":{"line":187,"column":28},"end":{"line":187,"column":71}},"60":{"start":{"line":190,"column":8},"end":{"line":195,"column":null}},"61":{"start":{"line":192,"column":16},"end":{"line":192,"column":null}},"62":{"start":{"line":193,"column":16},"end":{"line":193,"column":null}},"63":{"start":{"line":197,"column":8},"end":{"line":198,"column":null}},"64":{"start":{"line":197,"column":38},"end":{"line":197,"column":96}},"65":{"start":{"line":198,"column":13},"end":{"line":198,"column":null}},"66":{"start":{"line":207,"column":30},"end":{"line":207,"column":73}},"67":{"start":{"line":209,"column":8},"end":{"line":209,"column":null}},"68":{"start":{"line":219,"column":20},"end":{"line":219,"column":62}},"69":{"start":{"line":222,"column":20},"end":{"line":222,"column":null}},"70":{"start":{"line":226,"column":20},"end":{"line":226,"column":62}},"71":{"start":{"line":229,"column":20},"end":{"line":229,"column":null}},"72":{"start":{"line":233,"column":8},"end":{"line":233,"column":65}},"73":{"start":{"line":234,"column":8},"end":{"line":234,"column":null}},"74":{"start":{"line":243,"column":38},"end":{"line":243,"column":40}},"75":{"start":{"line":244,"column":39},"end":{"line":244,"column":74}},"76":{"start":{"line":248,"column":20},"end":{"line":248,"column":null}},"77":{"start":{"line":249,"column":20},"end":{"line":249,"column":null}},"78":{"start":{"line":253,"column":8},"end":{"line":253,"column":65}},"79":{"start":{"line":254,"column":8},"end":{"line":254,"column":null}},"80":{"start":{"line":255,"column":8},"end":{"line":255,"column":null}},"81":{"start":{"line":264,"column":30},"end":{"line":264,"column":73}},"82":{"start":{"line":265,"column":8},"end":{"line":265,"column":null}},"83":{"start":{"line":274,"column":35},"end":{"line":274,"column":67}},"84":{"start":{"line":275,"column":8},"end":{"line":277,"column":null}},"85":{"start":{"line":276,"column":12},"end":{"line":276,"column":null}},"86":{"start":{"line":279,"column":8},"end":{"line":279,"column":null}},"87":{"start":{"line":10,"column":0},"end":{"line":10,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":16,"column":11},"end":{"line":16,"column":18}},"loc":{"start":{"line":16,"column":70},"end":{"line":22,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":28,"column":11},"end":{"line":28,"column":18}},"loc":{"start":{"line":28,"column":57},"end":{"line":36,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":31,"column":53},"end":{"line":31,"column":56}},"loc":{"start":{"line":31,"column":60},"end":{"line":34,"column":9}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":38,"column":11},"end":{"line":38,"column":18}},"loc":{"start":{"line":39,"column":26},"end":{"line":57,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":44,"column":47},"end":{"line":44,"column":48}},"loc":{"start":{"line":44,"column":64},"end":{"line":55,"column":9}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":48,"column":21},"end":{"line":48,"column":25}},"loc":{"start":{"line":48,"column":29},"end":{"line":50,"column":21}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":63,"column":11},"end":{"line":63,"column":18}},"loc":{"start":{"line":63,"column":68},"end":{"line":70,"column":null}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":65,"column":50},"end":{"line":65,"column":53}},"loc":{"start":{"line":65,"column":57},"end":{"line":68,"column":9}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":72,"column":11},"end":{"line":72,"column":18}},"loc":{"start":{"line":73,"column":26},"end":{"line":83,"column":null}}},"9":{"name":"(anonymous_10)","decl":{"start":{"line":85,"column":11},"end":{"line":85,"column":18}},"loc":{"start":{"line":85,"column":81},"end":{"line":93,"column":null}}},"10":{"name":"(anonymous_11)","decl":{"start":{"line":88,"column":51},"end":{"line":88,"column":54}},"loc":{"start":{"line":88,"column":58},"end":{"line":91,"column":9}}},"11":{"name":"(anonymous_12)","decl":{"start":{"line":95,"column":11},"end":{"line":95,"column":18}},"loc":{"start":{"line":95,"column":71},"end":{"line":102,"column":null}}},"12":{"name":"(anonymous_13)","decl":{"start":{"line":97,"column":51},"end":{"line":97,"column":54}},"loc":{"start":{"line":97,"column":58},"end":{"line":100,"column":9}}},"13":{"name":"(anonymous_14)","decl":{"start":{"line":108,"column":11},"end":{"line":108,"column":18}},"loc":{"start":{"line":108,"column":63},"end":{"line":122,"column":null}}},"14":{"name":"(anonymous_15)","decl":{"start":{"line":129,"column":11},"end":{"line":129,"column":18}},"loc":{"start":{"line":131,"column":27},"end":{"line":143,"column":null}}},"15":{"name":"(anonymous_16)","decl":{"start":{"line":150,"column":11},"end":{"line":150,"column":18}},"loc":{"start":{"line":150,"column":88},"end":{"line":156,"column":null}}},"16":{"name":"(anonymous_17)","decl":{"start":{"line":163,"column":11},"end":{"line":163,"column":18}},"loc":{"start":{"line":163,"column":88},"end":{"line":177,"column":null}}},"17":{"name":"(anonymous_18)","decl":{"start":{"line":167,"column":57},"end":{"line":167,"column":60}},"loc":{"start":{"line":167,"column":64},"end":{"line":171,"column":13}}},"18":{"name":"(anonymous_19)","decl":{"start":{"line":183,"column":11},"end":{"line":183,"column":18}},"loc":{"start":{"line":183,"column":74},"end":{"line":199,"column":null}}},"19":{"name":"(anonymous_20)","decl":{"start":{"line":190,"column":53},"end":{"line":190,"column":56}},"loc":{"start":{"line":190,"column":60},"end":{"line":195,"column":9}}},"20":{"name":"(anonymous_21)","decl":{"start":{"line":206,"column":11},"end":{"line":206,"column":18}},"loc":{"start":{"line":206,"column":94},"end":{"line":210,"column":null}}},"21":{"name":"(anonymous_22)","decl":{"start":{"line":217,"column":11},"end":{"line":217,"column":18}},"loc":{"start":{"line":217,"column":85},"end":{"line":235,"column":null}}},"22":{"name":"(anonymous_23)","decl":{"start":{"line":242,"column":11},"end":{"line":242,"column":18}},"loc":{"start":{"line":242,"column":93},"end":{"line":256,"column":null}}},"23":{"name":"(anonymous_24)","decl":{"start":{"line":263,"column":11},"end":{"line":263,"column":18}},"loc":{"start":{"line":263,"column":102},"end":{"line":266,"column":null}}},"24":{"name":"(anonymous_25)","decl":{"start":{"line":270,"column":11},"end":{"line":270,"column":24}},"loc":{"start":{"line":272,"column":81},"end":{"line":280,"column":null}}},"25":{"name":"(anonymous_26)","decl":{"start":{"line":275,"column":53},"end":{"line":275,"column":56}},"loc":{"start":{"line":275,"column":60},"end":{"line":277,"column":9}}}},"branchMap":{"0":{"loc":{"start":{"line":33,"column":12},"end":{"line":33,"column":null}},"type":"if","locations":[{"start":{"line":33,"column":12},"end":{"line":33,"column":null}}]},"1":{"loc":{"start":{"line":33,"column":16},"end":{"line":33,"column":48}},"type":"binary-expr","locations":[{"start":{"line":33,"column":16},"end":{"line":33,"column":27}},{"start":{"line":33,"column":31},"end":{"line":33,"column":48}}]},"2":{"loc":{"start":{"line":43,"column":31},"end":{"line":43,"column":65}},"type":"binary-expr","locations":[{"start":{"line":43,"column":31},"end":{"line":43,"column":59}},{"start":{"line":43,"column":63},"end":{"line":43,"column":65}}]},"3":{"loc":{"start":{"line":45,"column":12},"end":{"line":54,"column":null}},"type":"if","locations":[{"start":{"line":45,"column":12},"end":{"line":54,"column":null}}]},"4":{"loc":{"start":{"line":67,"column":12},"end":{"line":67,"column":null}},"type":"if","locations":[{"start":{"line":67,"column":12},"end":{"line":67,"column":null}}]},"5":{"loc":{"start":{"line":67,"column":16},"end":{"line":67,"column":48}},"type":"binary-expr","locations":[{"start":{"line":67,"column":16},"end":{"line":67,"column":27}},{"start":{"line":67,"column":31},"end":{"line":67,"column":48}}]},"6":{"loc":{"start":{"line":90,"column":12},"end":{"line":90,"column":null}},"type":"if","locations":[{"start":{"line":90,"column":12},"end":{"line":90,"column":null}}]},"7":{"loc":{"start":{"line":90,"column":16},"end":{"line":90,"column":48}},"type":"binary-expr","locations":[{"start":{"line":90,"column":16},"end":{"line":90,"column":27}},{"start":{"line":90,"column":31},"end":{"line":90,"column":48}}]},"8":{"loc":{"start":{"line":99,"column":12},"end":{"line":99,"column":null}},"type":"if","locations":[{"start":{"line":99,"column":12},"end":{"line":99,"column":null}}]},"9":{"loc":{"start":{"line":99,"column":16},"end":{"line":99,"column":48}},"type":"binary-expr","locations":[{"start":{"line":99,"column":16},"end":{"line":99,"column":27}},{"start":{"line":99,"column":31},"end":{"line":99,"column":48}}]},"10":{"loc":{"start":{"line":138,"column":12},"end":{"line":141,"column":null}},"type":"if","locations":[{"start":{"line":138,"column":12},"end":{"line":141,"column":null}},{"start":{"line":139,"column":17},"end":{"line":141,"column":null}}]},"11":{"loc":{"start":{"line":139,"column":17},"end":{"line":141,"column":null}},"type":"if","locations":[{"start":{"line":139,"column":17},"end":{"line":141,"column":null}},{"start":{"line":141,"column":13},"end":{"line":141,"column":null}}]},"12":{"loc":{"start":{"line":174,"column":8},"end":{"line":174,"column":null}},"type":"if","locations":[{"start":{"line":174,"column":8},"end":{"line":174,"column":null}}]},"13":{"loc":{"start":{"line":197,"column":8},"end":{"line":198,"column":null}},"type":"if","locations":[{"start":{"line":197,"column":8},"end":{"line":198,"column":null}},{"start":{"line":198,"column":13},"end":{"line":198,"column":null}}]}},"s":{"0":5,"1":5,"2":5,"3":5,"4":1,"5":1,"6":1,"7":1,"8":1,"9":5,"10":5,"11":1,"12":2,"13":2,"14":2,"15":2,"16":3,"17":15,"18":1,"19":2,"20":1,"21":1,"22":5,"23":5,"24":1,"25":7,"26":7,"27":35,"28":7,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":13,"36":13,"37":60,"38":60,"39":13,"40":0,"41":4,"42":4,"43":0,"44":13,"45":7,"46":6,"47":1,"48":5,"49":0,"50":5,"51":1,"52":1,"53":1,"54":24,"55":23,"56":24,"57":1,"58":23,"59":1,"60":1,"61":1,"62":1,"63":1,"64":0,"65":1,"66":1,"67":1,"68":1,"69":4,"70":0,"71":0,"72":1,"73":1,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":5},"f":{"0":2,"1":1,"2":5,"3":2,"4":3,"5":15,"6":1,"7":5,"8":7,"9":0,"10":0,"11":13,"12":60,"13":4,"14":13,"15":1,"16":24,"17":123,"18":1,"19":5,"20":1,"21":1,"22":0,"23":0,"24":0,"25":0},"b":{"0":[5],"1":[5,5],"2":[2,0],"3":[1],"4":[5],"5":[5,5],"6":[0],"7":[0,0],"8":[60],"9":[60,60],"10":[1,5],"11":[0,5],"12":[1],"13":[0,1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/project/UserDefinedExternalDependency.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/project/UserDefinedExternalDependency.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":14,"column":8},"end":{"line":20,"column":null}},"5":{"start":{"line":15,"column":40},"end":{"line":15,"column":87}},"6":{"start":{"line":16,"column":12},"end":{"line":16,"column":null}},"7":{"start":{"line":17,"column":12},"end":{"line":17,"column":null}},"8":{"start":{"line":20,"column":9},"end":{"line":20,"column":null}},"9":{"start":{"line":24,"column":35},"end":{"line":24,"column":37}},"10":{"start":{"line":25,"column":35},"end":{"line":25,"column":67}},"11":{"start":{"line":26,"column":38},"end":{"line":26,"column":111}},"12":{"start":{"line":28,"column":36},"end":{"line":28,"column":78}},"13":{"start":{"line":30,"column":32},"end":{"line":30,"column":109}},"14":{"start":{"line":33,"column":12},"end":{"line":33,"column":null}},"15":{"start":{"line":37,"column":16},"end":{"line":37,"column":null}},"16":{"start":{"line":40,"column":8},"end":{"line":40,"column":null}},"17":{"start":{"line":41,"column":8},"end":{"line":41,"column":null}},"18":{"start":{"line":45,"column":35},"end":{"line":45,"column":67}},"19":{"start":{"line":47,"column":42},"end":{"line":47,"column":96}},"20":{"start":{"line":50,"column":20},"end":{"line":50,"column":null}},"21":{"start":{"line":55,"column":20},"end":{"line":55,"column":null}},"22":{"start":{"line":59,"column":8},"end":{"line":59,"column":null}},"23":{"start":{"line":10,"column":0},"end":{"line":10,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":13,"column":12},"end":{"line":13,"column":34}},"loc":{"start":{"line":13,"column":53},"end":{"line":21,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":23,"column":11},"end":{"line":23,"column":17}},"loc":{"start":{"line":23,"column":82},"end":{"line":42,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":44,"column":11},"end":{"line":44,"column":17}},"loc":{"start":{"line":44,"column":50},"end":{"line":60,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":14,"column":8},"end":{"line":20,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":8},"end":{"line":20,"column":null}},{"start":{"line":20,"column":9},"end":{"line":20,"column":null}}]},"1":{"loc":{"start":{"line":52,"column":20},"end":{"line":53,"column":81}},"type":"binary-expr","locations":[{"start":{"line":52,"column":20},"end":{"line":52,"column":75}},{"start":{"line":53,"column":20},"end":{"line":53,"column":81}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":9,"5":9,"6":9,"7":9,"8":0,"9":2,"10":2,"11":2,"12":2,"13":2,"14":2,"15":0,"16":2,"17":2,"18":7,"19":7,"20":0,"21":0,"22":7,"23":2},"f":{"0":9,"1":2,"2":7},"b":{"0":[9,0],"1":[7,7]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/queryHelper/ChunkCollection.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/queryHelper/ChunkCollection.ts","statementMap":{"0":{"start":{"line":10,"column":29},"end":{"line":10,"column":31}},"1":{"start":{"line":11,"column":2},"end":{"line":11,"column":null}},"2":{"start":{"line":13,"column":24},"end":{"line":13,"column":26}},"3":{"start":{"line":14,"column":37},"end":{"line":14,"column":38}},"4":{"start":{"line":17,"column":6},"end":{"line":17,"column":null}},"5":{"start":{"line":20,"column":28},"end":{"line":20,"column":61}},"6":{"start":{"line":22,"column":6},"end":{"line":22,"column":null}},"7":{"start":{"line":23,"column":6},"end":{"line":23,"column":null}},"8":{"start":{"line":25,"column":6},"end":{"line":25,"column":null}},"9":{"start":{"line":28,"column":6},"end":{"line":28,"column":null}},"10":{"start":{"line":29,"column":6},"end":{"line":29,"column":null}},"11":{"start":{"line":30,"column":6},"end":{"line":30,"column":null}},"12":{"start":{"line":31,"column":6},"end":{"line":31,"column":null}},"13":{"start":{"line":35,"column":2},"end":{"line":35,"column":null}},"14":{"start":{"line":37,"column":2},"end":{"line":37,"column":null}},"15":{"start":{"line":9,"column":0},"end":{"line":9,"column":24}}},"fnMap":{"0":{"name":"chunkCollection","decl":{"start":{"line":9,"column":24},"end":{"line":9,"column":39}},"loc":{"start":{"line":9,"column":109},"end":{"line":38,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":9,"column":62},"end":{"line":9,"column":86}},"type":"default-arg","locations":[{"start":{"line":9,"column":82},"end":{"line":9,"column":86}}]},"1":{"loc":{"start":{"line":9,"column":88},"end":{"line":9,"column":109}},"type":"default-arg","locations":[{"start":{"line":9,"column":105},"end":{"line":9,"column":109}}]}},"s":{"0":3,"1":3,"2":3,"3":3,"4":1,"5":7,"6":6,"7":6,"8":1,"9":1,"10":1,"11":1,"12":1,"13":2,"14":2,"15":2},"f":{"0":3},"b":{"0":[1],"1":[1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/queryHelper/QueryHelper.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/queryHelper/QueryHelper.ts","statementMap":{"0":{"start":{"line":3,"column":14},"end":{"line":3,"column":36}},"1":{"start":{"line":7,"column":8},"end":{"line":16,"column":null}},"2":{"start":{"line":10,"column":16},"end":{"line":11,"column":null}},"3":{"start":{"line":10,"column":31},"end":{"line":10,"column":83}},"4":{"start":{"line":11,"column":21},"end":{"line":11,"column":null}},"5":{"start":{"line":13,"column":16},"end":{"line":13,"column":null}},"6":{"start":{"line":5,"column":0},"end":{"line":5,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":4},"end":{"line":6,"column":17}},"loc":{"start":{"line":6,"column":77},"end":{"line":17,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":8,"column":12},"end":{"line":8,"column":19}},"loc":{"start":{"line":8,"column":27},"end":{"line":14,"column":13}}}},"branchMap":{"0":{"loc":{"start":{"line":10,"column":16},"end":{"line":11,"column":null}},"type":"if","locations":[{"start":{"line":10,"column":16},"end":{"line":11,"column":null}},{"start":{"line":11,"column":21},"end":{"line":11,"column":null}}]}},"s":{"0":11,"1":21,"2":27,"3":7,"4":20,"5":19,"6":11},"f":{"0":21,"1":27},"b":{"0":[7,20]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/stats/NativeMetricSender.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/stats/NativeMetricSender.ts","statementMap":{"0":{"start":{"line":4,"column":26},"end":{"line":4,"column":40}},"1":{"start":{"line":14,"column":48},"end":{"line":14,"column":50}},"2":{"start":{"line":16,"column":16},"end":{"line":16,"column":null}},"3":{"start":{"line":18,"column":12},"end":{"line":18,"column":null}},"4":{"start":{"line":20,"column":8},"end":{"line":20,"column":null}},"5":{"start":{"line":3,"column":0},"end":{"line":3,"column":22}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":4,"column":4},"end":{"line":4,"column":26}},"loc":{"start":{"line":4,"column":40},"end":{"line":4,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":14},"end":{"line":12,"column":40}},"loc":{"start":{"line":12,"column":83},"end":{"line":21,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":12},"end":{"line":13,"column":48}},"type":"binary-expr","locations":[{"start":{"line":13,"column":12},"end":{"line":13,"column":24}},{"start":{"line":13,"column":28},"end":{"line":13,"column":48}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":1},"f":{"0":0,"1":0},"b":{"0":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/stats/SFPStatsSender.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/stats/SFPStatsSender.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"4":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"5":{"start":{"line":8,"column":0},"end":{"line":8,"column":null}},"6":{"start":{"line":16,"column":37},"end":{"line":21,"column":null}},"7":{"start":{"line":22,"column":8},"end":{"line":22,"column":null}},"8":{"start":{"line":28,"column":16},"end":{"line":28,"column":null}},"9":{"start":{"line":29,"column":16},"end":{"line":29,"column":null}},"10":{"start":{"line":30,"column":16},"end":{"line":30,"column":null}},"11":{"start":{"line":33,"column":16},"end":{"line":33,"column":null}},"12":{"start":{"line":34,"column":16},"end":{"line":34,"column":null}},"13":{"start":{"line":35,"column":16},"end":{"line":35,"column":null}},"14":{"start":{"line":38,"column":16},"end":{"line":38,"column":null}},"15":{"start":{"line":39,"column":16},"end":{"line":39,"column":null}},"16":{"start":{"line":40,"column":16},"end":{"line":40,"column":null}},"17":{"start":{"line":43,"column":16},"end":{"line":43,"column":null}},"18":{"start":{"line":49,"column":12},"end":{"line":49,"column":null}},"19":{"start":{"line":50,"column":12},"end":{"line":50,"column":null}},"20":{"start":{"line":52,"column":12},"end":{"line":52,"column":null}},"21":{"start":{"line":57,"column":8},"end":{"line":57,"column":null}},"22":{"start":{"line":57,"column":43},"end":{"line":57,"column":null}},"23":{"start":{"line":61,"column":12},"end":{"line":61,"column":null}},"24":{"start":{"line":64,"column":22},"end":{"line":70,"column":null}},"25":{"start":{"line":71,"column":8},"end":{"line":71,"column":null}},"26":{"start":{"line":75,"column":8},"end":{"line":75,"column":null}},"27":{"start":{"line":75,"column":43},"end":{"line":75,"column":null}},"28":{"start":{"line":79,"column":12},"end":{"line":79,"column":null}},"29":{"start":{"line":82,"column":22},"end":{"line":88,"column":null}},"30":{"start":{"line":89,"column":8},"end":{"line":89,"column":null}},"31":{"start":{"line":93,"column":8},"end":{"line":93,"column":null}},"32":{"start":{"line":93,"column":43},"end":{"line":93,"column":null}},"33":{"start":{"line":97,"column":12},"end":{"line":97,"column":null}},"34":{"start":{"line":100,"column":22},"end":{"line":105,"column":null}},"35":{"start":{"line":106,"column":8},"end":{"line":106,"column":null}},"36":{"start":{"line":111,"column":12},"end":{"line":111,"column":null}},"37":{"start":{"line":10,"column":0},"end":{"line":10,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":15,"column":4},"end":{"line":15,"column":11}},"loc":{"start":{"line":15,"column":66},"end":{"line":23,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":25,"column":4},"end":{"line":25,"column":11}},"loc":{"start":{"line":25,"column":97},"end":{"line":45,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":47,"column":4},"end":{"line":47,"column":11}},"loc":{"start":{"line":47,"column":36},"end":{"line":54,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":56,"column":4},"end":{"line":56,"column":11}},"loc":{"start":{"line":56,"column":114},"end":{"line":72,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":74,"column":4},"end":{"line":74,"column":11}},"loc":{"start":{"line":74,"column":94},"end":{"line":90,"column":null}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":92,"column":4},"end":{"line":92,"column":11}},"loc":{"start":{"line":92,"column":79},"end":{"line":107,"column":null}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":109,"column":4},"end":{"line":109,"column":11}},"loc":{"start":{"line":109,"column":44},"end":{"line":113,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":18},"end":{"line":18,"column":52}},"type":"cond-expr","locations":[{"start":{"line":18,"column":33},"end":{"line":18,"column":37}},{"start":{"line":18,"column":40},"end":{"line":18,"column":52}}]},"1":{"loc":{"start":{"line":19,"column":22},"end":{"line":19,"column":55}},"type":"cond-expr","locations":[{"start":{"line":19,"column":42},"end":{"line":19,"column":47}},{"start":{"line":19,"column":50},"end":{"line":19,"column":55}}]},"2":{"loc":{"start":{"line":27,"column":12},"end":{"line":30,"column":null}},"type":"switch","locations":[{"start":{"line":27,"column":12},"end":{"line":30,"column":null}},{"start":{"line":32,"column":12},"end":{"line":35,"column":null}},{"start":{"line":37,"column":12},"end":{"line":40,"column":null}},{"start":{"line":42,"column":12},"end":{"line":43,"column":null}}]},"3":{"loc":{"start":{"line":57,"column":8},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":57,"column":8},"end":{"line":57,"column":null}}]},"4":{"loc":{"start":{"line":75,"column":8},"end":{"line":75,"column":null}},"type":"if","locations":[{"start":{"line":75,"column":8},"end":{"line":75,"column":null}}]},"5":{"loc":{"start":{"line":93,"column":8},"end":{"line":93,"column":null}},"type":"if","locations":[{"start":{"line":93,"column":8},"end":{"line":93,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0,0,0],"3":[0],"4":[0],"5":[0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/DataDogMetricSender.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/DataDogMetricSender.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":7,"column":8},"end":{"line":7,"column":null}},"4":{"start":{"line":14,"column":12},"end":{"line":19,"column":null}},"5":{"start":{"line":21,"column":12},"end":{"line":21,"column":null}},"6":{"start":{"line":27,"column":34},"end":{"line":27,"column":71}},"7":{"start":{"line":28,"column":12},"end":{"line":28,"column":null}},"8":{"start":{"line":29,"column":12},"end":{"line":29,"column":null}},"9":{"start":{"line":31,"column":12},"end":{"line":35,"column":null}},"10":{"start":{"line":41,"column":34},"end":{"line":41,"column":71}},"11":{"start":{"line":42,"column":12},"end":{"line":42,"column":null}},"12":{"start":{"line":43,"column":12},"end":{"line":43,"column":null}},"13":{"start":{"line":45,"column":12},"end":{"line":49,"column":null}},"14":{"start":{"line":5,"column":0},"end":{"line":5,"column":13}}},"fnMap":{"0":{"name":"(anonymous_6)","decl":{"start":{"line":6,"column":4},"end":{"line":6,"column":16}},"loc":{"start":{"line":6,"column":30},"end":{"line":8,"column":null}}},"1":{"name":"(anonymous_7)","decl":{"start":{"line":12,"column":11},"end":{"line":12,"column":21}},"loc":{"start":{"line":12,"column":53},"end":{"line":23,"column":null}}},"2":{"name":"(anonymous_8)","decl":{"start":{"line":25,"column":11},"end":{"line":25,"column":26}},"loc":{"start":{"line":25,"column":100},"end":{"line":37,"column":null}}},"3":{"name":"(anonymous_9)","decl":{"start":{"line":39,"column":11},"end":{"line":39,"column":26}},"loc":{"start":{"line":39,"column":85},"end":{"line":51,"column":null}}}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":1},"f":{"0":0,"1":0,"2":0,"3":0},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/NewRelicMetricSender.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/NewRelicMetricSender.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"2":{"start":{"line":9,"column":0},"end":{"line":9,"column":null}},"3":{"start":{"line":13,"column":8},"end":{"line":13,"column":null}},"4":{"start":{"line":21,"column":12},"end":{"line":21,"column":null}},"5":{"start":{"line":23,"column":12},"end":{"line":23,"column":null}},"6":{"start":{"line":28,"column":8},"end":{"line":28,"column":null}},"7":{"start":{"line":29,"column":28},"end":{"line":29,"column":58}},"8":{"start":{"line":30,"column":8},"end":{"line":30,"column":null}},"9":{"start":{"line":31,"column":22},"end":{"line":31,"column":56}},"10":{"start":{"line":32,"column":8},"end":{"line":32,"column":null}},"11":{"start":{"line":33,"column":8},"end":{"line":43,"column":null}},"12":{"start":{"line":35,"column":16},"end":{"line":35,"column":null}},"13":{"start":{"line":37,"column":12},"end":{"line":42,"column":null}},"14":{"start":{"line":38,"column":16},"end":{"line":42,"column":null}},"15":{"start":{"line":47,"column":8},"end":{"line":47,"column":null}},"16":{"start":{"line":48,"column":28},"end":{"line":48,"column":51}},"17":{"start":{"line":49,"column":8},"end":{"line":49,"column":null}},"18":{"start":{"line":50,"column":8},"end":{"line":50,"column":null}},"19":{"start":{"line":51,"column":22},"end":{"line":51,"column":56}},"20":{"start":{"line":52,"column":8},"end":{"line":52,"column":null}},"21":{"start":{"line":54,"column":8},"end":{"line":64,"column":null}},"22":{"start":{"line":56,"column":16},"end":{"line":56,"column":null}},"23":{"start":{"line":58,"column":12},"end":{"line":63,"column":null}},"24":{"start":{"line":59,"column":16},"end":{"line":63,"column":null}},"25":{"start":{"line":11,"column":0},"end":{"line":11,"column":13}}},"fnMap":{"0":{"name":"(anonymous_6)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":16}},"loc":{"start":{"line":12,"column":30},"end":{"line":14,"column":null}}},"1":{"name":"(anonymous_7)","decl":{"start":{"line":19,"column":11},"end":{"line":19,"column":21}},"loc":{"start":{"line":19,"column":53},"end":{"line":25,"column":null}}},"2":{"name":"(anonymous_8)","decl":{"start":{"line":27,"column":11},"end":{"line":27,"column":26}},"loc":{"start":{"line":27,"column":100},"end":{"line":44,"column":null}}},"3":{"name":"(anonymous_9)","decl":{"start":{"line":33,"column":40},"end":{"line":33,"column":41}},"loc":{"start":{"line":33,"column":66},"end":{"line":43,"column":9}}},"4":{"name":"(anonymous_10)","decl":{"start":{"line":46,"column":11},"end":{"line":46,"column":26}},"loc":{"start":{"line":46,"column":85},"end":{"line":65,"column":null}}},"5":{"name":"(anonymous_11)","decl":{"start":{"line":54,"column":40},"end":{"line":54,"column":41}},"loc":{"start":{"line":54,"column":66},"end":{"line":64,"column":9}}}},"branchMap":{"0":{"loc":{"start":{"line":37,"column":12},"end":{"line":42,"column":null}},"type":"if","locations":[{"start":{"line":37,"column":12},"end":{"line":42,"column":null}}]},"1":{"loc":{"start":{"line":58,"column":12},"end":{"line":63,"column":null}},"type":"if","locations":[{"start":{"line":58,"column":12},"end":{"line":63,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0},"b":{"0":[0],"1":[0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/SplunkMetricSender.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/SplunkMetricSender.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":9,"column":8},"end":{"line":9,"column":null}},"4":{"start":{"line":15,"column":10},"end":{"line":18,"column":null}},"5":{"start":{"line":22,"column":8},"end":{"line":22,"column":null}},"6":{"start":{"line":23,"column":24},"end":{"line":23,"column":176}},"7":{"start":{"line":24,"column":8},"end":{"line":32,"column":null}},"8":{"start":{"line":25,"column":29},"end":{"line":25,"column":126}},"9":{"start":{"line":27,"column":12},"end":{"line":31,"column":null}},"10":{"start":{"line":36,"column":8},"end":{"line":36,"column":null}},"11":{"start":{"line":37,"column":24},"end":{"line":37,"column":163}},"12":{"start":{"line":38,"column":8},"end":{"line":46,"column":null}},"13":{"start":{"line":39,"column":29},"end":{"line":39,"column":126}},"14":{"start":{"line":41,"column":12},"end":{"line":45,"column":null}},"15":{"start":{"line":7,"column":0},"end":{"line":7,"column":13}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":8,"column":4},"end":{"line":8,"column":16}},"loc":{"start":{"line":8,"column":30},"end":{"line":10,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":14,"column":11},"end":{"line":14,"column":21}},"loc":{"start":{"line":14,"column":53},"end":{"line":19,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":21,"column":11},"end":{"line":21,"column":26}},"loc":{"start":{"line":21,"column":100},"end":{"line":33,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":25,"column":15},"end":{"line":25,"column":23}},"loc":{"start":{"line":25,"column":27},"end":{"line":25,"column":126}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":26,"column":16},"end":{"line":26,"column":21}},"loc":{"start":{"line":26,"column":25},"end":{"line":32,"column":9}}},"5":{"name":"(anonymous_12)","decl":{"start":{"line":35,"column":11},"end":{"line":35,"column":26}},"loc":{"start":{"line":35,"column":85},"end":{"line":47,"column":null}}},"6":{"name":"(anonymous_13)","decl":{"start":{"line":39,"column":15},"end":{"line":39,"column":23}},"loc":{"start":{"line":39,"column":27},"end":{"line":39,"column":126}}},"7":{"name":"(anonymous_14)","decl":{"start":{"line":40,"column":16},"end":{"line":40,"column":21}},"loc":{"start":{"line":40,"column":25},"end":{"line":46,"column":9}}}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/AliasList.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/AliasList.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":5,"column":28},"end":{"line":5,"column":63}},"2":{"start":{"line":6,"column":4},"end":{"line":6,"column":null}},"3":{"start":{"line":7,"column":4},"end":{"line":7,"column":null}},"4":{"start":{"line":4,"column":0},"end":{"line":4,"column":7}},"5":{"start":{"line":12,"column":28},"end":{"line":12,"column":63}},"6":{"start":{"line":13,"column":4},"end":{"line":13,"column":null}},"7":{"start":{"line":14,"column":4},"end":{"line":14,"column":null}},"8":{"start":{"line":10,"column":0},"end":{"line":10,"column":7}}},"fnMap":{"0":{"name":"convertAliasToUsername","decl":{"start":{"line":4,"column":22},"end":{"line":4,"column":44}},"loc":{"start":{"line":4,"column":58},"end":{"line":8,"column":null}}},"1":{"name":"convertUsernameToAlias","decl":{"start":{"line":10,"column":22},"end":{"line":10,"column":44}},"loc":{"start":{"line":10,"column":61},"end":{"line":16,"column":null}}}},"branchMap":{},"s":{"0":4,"1":0,"2":0,"3":0,"4":4,"5":0,"6":0,"7":0,"8":4},"f":{"0":0,"1":0},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/ChunkArray.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/ChunkArray.ts","statementMap":{"0":{"start":{"line":2,"column":17},"end":{"line":2,"column":19}},"1":{"start":{"line":3,"column":12},"end":{"line":3,"column":13}},"2":{"start":{"line":4,"column":12},"end":{"line":4,"column":29}},"3":{"start":{"line":7,"column":8},"end":{"line":7,"column":null}},"4":{"start":{"line":10,"column":4},"end":{"line":10,"column":null}},"5":{"start":{"line":1,"column":0},"end":{"line":1,"column":16}}},"fnMap":{"0":{"name":"chunkArray","decl":{"start":{"line":1,"column":16},"end":{"line":1,"column":26}},"loc":{"start":{"line":1,"column":62},"end":{"line":11,"column":null}}}},"branchMap":{},"s":{"0":2,"1":2,"2":2,"3":9,"4":2,"5":1},"f":{"0":2},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/Delay.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/Delay.ts","statementMap":{"0":{"start":{"line":2,"column":4},"end":{"line":2,"column":null}},"1":{"start":{"line":2,"column":36},"end":{"line":2,"column":59}},"2":{"start":{"line":1,"column":0},"end":{"line":1,"column":7}}},"fnMap":{"0":{"name":"delay","decl":{"start":{"line":1,"column":22},"end":{"line":1,"column":27}},"loc":{"start":{"line":1,"column":42},"end":{"line":3,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":2,"column":24},"end":{"line":2,"column":31}},"loc":{"start":{"line":2,"column":36},"end":{"line":2,"column":59}}}},"branchMap":{"0":{"loc":{"start":{"line":1,"column":28},"end":{"line":1,"column":42}},"type":"default-arg","locations":[{"start":{"line":1,"column":41},"end":{"line":1,"column":42}}]}},"s":{"0":0,"1":0,"2":1},"f":{"0":0,"1":0},"b":{"0":[0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/FileSystem.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/FileSystem.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":16,"column":33},"end":{"line":16,"column":35}},"3":{"start":{"line":18,"column":8},"end":{"line":18,"column":null}},"4":{"start":{"line":18,"column":58},"end":{"line":18,"column":null}},"5":{"start":{"line":20,"column":8},"end":{"line":48,"column":null}},"6":{"start":{"line":21,"column":36},"end":{"line":21,"column":61}},"7":{"start":{"line":23,"column":12},"end":{"line":47,"column":null}},"8":{"start":{"line":26,"column":20},"end":{"line":26,"column":null}},"9":{"start":{"line":28,"column":20},"end":{"line":28,"column":null}},"10":{"start":{"line":34,"column":28},"end":{"line":34,"column":null}},"11":{"start":{"line":36,"column":28},"end":{"line":36,"column":null}},"12":{"start":{"line":39,"column":20},"end":{"line":39,"column":null}},"13":{"start":{"line":42,"column":24},"end":{"line":42,"column":null}},"14":{"start":{"line":44,"column":24},"end":{"line":44,"column":null}},"15":{"start":{"line":50,"column":8},"end":{"line":50,"column":null}},"16":{"start":{"line":4,"column":0},"end":{"line":4,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":11,"column":4},"end":{"line":11,"column":11}},"loc":{"start":{"line":14,"column":35},"end":{"line":51,"column":null}}},"1":{"name":"readdirRecursiveHandler","decl":{"start":{"line":20,"column":18},"end":{"line":20,"column":41}},"loc":{"start":{"line":20,"column":59},"end":{"line":48,"column":9}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":23,"column":27},"end":{"line":23,"column":31}},"loc":{"start":{"line":23,"column":35},"end":{"line":47,"column":13}}}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":8},"end":{"line":13,"column":43}},"type":"default-arg","locations":[{"start":{"line":13,"column":38},"end":{"line":13,"column":43}}]},"1":{"loc":{"start":{"line":14,"column":8},"end":{"line":14,"column":35}},"type":"default-arg","locations":[{"start":{"line":14,"column":30},"end":{"line":14,"column":35}}]},"2":{"loc":{"start":{"line":18,"column":8},"end":{"line":18,"column":null}},"type":"if","locations":[{"start":{"line":18,"column":8},"end":{"line":18,"column":null}}]}},"s":{"0":2,"1":2,"2":4,"3":4,"4":0,"5":4,"6":24,"7":24,"8":22,"9":22,"10":5,"11":5,"12":20,"13":12,"14":12,"15":4,"16":2},"f":{"0":4,"1":24,"2":44},"b":{"0":[0],"1":[0],"2":[0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/Fileutils.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/Fileutils.ts","statementMap":{"0":{"start":{"line":1,"column":11},"end":{"line":1,"column":24}},"1":{"start":{"line":2,"column":13},"end":{"line":2,"column":28}},"2":{"start":{"line":3,"column":10},"end":{"line":3,"column":27}},"3":{"start":{"line":4,"column":11},"end":{"line":4,"column":24}},"4":{"start":{"line":6,"column":12},"end":{"line":6,"column":19}},"5":{"start":{"line":8,"column":13},"end":{"line":8,"column":null}},"6":{"start":{"line":16,"column":8},"end":{"line":24,"column":null}},"7":{"start":{"line":19,"column":20},"end":{"line":19,"column":null}},"8":{"start":{"line":21,"column":20},"end":{"line":21,"column":null}},"9":{"start":{"line":32,"column":31},"end":{"line":32,"column":33}},"10":{"start":{"line":33,"column":25},"end":{"line":33,"column":46}},"11":{"start":{"line":34,"column":25},"end":{"line":34,"column":46}},"12":{"start":{"line":36,"column":12},"end":{"line":36,"column":null}},"13":{"start":{"line":37,"column":12},"end":{"line":37,"column":null}},"14":{"start":{"line":39,"column":32},"end":{"line":39,"column":54}},"15":{"start":{"line":40,"column":8},"end":{"line":51,"column":null}},"16":{"start":{"line":41,"column":26},"end":{"line":41,"column":49}},"17":{"start":{"line":42,"column":24},"end":{"line":42,"column":44}},"18":{"start":{"line":45,"column":20},"end":{"line":45,"column":null}},"19":{"start":{"line":48,"column":38},"end":{"line":48,"column":78}},"20":{"start":{"line":49,"column":16},"end":{"line":49,"column":null}},"21":{"start":{"line":52,"column":8},"end":{"line":52,"column":null}},"22":{"start":{"line":56,"column":22},"end":{"line":56,"column":34}},"23":{"start":{"line":57,"column":24},"end":{"line":57,"column":64}},"24":{"start":{"line":59,"column":12},"end":{"line":59,"column":null}},"25":{"start":{"line":60,"column":12},"end":{"line":60,"column":null}},"26":{"start":{"line":63,"column":8},"end":{"line":63,"column":null}},"27":{"start":{"line":71,"column":22},"end":{"line":71,"column":34}},"28":{"start":{"line":72,"column":24},"end":{"line":72,"column":64}},"29":{"start":{"line":74,"column":12},"end":{"line":74,"column":null}},"30":{"start":{"line":75,"column":12},"end":{"line":75,"column":null}},"31":{"start":{"line":77,"column":8},"end":{"line":77,"column":null}},"32":{"start":{"line":86,"column":20},"end":{"line":86,"column":28}},"33":{"start":{"line":87,"column":24},"end":{"line":87,"column":61}},"34":{"start":{"line":88,"column":24},"end":{"line":88,"column":60}},"35":{"start":{"line":90,"column":8},"end":{"line":100,"column":null}},"36":{"start":{"line":91,"column":27},"end":{"line":91,"column":69}},"37":{"start":{"line":93,"column":16},"end":{"line":93,"column":null}},"38":{"start":{"line":96,"column":20},"end":{"line":96,"column":null}},"39":{"start":{"line":99,"column":12},"end":{"line":99,"column":null}},"40":{"start":{"line":108,"column":24},"end":{"line":108,"column":43}},"41":{"start":{"line":109,"column":23},"end":{"line":109,"column":54}},"42":{"start":{"line":111,"column":12},"end":{"line":111,"column":null}},"43":{"start":{"line":113,"column":12},"end":{"line":113,"column":null}},"44":{"start":{"line":115,"column":8},"end":{"line":115,"column":null}},"45":{"start":{"line":124,"column":21},"end":{"line":124,"column":39}},"46":{"start":{"line":126,"column":24},"end":{"line":126,"column":40}},"47":{"start":{"line":127,"column":30},"end":{"line":127,"column":49}},"48":{"start":{"line":129,"column":16},"end":{"line":129,"column":null}},"49":{"start":{"line":131,"column":20},"end":{"line":131,"column":null}},"50":{"start":{"line":133,"column":16},"end":{"line":135,"column":null}},"51":{"start":{"line":134,"column":20},"end":{"line":134,"column":null}},"52":{"start":{"line":137,"column":16},"end":{"line":137,"column":null}},"53":{"start":{"line":147,"column":21},"end":{"line":147,"column":39}},"54":{"start":{"line":148,"column":23},"end":{"line":148,"column":25}},"55":{"start":{"line":150,"column":24},"end":{"line":150,"column":40}},"56":{"start":{"line":151,"column":30},"end":{"line":151,"column":49}},"57":{"start":{"line":153,"column":29},"end":{"line":153,"column":48}},"58":{"start":{"line":154,"column":29},"end":{"line":154,"column":30}},"59":{"start":{"line":155,"column":40},"end":{"line":155,"column":49}},"60":{"start":{"line":157,"column":24},"end":{"line":157,"column":null}},"61":{"start":{"line":159,"column":40},"end":{"line":159,"column":82}},"62":{"start":{"line":161,"column":28},"end":{"line":161,"column":null}},"63":{"start":{"line":165,"column":24},"end":{"line":165,"column":null}},"64":{"start":{"line":170,"column":8},"end":{"line":170,"column":null}},"65":{"start":{"line":179,"column":12},"end":{"line":190,"column":null}},"66":{"start":{"line":180,"column":30},"end":{"line":180,"column":53}},"67":{"start":{"line":184,"column":20},"end":{"line":184,"column":null}},"68":{"start":{"line":188,"column":20},"end":{"line":188,"column":null}},"69":{"start":{"line":192,"column":12},"end":{"line":192,"column":null}},"70":{"start":{"line":196,"column":21},"end":{"line":196,"column":23}},"71":{"start":{"line":197,"column":25},"end":{"line":197,"column":89}},"72":{"start":{"line":198,"column":31},"end":{"line":198,"column":48}},"73":{"start":{"line":199,"column":21},"end":{"line":199,"column":22}},"74":{"start":{"line":200,"column":12},"end":{"line":200,"column":null}},"75":{"start":{"line":202,"column":8},"end":{"line":202,"column":null}},"76":{"start":{"line":10,"column":0},"end":{"line":10,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":15,"column":11},"end":{"line":15,"column":18}},"loc":{"start":{"line":15,"column":62},"end":{"line":25,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":16,"column":35},"end":{"line":16,"column":44}},"loc":{"start":{"line":16,"column":48},"end":{"line":24,"column":9}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":31,"column":11},"end":{"line":31,"column":18}},"loc":{"start":{"line":31,"column":76},"end":{"line":53,"column":null}}},"3":{"name":"(anonymous_3)","decl":{"start":{"line":40,"column":25},"end":{"line":40,"column":29}},"loc":{"start":{"line":40,"column":33},"end":{"line":51,"column":9}}},"4":{"name":"(anonymous_4)","decl":{"start":{"line":55,"column":11},"end":{"line":55,"column":18}},"loc":{"start":{"line":55,"column":35},"end":{"line":64,"column":null}}},"5":{"name":"(anonymous_5)","decl":{"start":{"line":70,"column":11},"end":{"line":70,"column":18}},"loc":{"start":{"line":70,"column":53},"end":{"line":78,"column":null}}},"6":{"name":"(anonymous_6)","decl":{"start":{"line":85,"column":11},"end":{"line":85,"column":18}},"loc":{"start":{"line":85,"column":88},"end":{"line":101,"column":null}}},"7":{"name":"(anonymous_7)","decl":{"start":{"line":90,"column":36},"end":{"line":90,"column":37}},"loc":{"start":{"line":90,"column":60},"end":{"line":100,"column":9}}},"8":{"name":"(anonymous_8)","decl":{"start":{"line":107,"column":11},"end":{"line":107,"column":18}},"loc":{"start":{"line":107,"column":82},"end":{"line":116,"column":null}}},"9":{"name":"(anonymous_9)","decl":{"start":{"line":123,"column":11},"end":{"line":123,"column":18}},"loc":{"start":{"line":123,"column":45},"end":{"line":140,"column":null}}},"10":{"name":"(anonymous_10)","decl":{"start":{"line":133,"column":44},"end":{"line":133,"column":54}},"loc":{"start":{"line":133,"column":67},"end":{"line":135,"column":17}}},"11":{"name":"(anonymous_11)","decl":{"start":{"line":146,"column":11},"end":{"line":146,"column":18}},"loc":{"start":{"line":146,"column":47},"end":{"line":171,"column":null}}},"12":{"name":"(anonymous_12)","decl":{"start":{"line":177,"column":11},"end":{"line":177,"column":18}},"loc":{"start":{"line":177,"column":46},"end":{"line":194,"column":null}}},"13":{"name":"(anonymous_13)","decl":{"start":{"line":179,"column":43},"end":{"line":179,"column":53}},"loc":{"start":{"line":179,"column":64},"end":{"line":190,"column":13}}},"14":{"name":"(anonymous_14)","decl":{"start":{"line":195,"column":11},"end":{"line":195,"column":18}},"loc":{"start":{"line":195,"column":37},"end":{"line":203,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":31,"column":50},"end":{"line":31,"column":76}},"type":"default-arg","locations":[{"start":{"line":31,"column":70},"end":{"line":31,"column":76}}]},"1":{"loc":{"start":{"line":44,"column":20},"end":{"line":44,"column":86}},"type":"binary-expr","locations":[{"start":{"line":44,"column":20},"end":{"line":44,"column":66}},{"start":{"line":44,"column":70},"end":{"line":44,"column":86}}]},"2":{"loc":{"start":{"line":85,"column":53},"end":{"line":85,"column":88}},"type":"default-arg","locations":[{"start":{"line":85,"column":86},"end":{"line":85,"column":88}}]},"3":{"loc":{"start":{"line":85,"column":55},"end":{"line":85,"column":81}},"type":"default-arg","locations":[{"start":{"line":85,"column":76},"end":{"line":85,"column":81}}]},"4":{"loc":{"start":{"line":87,"column":24},"end":{"line":87,"column":61}},"type":"cond-expr","locations":[{"start":{"line":87,"column":53},"end":{"line":87,"column":56}},{"start":{"line":87,"column":59},"end":{"line":87,"column":61}}]},"5":{"loc":{"start":{"line":88,"column":24},"end":{"line":88,"column":60}},"type":"cond-expr","locations":[{"start":{"line":88,"column":45},"end":{"line":88,"column":54}},{"start":{"line":88,"column":57},"end":{"line":88,"column":60}}]},"6":{"loc":{"start":{"line":95,"column":20},"end":{"line":95,"column":90}},"type":"binary-expr","locations":[{"start":{"line":95,"column":20},"end":{"line":95,"column":41}},{"start":{"line":95,"column":45},"end":{"line":95,"column":65}},{"start":{"line":95,"column":69},"end":{"line":95,"column":90}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":1},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0},"b":{"0":[0],"1":[0,0],"2":[0],"3":[0],"4":[0,0],"5":[0,0],"6":[0,0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/ObjectCRUDHelper.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/ObjectCRUDHelper.ts","statementMap":{"0":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"1":{"start":{"line":5,"column":14},"end":{"line":5,"column":36}},"2":{"start":{"line":9,"column":8},"end":{"line":25,"column":null}},"3":{"start":{"line":11,"column":29},"end":{"line":11,"column":63}},"4":{"start":{"line":12,"column":16},"end":{"line":22,"column":null}},"5":{"start":{"line":13,"column":48},"end":{"line":13,"column":52}},"6":{"start":{"line":16,"column":28},"end":{"line":16,"column":null}},"7":{"start":{"line":19,"column":20},"end":{"line":20,"column":null}},"8":{"start":{"line":19,"column":47},"end":{"line":19,"column":76}},"9":{"start":{"line":20,"column":25},"end":{"line":20,"column":null}},"10":{"start":{"line":21,"column":23},"end":{"line":22,"column":null}},"11":{"start":{"line":21,"column":59},"end":{"line":21,"column":92}},"12":{"start":{"line":22,"column":21},"end":{"line":22,"column":null}},"13":{"start":{"line":29,"column":8},"end":{"line":36,"column":null}},"14":{"start":{"line":31,"column":29},"end":{"line":31,"column":63}},"15":{"start":{"line":32,"column":16},"end":{"line":33,"column":null}},"16":{"start":{"line":32,"column":36},"end":{"line":32,"column":53}},"17":{"start":{"line":33,"column":21},"end":{"line":33,"column":null}},"18":{"start":{"line":7,"column":0},"end":{"line":7,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":8,"column":4},"end":{"line":8,"column":17}},"loc":{"start":{"line":8,"column":79},"end":{"line":26,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":12},"end":{"line":10,"column":19}},"loc":{"start":{"line":10,"column":27},"end":{"line":23,"column":13}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":28,"column":4},"end":{"line":28,"column":17}},"loc":{"start":{"line":28,"column":79},"end":{"line":37,"column":null}}},"3":{"name":"(anonymous_3)","decl":{"start":{"line":30,"column":12},"end":{"line":30,"column":19}},"loc":{"start":{"line":30,"column":27},"end":{"line":34,"column":13}}}},"branchMap":{"0":{"loc":{"start":{"line":12,"column":16},"end":{"line":22,"column":null}},"type":"if","locations":[{"start":{"line":12,"column":16},"end":{"line":22,"column":null}},{"start":{"line":21,"column":23},"end":{"line":22,"column":null}}]},"1":{"loc":{"start":{"line":19,"column":20},"end":{"line":20,"column":null}},"type":"if","locations":[{"start":{"line":19,"column":20},"end":{"line":20,"column":null}},{"start":{"line":20,"column":25},"end":{"line":20,"column":null}}]},"2":{"loc":{"start":{"line":21,"column":23},"end":{"line":22,"column":null}},"type":"if","locations":[{"start":{"line":21,"column":23},"end":{"line":22,"column":null}},{"start":{"line":22,"column":21},"end":{"line":22,"column":null}}]},"3":{"loc":{"start":{"line":32,"column":16},"end":{"line":33,"column":null}},"type":"if","locations":[{"start":{"line":32,"column":16},"end":{"line":33,"column":null}},{"start":{"line":33,"column":21},"end":{"line":33,"column":null}}]}},"s":{"0":4,"1":4,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":3,"14":3,"15":3,"16":2,"17":1,"18":4},"f":{"0":0,"1":0,"2":3,"3":3},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[2,1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/VersionNumberConverter.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/VersionNumberConverter.ts","statementMap":{"0":{"start":{"line":7,"column":27},"end":{"line":7,"column":34}},"1":{"start":{"line":9,"column":35},"end":{"line":9,"column":71}},"2":{"start":{"line":11,"column":8},"end":{"line":12,"column":null}},"3":{"start":{"line":14,"column":4},"end":{"line":14,"column":null}},"4":{"start":{"line":6,"column":0},"end":{"line":6,"column":24}},"5":{"start":{"line":23,"column":43},"end":{"line":23,"column":44}},"6":{"start":{"line":24,"column":17},"end":{"line":24,"column":18}},"7":{"start":{"line":26,"column":12},"end":{"line":26,"column":null}},"8":{"start":{"line":29,"column":12},"end":{"line":29,"column":null}},"9":{"start":{"line":32,"column":4},"end":{"line":32,"column":null}}},"fnMap":{"0":{"name":"convertBuildNumDotDelimToHyphen","decl":{"start":{"line":6,"column":24},"end":{"line":6,"column":55}},"loc":{"start":{"line":6,"column":71},"end":{"line":15,"column":null}}},"1":{"name":"getIndexOfBuildNumDelimeter","decl":{"start":{"line":22,"column":9},"end":{"line":22,"column":36}},"loc":{"start":{"line":22,"column":52},"end":{"line":33,"column":null}}}},"branchMap":{},"s":{"0":103,"1":103,"2":103,"3":103,"4":2,"5":103,"6":103,"7":309,"8":103,"9":0},"f":{"0":103,"1":103},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/extractDomainFromUrl.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/extractDomainFromUrl.ts","statementMap":{"0":{"start":{"line":7,"column":4},"end":{"line":7,"column":null}},"1":{"start":{"line":7,"column":14},"end":{"line":7,"column":null}},"2":{"start":{"line":8,"column":20},"end":{"line":8,"column":69}},"3":{"start":{"line":9,"column":4},"end":{"line":9,"column":null}},"4":{"start":{"line":6,"column":0},"end":{"line":6,"column":24}}},"fnMap":{"0":{"name":"extractDomainFromUrl","decl":{"start":{"line":6,"column":24},"end":{"line":6,"column":44}},"loc":{"start":{"line":6,"column":56},"end":{"line":10,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":7,"column":4},"end":{"line":7,"column":null}},"type":"if","locations":[{"start":{"line":7,"column":4},"end":{"line":7,"column":null}}]},"1":{"loc":{"start":{"line":9,"column":11},"end":{"line":9,"column":32}},"type":"binary-expr","locations":[{"start":{"line":9,"column":11},"end":{"line":9,"column":18}},{"start":{"line":9,"column":22},"end":{"line":9,"column":32}}]}},"s":{"0":7,"1":3,"2":4,"3":4,"4":1},"f":{"0":7},"b":{"0":[3],"1":[4,3]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/xml2json.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/core/utils/xml2json.ts","statementMap":{"0":{"start":{"line":1,"column":18},"end":{"line":1,"column":68}},"1":{"start":{"line":4,"column":4},"end":{"line":9,"column":null}},"2":{"start":{"line":5,"column":8},"end":{"line":8,"column":null}},"3":{"start":{"line":6,"column":12},"end":{"line":7,"column":null}},"4":{"start":{"line":6,"column":21},"end":{"line":6,"column":33}},"5":{"start":{"line":7,"column":17},"end":{"line":7,"column":null}},"6":{"start":{"line":3,"column":0},"end":{"line":3,"column":24}}},"fnMap":{"0":{"name":"xml2json","decl":{"start":{"line":3,"column":24},"end":{"line":3,"column":32}},"loc":{"start":{"line":3,"column":36},"end":{"line":10,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":4,"column":28},"end":{"line":4,"column":29}},"loc":{"start":{"line":4,"column":48},"end":{"line":9,"column":5}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":5,"column":35},"end":{"line":5,"column":45}},"loc":{"start":{"line":5,"column":54},"end":{"line":8,"column":9}}}},"branchMap":{"0":{"loc":{"start":{"line":6,"column":12},"end":{"line":7,"column":null}},"type":"if","locations":[{"start":{"line":6,"column":12},"end":{"line":7,"column":null}},{"start":{"line":7,"column":17},"end":{"line":7,"column":null}}]}},"s":{"0":4,"1":12,"2":12,"3":12,"4":0,"5":12,"6":4},"f":{"0":12,"1":12,"2":12},"b":{"0":[0,12]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/changelog/CommitUpdater.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/changelog/CommitUpdater.ts","statementMap":{"0":{"start":{"line":7,"column":16},"end":{"line":7,"column":38}},"1":{"start":{"line":8,"column":16},"end":{"line":8,"column":66}},"2":{"start":{"line":9,"column":16},"end":{"line":9,"column":69}},"3":{"start":{"line":10,"column":16},"end":{"line":10,"column":58}},"4":{"start":{"line":20,"column":53},"end":{"line":21,"column":null}},"5":{"start":{"line":25,"column":12},"end":{"line":25,"column":null}},"6":{"start":{"line":29,"column":16},"end":{"line":31,"column":null}},"7":{"start":{"line":30,"column":32},"end":{"line":30,"column":100}},"8":{"start":{"line":33,"column":20},"end":{"line":37,"column":null}},"9":{"start":{"line":38,"column":20},"end":{"line":38,"column":null}},"10":{"start":{"line":39,"column":20},"end":{"line":39,"column":null}},"11":{"start":{"line":40,"column":20},"end":{"line":40,"column":null}},"12":{"start":{"line":45,"column":16},"end":{"line":45,"column":null}},"13":{"start":{"line":48,"column":16},"end":{"line":48,"column":null}},"14":{"start":{"line":50,"column":16},"end":{"line":50,"column":null}},"15":{"start":{"line":53,"column":16},"end":{"line":53,"column":null}},"16":{"start":{"line":5,"column":0},"end":{"line":5,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":4},"end":{"line":6,"column":null}},"loc":{"start":{"line":10,"column":58},"end":{"line":11,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":18,"column":4},"end":{"line":18,"column":10}},"loc":{"start":{"line":18,"column":10},"end":{"line":56,"column":null}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":30,"column":21},"end":{"line":30,"column":27}},"loc":{"start":{"line":30,"column":32},"end":{"line":30,"column":100}}}},"branchMap":{},"s":{"0":4,"1":4,"2":4,"3":4,"4":10,"5":10,"6":6,"7":76,"8":2,"9":2,"10":2,"11":2,"12":4,"13":0,"14":0,"15":4,"16":1},"f":{"0":4,"1":4,"2":76},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/changelog/OrgsUpdater.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/changelog/OrgsUpdater.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":9,"column":16},"end":{"line":9,"column":50}},"2":{"start":{"line":10,"column":16},"end":{"line":10,"column":38}},"3":{"start":{"line":11,"column":16},"end":{"line":11,"column":27}},"4":{"start":{"line":12,"column":16},"end":{"line":12,"column":50}},"5":{"start":{"line":14,"column":8},"end":{"line":14,"column":null}},"6":{"start":{"line":17,"column":12},"end":{"line":17,"column":null}},"7":{"start":{"line":24,"column":26},"end":{"line":24,"column":89}},"8":{"start":{"line":24,"column":67},"end":{"line":24,"column":88}},"9":{"start":{"line":27,"column":20},"end":{"line":27,"column":null}},"10":{"start":{"line":28,"column":20},"end":{"line":28,"column":null}},"11":{"start":{"line":29,"column":20},"end":{"line":29,"column":null}},"12":{"start":{"line":31,"column":20},"end":{"line":36,"column":null}},"13":{"start":{"line":40,"column":16},"end":{"line":47,"column":null}},"14":{"start":{"line":49,"column":12},"end":{"line":55,"column":null}},"15":{"start":{"line":58,"column":22},"end":{"line":58,"column":85}},"16":{"start":{"line":58,"column":63},"end":{"line":58,"column":84}},"17":{"start":{"line":61,"column":42},"end":{"line":62,"column":null}},"18":{"start":{"line":62,"column":36},"end":{"line":62,"column":99}},"19":{"start":{"line":67,"column":24},"end":{"line":67,"column":null}},"20":{"start":{"line":68,"column":24},"end":{"line":68,"column":null}},"21":{"start":{"line":71,"column":24},"end":{"line":71,"column":null}},"22":{"start":{"line":75,"column":20},"end":{"line":75,"column":null}},"23":{"start":{"line":76,"column":20},"end":{"line":76,"column":null}},"24":{"start":{"line":79,"column":24},"end":{"line":79,"column":null}},"25":{"start":{"line":81,"column":24},"end":{"line":81,"column":null}},"26":{"start":{"line":85,"column":20},"end":{"line":85,"column":null}},"27":{"start":{"line":86,"column":20},"end":{"line":86,"column":null}},"28":{"start":{"line":89,"column":16},"end":{"line":95,"column":null}},"29":{"start":{"line":98,"column":16},"end":{"line":103,"column":null}},"30":{"start":{"line":104,"column":16},"end":{"line":109,"column":null}},"31":{"start":{"line":120,"column":27},"end":{"line":120,"column":45}},"32":{"start":{"line":121,"column":8},"end":{"line":125,"column":null}},"33":{"start":{"line":4,"column":0},"end":{"line":4,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":8,"column":4},"end":{"line":8,"column":null}},"loc":{"start":{"line":12,"column":50},"end":{"line":19,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":21,"column":4},"end":{"line":21,"column":10}},"loc":{"start":{"line":21,"column":10},"end":{"line":112,"column":null}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":24,"column":59},"end":{"line":24,"column":62}},"loc":{"start":{"line":24,"column":67},"end":{"line":24,"column":88}}},"3":{"name":"(anonymous_3)","decl":{"start":{"line":58,"column":55},"end":{"line":58,"column":58}},"loc":{"start":{"line":58,"column":63},"end":{"line":58,"column":84}}},"4":{"name":"(anonymous_4)","decl":{"start":{"line":62,"column":21},"end":{"line":62,"column":31}},"loc":{"start":{"line":62,"column":36},"end":{"line":62,"column":99}}},"5":{"name":"(anonymous_5)","decl":{"start":{"line":119,"column":12},"end":{"line":119,"column":30}},"loc":{"start":{"line":119,"column":47},"end":{"line":126,"column":null}}}},"branchMap":{},"s":{"0":1,"1":4,"2":4,"3":4,"4":4,"5":4,"6":2,"7":2,"8":1,"9":1,"10":1,"11":1,"12":1,"13":0,"14":2,"15":2,"16":3,"17":2,"18":2,"19":0,"20":0,"21":1,"22":1,"23":1,"24":1,"25":0,"26":1,"27":1,"28":2,"29":0,"30":0,"31":6,"32":6,"33":1},"f":{"0":4,"1":4,"2":1,"3":3,"4":2,"5":6},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/changelog/WorkItemUpdater.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/changelog/WorkItemUpdater.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":6,"column":24},"end":{"line":6,"column":46}},"2":{"start":{"line":6,"column":56},"end":{"line":6,"column":81}},"3":{"start":{"line":6,"column":90},"end":{"line":6,"column":104}},"4":{"start":{"line":14,"column":42},"end":{"line":14,"column":70}},"5":{"start":{"line":15,"column":8},"end":{"line":15,"column":null}},"6":{"start":{"line":19,"column":44},"end":{"line":19,"column":85}},"7":{"start":{"line":20,"column":50},"end":{"line":20,"column":90}},"8":{"start":{"line":24,"column":28},"end":{"line":24,"column":null}},"9":{"start":{"line":25,"column":28},"end":{"line":25,"column":null}},"10":{"start":{"line":27,"column":28},"end":{"line":27,"column":null}},"11":{"start":{"line":38,"column":12},"end":{"line":38,"column":null}},"12":{"start":{"line":5,"column":0},"end":{"line":5,"column":21}}},"fnMap":{"0":{"name":"(anonymous_6)","decl":{"start":{"line":6,"column":4},"end":{"line":6,"column":24}},"loc":{"start":{"line":6,"column":104},"end":{"line":6,"column":null}}},"1":{"name":"(anonymous_7)","decl":{"start":{"line":11,"column":4},"end":{"line":11,"column":10}},"loc":{"start":{"line":11,"column":10},"end":{"line":40,"column":null}}}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":2,"5":2,"6":24,"7":24,"8":3,"9":3,"10":1,"11":3,"12":1},"f":{"0":1,"1":1},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/dependency/ShrinkImpl.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/dependency/ShrinkImpl.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"4":{"start":{"line":6,"column":14},"end":{"line":6,"column":34}},"5":{"start":{"line":7,"column":0},"end":{"line":7,"column":null}},"6":{"start":{"line":14,"column":24},"end":{"line":14,"column":47}},"7":{"start":{"line":14,"column":57},"end":{"line":14,"column":72}},"8":{"start":{"line":16,"column":8},"end":{"line":16,"column":null}},"9":{"start":{"line":18,"column":8},"end":{"line":18,"column":null}},"10":{"start":{"line":20,"column":45},"end":{"line":21,"column":null}},"11":{"start":{"line":24,"column":8},"end":{"line":24,"column":null}},"12":{"start":{"line":25,"column":8},"end":{"line":25,"column":null}},"13":{"start":{"line":27,"column":8},"end":{"line":27,"column":null}},"14":{"start":{"line":29,"column":8},"end":{"line":29,"column":null}},"15":{"start":{"line":33,"column":19},"end":{"line":33,"column":44}},"16":{"start":{"line":36,"column":12},"end":{"line":40,"column":null}},"17":{"start":{"line":41,"column":33},"end":{"line":41,"column":55}},"18":{"start":{"line":42,"column":38},"end":{"line":42,"column":65}},"19":{"start":{"line":45,"column":20},"end":{"line":51,"column":null}},"20":{"start":{"line":53,"column":37},"end":{"line":53,"column":38}},"21":{"start":{"line":55,"column":32},"end":{"line":55,"column":null}},"22":{"start":{"line":60,"column":20},"end":{"line":64,"column":null}},"23":{"start":{"line":68,"column":12},"end":{"line":68,"column":null}},"24":{"start":{"line":74,"column":8},"end":{"line":78,"column":null}},"25":{"start":{"line":76,"column":16},"end":{"line":76,"column":null}},"26":{"start":{"line":9,"column":0},"end":{"line":9,"column":21}}},"fnMap":{"0":{"name":"(anonymous_7)","decl":{"start":{"line":14,"column":4},"end":{"line":14,"column":24}},"loc":{"start":{"line":14,"column":72},"end":{"line":14,"column":null}}},"1":{"name":"(anonymous_8)","decl":{"start":{"line":15,"column":11},"end":{"line":15,"column":17}},"loc":{"start":{"line":15,"column":58},"end":{"line":30,"column":null}}},"2":{"name":"(anonymous_9)","decl":{"start":{"line":32,"column":12},"end":{"line":32,"column":18}},"loc":{"start":{"line":32,"column":65},"end":{"line":71,"column":null}}},"3":{"name":"(anonymous_10)","decl":{"start":{"line":73,"column":12},"end":{"line":73,"column":18}},"loc":{"start":{"line":73,"column":81},"end":{"line":79,"column":null}}},"4":{"name":"(anonymous_11)","decl":{"start":{"line":74,"column":58},"end":{"line":74,"column":61}},"loc":{"start":{"line":74,"column":65},"end":{"line":78,"column":9}}}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":2,"7":2,"8":2,"9":2,"10":2,"11":2,"12":2,"13":2,"14":2,"15":2,"16":12,"17":12,"18":12,"19":22,"20":36,"21":24,"22":18,"23":12,"24":12,"25":10,"26":1},"f":{"0":2,"1":2,"2":2,"3":12,"4":72},"b":{}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/parallelBuilder/BuildCollections.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/parallelBuilder/BuildCollections.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"2":{"start":{"line":12,"column":8},"end":{"line":12,"column":null}},"3":{"start":{"line":16,"column":8},"end":{"line":16,"column":null}},"4":{"start":{"line":24,"column":8},"end":{"line":24,"column":null}},"5":{"start":{"line":26,"column":28},"end":{"line":26,"column":80}},"6":{"start":{"line":29,"column":16},"end":{"line":49,"column":null}},"7":{"start":{"line":30,"column":20},"end":{"line":30,"column":null}},"8":{"start":{"line":30,"column":65},"end":{"line":30,"column":null}},"9":{"start":{"line":32,"column":20},"end":{"line":45,"column":null}},"10":{"start":{"line":36,"column":28},"end":{"line":41,"column":null}},"11":{"start":{"line":36,"column":80},"end":{"line":36,"column":116}},"12":{"start":{"line":37,"column":32},"end":{"line":37,"column":null}},"13":{"start":{"line":39,"column":32},"end":{"line":41,"column":null}},"14":{"start":{"line":44,"column":24},"end":{"line":44,"column":null}},"15":{"start":{"line":47,"column":20},"end":{"line":49,"column":null}},"16":{"start":{"line":59,"column":8},"end":{"line":59,"column":null}},"17":{"start":{"line":63,"column":8},"end":{"line":63,"column":null}},"18":{"start":{"line":7,"column":0},"end":{"line":7,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":11,"column":4},"end":{"line":11,"column":16}},"loc":{"start":{"line":11,"column":40},"end":{"line":13,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":15,"column":4},"end":{"line":15,"column":8}},"loc":{"start":{"line":15,"column":13},"end":{"line":17,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":23,"column":12},"end":{"line":23,"column":41}},"loc":{"start":{"line":23,"column":66},"end":{"line":52,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":32,"column":49},"end":{"line":32,"column":68}},"loc":{"start":{"line":32,"column":72},"end":{"line":45,"column":21}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":36,"column":71},"end":{"line":36,"column":75}},"loc":{"start":{"line":36,"column":80},"end":{"line":36,"column":116}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":58,"column":4},"end":{"line":58,"column":28}},"loc":{"start":{"line":58,"column":40},"end":{"line":60,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":62,"column":4},"end":{"line":62,"column":26}},"loc":{"start":{"line":62,"column":38},"end":{"line":64,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":29,"column":16},"end":{"line":49,"column":null}},"type":"if","locations":[{"start":{"line":29,"column":16},"end":{"line":49,"column":null}},{"start":{"line":47,"column":20},"end":{"line":49,"column":null}}]},"1":{"loc":{"start":{"line":30,"column":20},"end":{"line":30,"column":null}},"type":"if","locations":[{"start":{"line":30,"column":20},"end":{"line":30,"column":null}}]},"2":{"loc":{"start":{"line":36,"column":28},"end":{"line":41,"column":null}},"type":"if","locations":[{"start":{"line":36,"column":28},"end":{"line":41,"column":null}},{"start":{"line":39,"column":32},"end":{"line":41,"column":null}}]},"3":{"loc":{"start":{"line":63,"column":15},"end":{"line":63,"column":60}},"type":"cond-expr","locations":[{"start":{"line":63,"column":48},"end":{"line":63,"column":52}},{"start":{"line":63,"column":55},"end":{"line":63,"column":60}}]}},"s":{"0":1,"1":1,"2":4,"3":2,"4":4,"5":4,"6":7,"7":6,"8":6,"9":6,"10":3,"11":9,"12":2,"13":1,"14":5,"15":1,"16":0,"17":0,"18":1},"f":{"0":4,"1":2,"2":4,"3":6,"4":9,"5":0,"6":0},"b":{"0":[6,1],"1":[6],"2":[2,1],"3":[0,0]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/parallelBuilder/UndirectedGraph.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/parallelBuilder/UndirectedGraph.ts","statementMap":{"0":{"start":{"line":5,"column":8},"end":{"line":5,"column":null}},"1":{"start":{"line":9,"column":8},"end":{"line":9,"column":null}},"2":{"start":{"line":13,"column":8},"end":{"line":14,"column":null}},"3":{"start":{"line":13,"column":40},"end":{"line":13,"column":71}},"4":{"start":{"line":14,"column":13},"end":{"line":14,"column":null}},"5":{"start":{"line":18,"column":8},"end":{"line":18,"column":null}},"6":{"start":{"line":18,"column":33},"end":{"line":18,"column":null}},"7":{"start":{"line":19,"column":8},"end":{"line":19,"column":null}},"8":{"start":{"line":19,"column":43},"end":{"line":19,"column":null}},"9":{"start":{"line":20,"column":8},"end":{"line":20,"column":null}},"10":{"start":{"line":20,"column":43},"end":{"line":20,"column":null}},"11":{"start":{"line":22,"column":8},"end":{"line":22,"column":null}},"12":{"start":{"line":22,"column":61},"end":{"line":22,"column":null}},"13":{"start":{"line":23,"column":8},"end":{"line":23,"column":null}},"14":{"start":{"line":23,"column":61},"end":{"line":23,"column":null}},"15":{"start":{"line":31,"column":35},"end":{"line":31,"column":37}},"16":{"start":{"line":32,"column":50},"end":{"line":32,"column":52}},"17":{"start":{"line":33,"column":30},"end":{"line":33,"column":49}},"18":{"start":{"line":35,"column":8},"end":{"line":45,"column":null}},"19":{"start":{"line":36,"column":12},"end":{"line":36,"column":null}},"20":{"start":{"line":36,"column":25},"end":{"line":36,"column":null}},"21":{"start":{"line":37,"column":12},"end":{"line":37,"column":null}},"22":{"start":{"line":37,"column":40},"end":{"line":37,"column":null}},"23":{"start":{"line":38,"column":12},"end":{"line":38,"column":null}},"24":{"start":{"line":39,"column":12},"end":{"line":39,"column":null}},"25":{"start":{"line":40,"column":12},"end":{"line":44,"column":null}},"26":{"start":{"line":42,"column":20},"end":{"line":42,"column":null}},"27":{"start":{"line":47,"column":8},"end":{"line":47,"column":null}},"28":{"start":{"line":1,"column":0},"end":{"line":1,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":4,"column":4},"end":{"line":4,"column":null}},"loc":{"start":{"line":4,"column":4},"end":{"line":6,"column":null}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":8,"column":4},"end":{"line":8,"column":8}},"loc":{"start":{"line":8,"column":21},"end":{"line":10,"column":null}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":13}},"loc":{"start":{"line":12,"column":26},"end":{"line":15,"column":null}}},"3":{"name":"(anonymous_3)","decl":{"start":{"line":17,"column":4},"end":{"line":17,"column":11}},"loc":{"start":{"line":17,"column":44},"end":{"line":24,"column":null}}},"4":{"name":"(anonymous_4)","decl":{"start":{"line":30,"column":4},"end":{"line":30,"column":7}},"loc":{"start":{"line":30,"column":21},"end":{"line":48,"column":null}}},"5":{"name":"dfsHandler","decl":{"start":{"line":35,"column":18},"end":{"line":35,"column":28}},"loc":{"start":{"line":35,"column":35},"end":{"line":45,"column":9}}},"6":{"name":"(anonymous_6)","decl":{"start":{"line":40,"column":43},"end":{"line":40,"column":51}},"loc":{"start":{"line":40,"column":55},"end":{"line":44,"column":13}}}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":8},"end":{"line":14,"column":null}},"type":"if","locations":[{"start":{"line":13,"column":8},"end":{"line":14,"column":null}},{"start":{"line":14,"column":13},"end":{"line":14,"column":null}}]},"1":{"loc":{"start":{"line":18,"column":8},"end":{"line":18,"column":null}},"type":"if","locations":[{"start":{"line":18,"column":8},"end":{"line":18,"column":null}}]},"2":{"loc":{"start":{"line":19,"column":8},"end":{"line":19,"column":null}},"type":"if","locations":[{"start":{"line":19,"column":8},"end":{"line":19,"column":null}}]},"3":{"loc":{"start":{"line":20,"column":8},"end":{"line":20,"column":null}},"type":"if","locations":[{"start":{"line":20,"column":8},"end":{"line":20,"column":null}}]},"4":{"loc":{"start":{"line":22,"column":8},"end":{"line":22,"column":null}},"type":"if","locations":[{"start":{"line":22,"column":8},"end":{"line":22,"column":null}}]},"5":{"loc":{"start":{"line":23,"column":8},"end":{"line":23,"column":null}},"type":"if","locations":[{"start":{"line":23,"column":8},"end":{"line":23,"column":null}}]},"6":{"loc":{"start":{"line":36,"column":12},"end":{"line":36,"column":null}},"type":"if","locations":[{"start":{"line":36,"column":12},"end":{"line":36,"column":null}}]},"7":{"loc":{"start":{"line":37,"column":12},"end":{"line":37,"column":null}},"type":"if","locations":[{"start":{"line":37,"column":12},"end":{"line":37,"column":null}}]}},"s":{"0":12,"1":17,"2":22,"3":21,"4":1,"5":17,"6":1,"7":16,"8":2,"9":14,"10":1,"11":13,"12":13,"13":13,"14":13,"15":4,"16":4,"17":4,"18":4,"19":19,"20":0,"21":19,"22":1,"23":18,"24":18,"25":18,"26":15,"27":3,"28":2},"f":{"0":12,"1":17,"2":22,"3":17,"4":4,"5":19,"6":42},"b":{"0":[21,1],"1":[1],"2":[2],"3":[1],"4":[13],"5":[13],"6":[0],"7":[1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/release/ReleaseDefinition.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/impl/release/ReleaseDefinition.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":null}},"1":{"start":{"line":3,"column":13},"end":{"line":3,"column":31}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}},"5":{"start":{"line":8,"column":11},"end":{"line":8,"column":30}},"6":{"start":{"line":9,"column":13},"end":{"line":9,"column":28}},"7":{"start":{"line":14,"column":8},"end":{"line":14,"column":null}},"8":{"start":{"line":16,"column":32},"end":{"line":16,"column":81}},"9":{"start":{"line":17,"column":8},"end":{"line":17,"column":null}},"10":{"start":{"line":20,"column":8},"end":{"line":21,"column":null}},"11":{"start":{"line":21,"column":12},"end":{"line":21,"column":null}},"12":{"start":{"line":24,"column":12},"end":{"line":24,"column":null}},"13":{"start":{"line":33,"column":26},"end":{"line":33,"column":50}},"14":{"start":{"line":34,"column":16},"end":{"line":34,"column":null}},"15":{"start":{"line":35,"column":34},"end":{"line":35,"column":75}},"16":{"start":{"line":36,"column":16},"end":{"line":36,"column":null}},"17":{"start":{"line":38,"column":16},"end":{"line":38,"column":null}},"18":{"start":{"line":41,"column":12},"end":{"line":41,"column":null}},"19":{"start":{"line":44,"column":32},"end":{"line":44,"column":78}},"20":{"start":{"line":45,"column":8},"end":{"line":45,"column":null}},"21":{"start":{"line":50,"column":12},"end":{"line":50,"column":null}},"22":{"start":{"line":55,"column":21},"end":{"line":57,"column":null}},"23":{"start":{"line":60,"column":24},"end":{"line":60,"column":68}},"24":{"start":{"line":61,"column":31},"end":{"line":61,"column":59}},"25":{"start":{"line":65,"column":16},"end":{"line":66,"column":71}},"26":{"start":{"line":68,"column":12},"end":{"line":74,"column":null}},"27":{"start":{"line":69,"column":16},"end":{"line":73,"column":null}},"28":{"start":{"line":76,"column":12},"end":{"line":76,"column":null}},"29":{"start":{"line":11,"column":0},"end":{"line":11,"column":21}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":8}},"loc":{"start":{"line":12,"column":25},"end":{"line":15,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":16,"column":4},"end":{"line":16,"column":32}},"loc":{"start":{"line":16,"column":81},"end":{"line":26,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":28,"column":11},"end":{"line":28,"column":24}},"loc":{"start":{"line":28,"column":77},"end":{"line":46,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":48,"column":12},"end":{"line":48,"column":50}},"loc":{"start":{"line":48,"column":95},"end":{"line":52,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":54,"column":12},"end":{"line":54,"column":37}},"loc":{"start":{"line":54,"column":80},"end":{"line":78,"column":null}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":68,"column":37},"end":{"line":68,"column":38}},"loc":{"start":{"line":68,"column":57},"end":{"line":74,"column":13}}}},"branchMap":{"0":{"loc":{"start":{"line":20,"column":8},"end":{"line":21,"column":null}},"type":"if","locations":[{"start":{"line":20,"column":8},"end":{"line":21,"column":null}}]},"1":{"loc":{"start":{"line":20,"column":12},"end":{"line":20,"column":110}},"type":"binary-expr","locations":[{"start":{"line":20,"column":12},"end":{"line":20,"column":53}},{"start":{"line":20,"column":57},"end":{"line":20,"column":110}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":0,"8":5,"9":5,"10":1,"11":1,"12":0,"13":0,"14":0,"15":0,"16":0,"17":5,"18":0,"19":5,"20":0,"21":0,"22":5,"23":5,"24":5,"25":4,"26":4,"27":6,"28":4,"29":1},"f":{"0":0,"1":5,"2":5,"3":0,"4":5,"5":6},"b":{"0":[1],"1":[1,1]}} +,"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/utils/Get18DigitSalesforceId.ts": {"path":"/Users/azlam/projects/flxbl-io/sfpowerscripts/packages/sfpowerscripts-cli/src/utils/Get18DigitSalesforceId.ts","statementMap":{"0":{"start":{"line":3,"column":8},"end":{"line":3,"column":null}},"1":{"start":{"line":5,"column":20},"end":{"line":5,"column":22}},"2":{"start":{"line":6,"column":25},"end":{"line":6,"column":26}},"3":{"start":{"line":7,"column":23},"end":{"line":7,"column":24}},"4":{"start":{"line":8,"column":32},"end":{"line":8,"column":33}},"5":{"start":{"line":9,"column":30},"end":{"line":9,"column":67}},"6":{"start":{"line":10,"column":16},"end":{"line":10,"column":null}},"7":{"start":{"line":10,"column":54},"end":{"line":10,"column":null}},"8":{"start":{"line":12,"column":12},"end":{"line":12,"column":null}},"9":{"start":{"line":14,"column":26},"end":{"line":14,"column":42}},"10":{"start":{"line":15,"column":8},"end":{"line":15,"column":null}},"11":{"start":{"line":17,"column":8},"end":{"line":17,"column":null}},"12":{"start":{"line":1,"column":0},"end":{"line":1,"column":24}}},"fnMap":{"0":{"name":"get18DigitSalesforceId","decl":{"start":{"line":1,"column":24},"end":{"line":1,"column":46}},"loc":{"start":{"line":1,"column":63},"end":{"line":19,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":2,"column":8},"end":{"line":2,"column":42}},"type":"binary-expr","locations":[{"start":{"line":2,"column":8},"end":{"line":2,"column":16}},{"start":{"line":2,"column":20},"end":{"line":2,"column":42}}]},"1":{"loc":{"start":{"line":4,"column":15},"end":{"line":4,"column":49}},"type":"binary-expr","locations":[{"start":{"line":4,"column":15},"end":{"line":4,"column":23}},{"start":{"line":4,"column":27},"end":{"line":4,"column":49}}]},"2":{"loc":{"start":{"line":10,"column":16},"end":{"line":10,"column":null}},"type":"if","locations":[{"start":{"line":10,"column":16},"end":{"line":10,"column":null}}]},"3":{"loc":{"start":{"line":10,"column":20},"end":{"line":10,"column":52}},"type":"binary-expr","locations":[{"start":{"line":10,"column":20},"end":{"line":10,"column":34}},{"start":{"line":10,"column":38},"end":{"line":10,"column":52}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":1},"f":{"0":0},"b":{"0":[0,0],"1":[0,0],"2":[0],"3":[0,0]}} +} diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/base.css b/packages/sfpowerscripts-cli/coverage/lcov-report/base.css new file mode 100644 index 000000000..f418035b4 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/block-navigation.js b/packages/sfpowerscripts-cli/coverage/lcov-report/block-navigation.js new file mode 100644 index 000000000..cc1213023 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selecter that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/core/git/Git.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/core/git/Git.ts.html new file mode 100644 index 000000000..64189ce82 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/core/git/Git.ts.html @@ -0,0 +1,823 @@ + + + + + + Code coverage report for core/git/Git.ts + + + + + + + + + +
+
+

All files / core/git Git.ts

+
+ +
+ 6% + Statements + 6/100 +
+ + +
+ 0% + Branches + 0/15 +
+ + +
+ 0% + Functions + 0/26 +
+ + +
+ 6.38% + Lines + 6/94 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +2471x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import simplegit, { SimpleGit } from 'simple-git';
+import fs = require('fs-extra');
+import GitIdentity from './GitIdentity';
+const tmp = require('tmp');
+ 
+//Git Abstraction
+export default class Git {
+    private _git: SimpleGit;
+    private repositoryLocation: string;
+    private tempRepoLocation: any;
+    private _isATemporaryRepo: boolean = false;
+ 
+    private constructor(private projectDir?: string, private logger?: Logger) {
+        if (this.projectDir) {
+            this._git = simplegit(this.projectDir);
+            this.repositoryLocation = this.projectDir;
+        } else {
+            this._git = simplegit();
+            this.repositoryLocation = process.cwd();
+        }
+    }
+ 
+    async fetch() {
+        return this._git.fetch('origin');
+    }
+ 
+    async getHeadCommit(): Promise<string> {
+        return this._git.revparse(['HEAD']);
+    }
+ 
+    async show(options: string[]): Promise<string> {
+        return this._git.show(options);
+    }
+ 
+    async tag(options: string[]): Promise<string[]> {
+        let tagResult = await this._git.tag(options);
+ 
+        let temp: string[] = tagResult.split('\n');
+        temp.pop();
+ 
+        return temp;
+    }
+ 
+    async diff(options: string[]): Promise<string[]> {
+        let diffResult = await this._git.diff(options);
+ 
+        let temp: string[] = diffResult.split('\n');
+        temp.pop();
+ 
+        return temp;
+    }
+ 
+    async log(options: string[]): Promise<string[]> {
+        let gitLogResult = await this._git.log(options);
+ 
+        return gitLogResult['all'][0]['hash'].split('\n');
+    }
+ 
+    public async getRemoteOriginUrl(overrideOriginURL?: string): Promise<string> {
+        let remoteOriginURL;
+        if (!overrideOriginURL) {
+            remoteOriginURL = (await this._git.getConfig('remote.origin.url')).value;
+            if (!remoteOriginURL) {
+                remoteOriginURL = (await this._git.getConfig('remote.origin.url')).value;
+            }
+            SFPLogger.log(`Fetched Remote URL ${remoteOriginURL}`, LoggerLevel.DEBUG);
+        } else remoteOriginURL = overrideOriginURL;
+ 
+        Iif (!remoteOriginURL) throw new Error('Remote origin must be set in repository');
+ 
+        return remoteOriginURL;
+    }
+ 
+    public async commitFile(pathToFiles: string[], message = `[skip ci] Autogenerated commit by sfp`) {
+        try {
+            await new GitIdentity(this._git).setUsernameAndEmail();
+            await this._git.add(pathToFiles);
+            await this._git.commit(message);
+            SFPLogger.log(`Committed File ${pathToFiles}`);
+        } catch (error) {
+            SFPLogger.log(
+                `Unable to commit file, probably due to no change or something else,Please try manually`,
+                LoggerLevel.ERROR
+            );
+            throw error;
+        }
+    }
+ 
+    async pushTags(tags?: string[]) {
+        if (!tags) await this._git.pushTags();
+        else {
+            for (let tag of tags) {
+                await this._git.push('origin', tag);
+            }
+        }
+    }
+ 
+    async deleteTags(tags?: string[]) {
+        Iif (tags) await this._git.push('origin', '--delete', tags);
+    }
+ 
+    async addAnnotatedTag(tagName: string, annotation: string, commitId?: string) {
+        try {
+            await new GitIdentity(this._git).setUsernameAndEmail();
+            if (!commitId) {
+                await this._git.addAnnotatedTag(tagName, annotation);
+            } else {
+                const commands = ['tag', tagName, commitId, '-m', annotation];
+                await this._git.raw(commands);
+            }
+        } catch (error) {
+            SFPLogger.log(
+                `Unable to commit file, probably due to no change or something else,Please try manually`,
+                LoggerLevel.ERROR
+            );
+            throw error;
+        }
+    }
+ 
+    public async isBranchExists(branch: string): Promise<boolean> {
+        const listOfBranches = await this._git.branch(['-la']);
+ 
+        return listOfBranches.all.find((elem) => elem.endsWith(branch)) ? true : false;
+    }
+ 
+    static async initiateRepoAtTempLocation(logger: Logger, commitRef?: string, branch?: string): Promise<Git> {
+        let locationOfCopiedDirectory = tmp.dirSync({ unsafeCleanup: true });
+ 
+        SFPLogger.log(`Copying the repository to ${locationOfCopiedDirectory.name}`, LoggerLevel.INFO, logger);
+        let repoDir = locationOfCopiedDirectory.name;
+ 
+        // Copy source directory to temp dir
+        fs.copySync(process.cwd(), repoDir);
+ 
+        //Initiate git on new repo on using the abstracted object
+        let git = new Git(repoDir, logger);
+        git._isATemporaryRepo = true;
+        git.tempRepoLocation = locationOfCopiedDirectory;
+ 
+        await git.addSafeConfig(repoDir);
+        await git.getRemoteOriginUrl();
+        await git.fetch();
+        if (branch) {
+            await git.createBranch(branch);
+        }
+        if (commitRef) {
+            await git.checkout(commitRef, true);
+        }
+ 
+        SFPLogger.log(
+            `Successfully created temporary repository at ${repoDir} with commit ${commitRef ? commitRef : 'HEAD'}`,
+            LoggerLevel.INFO,
+            logger
+        );
+        return git;
+    }
+ 
+    static async initiateRepo(logger?: Logger, projectDir?: string) {
+        let git = new Git(projectDir, logger);
+        if (projectDir) await git.addSafeConfig(projectDir);
+        else {
+            await git.addSafeConfig(process.cwd());
+        }
+        await git.getRemoteOriginUrl();
+        return git;
+    }
+ 
+    public getRepositoryPath() {
+        return this.repositoryLocation;
+    }
+ 
+    async deleteTempoRepoIfAny() {
+        Iif (this.tempRepoLocation) this.tempRepoLocation.removeCallback();
+    }
+ 
+    async addSafeConfig(repoDir: string) {
+        try
+        {
+        //add workaround for safe directory (https://github.com/actions/runner/issues/2033)
+        await this._git.addConfig('safe.directory', repoDir, false, 'global');
+        }catch(error)
+        {
+            //ignore error
+            SFPLogger.log(`Unable to set safe.directory`,LoggerLevel.TRACE)
+        }
+    }
+ 
+    async pushToRemote(branch: string, isForce: boolean) {
+        Iif (!branch) branch = (await this._git.branch()).current;
+        SFPLogger.log(`Pushing ${branch}`, LoggerLevel.INFO, this.logger);
+        if (process.env.sfp_OVERRIDE_ORIGIN_URL) {
+            await this._git.removeRemote('origin');
+            await this._git.addRemote('origin', process.env.sfp_OVERRIDE_ORIGIN_URL);
+        }
+ 
+        if (isForce) {
+            await this._git.push('origin', branch, [`--force`]);
+        } else {
+            await this._git.push('origin', branch);
+        }
+    }
+ 
+    isATemporaryRepo(): boolean {
+        return this._isATemporaryRepo;
+    }
+ 
+    async getCurrentCommitId() {
+        return this._git.revparse(['HEAD']);
+    }
+ 
+    async checkout(commitRef: string, isForce?: boolean) {
+        if (isForce) {
+            return this._git.checkout(commitRef, [`--force`]);
+        } else return this._git.checkout(commitRef, {});
+    }
+ 
+    async checkoutPath(commitRef: string, path: string, isForce?: boolean) {
+        if (isForce) {
+            return this._git.checkout(commitRef, [path, `--force`]);
+        } else return this._git.checkout(commitRef, [path]);
+    }
+ 
+    async stageChangedFiles(path: string): Promise<boolean> {
+        try {
+            await this._git.add(path);
+            return true;
+        } catch (error) {
+            SFPLogger.log(`Nothing to add, ignoring`, LoggerLevel.INFO, this.logger);
+            return false;
+        }
+    }
+    async createBranch(branch: string) {
+        if (await this.isBranchExists(branch)) {
+            await this._git.checkout(branch, ['-f']);
+            try {
+                // For ease-of-use when running locally and local branch exists
+                await this._git.merge([`refs/remotes/origin/${branch}`]);
+            } catch (error) {
+                SFPLogger.log(`Unable to find remote`, LoggerLevel.TRACE, this.logger);
+            }
+        } else {
+            await this._git.checkout(['-b', branch]);
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/core/git/GitIdentity.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/core/git/GitIdentity.ts.html new file mode 100644 index 000000000..bf6949b0f --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/core/git/GitIdentity.ts.html @@ -0,0 +1,187 @@ + + + + + + Code coverage report for core/git/GitIdentity.ts + + + + + + + + + +
+
+

All files / core/git GitIdentity.ts

+
+ +
+ 10% + Statements + 1/10 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 10% + Lines + 1/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { SimpleGit } from 'simple-git/promise';
+ 
+export default class GitIdentity {
+    constructor(private git: SimpleGit) {}
+ 
+    async setUsernameAndEmail(): Promise<void> {
+        await this.setUsername();
+        await this.setEmail();
+    }
+ 
+    private async setUsername(): Promise<void> {
+        let username: string;
+ 
+        if (process.env.sfp_GIT_USERNAME) {
+            username = process.env.sfp_GIT_USERNAME;
+        } else {
+            username = 'sfp';
+        }
+ 
+        await this.git.addConfig('user.name', username);
+    }
+ 
+    private async setEmail(): Promise<void> {
+        let email: string;
+ 
+        if (process.env.sfp_GIT_EMAIL) {
+            email = process.env.sfp_GIT_EMAIL;
+        } else {
+            email = 'sfp@flxblio.io';
+        }
+ 
+        await this.git.addConfig('user.email', email);
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/core/git/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/core/git/index.html new file mode 100644 index 000000000..c63f4d34a --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/core/git/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for core/git + + + + + + + + + +
+
+

All files core/git

+
+ +
+ 6.36% + Statements + 7/110 +
+ + +
+ 0% + Branches + 0/15 +
+ + +
+ 0% + Functions + 0/30 +
+ + +
+ 6.73% + Lines + 7/104 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
Git.ts +
+
6%6/1000%0/150%0/266.38%6/94
GitIdentity.ts +
+
10%1/10100%0/00%0/410%1/10
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/favicon.png b/packages/sfpowerscripts-cli/coverage/lcov-report/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..c1525b811a167671e9de1fa78aab9f5c0b61cef7 GIT binary patch literal 445 zcmV;u0Yd(XP))rP{nL}Ln%S7`m{0DjX9TLF* zFCb$4Oi7vyLOydb!7n&^ItCzb-%BoB`=x@N2jll2Nj`kauio%aw_@fe&*}LqlFT43 z8doAAe))z_%=P%v^@JHp3Hjhj^6*Kr_h|g_Gr?ZAa&y>wxHE99Gk>A)2MplWz2xdG zy8VD2J|Uf#EAw*bo5O*PO_}X2Tob{%bUoO2G~T`@%S6qPyc}VkhV}UifBuRk>%5v( z)x7B{I~z*k<7dv#5tC+m{km(D087J4O%+<<;K|qwefb6@GSX45wCK}Sn*> + + + + Code coverage report for impl/changelog/OrgsUpdater.ts + + + + + + + + + +
+
+

All files / impl/changelog OrgsUpdater.ts

+
+ +
+ 82.35% + Statements + 28/34 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 6/6 +
+ + +
+ 81.25% + Lines + 26/32 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128  +1x +  +1x +  +  +  +  +4x +4x +4x +4x +  +4x +  +  +2x +  +  +  +  +  +  +2x +  +  +1x +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +  +  +  +  +  +  +  +  +3x +  +  +2x +2x +  +  +  +  +  +  +  +  +1x +  +  +  +1x +1x +  +  +1x +  +  +  +  +  +1x +1x +  +  +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +6x +6x +  +  +  +  +  +  + 
import { ReleaseChangelog, Release, ReleaseId } from './ReleaseChangelog';
+import lodash = require('lodash');
+ 
+export default class OrgsUpdater {
+    private latestReleaseId: ReleaseId;
+    private idOfReleaseWithMatchingHashId: ReleaseId;
+ 
+    constructor(
+        private releaseChangelog: ReleaseChangelog,
+        private latestRelease: Release,
+        private org: string,
+        private releaseWithMatchingHashId: Release
+    ) {
+        this.latestReleaseId = this.convertReleaseToId(this.latestRelease);
+ 
+        if (this.releaseWithMatchingHashId) {
+            this.idOfReleaseWithMatchingHashId = this.convertReleaseToId(this.releaseWithMatchingHashId);
+        }
+    }
+ 
+    update(): void {
+        if (!this.idOfReleaseWithMatchingHashId) {
+            if (this.releaseChangelog.orgs) {
+                let org = this.releaseChangelog.orgs.find((org) => org.name === this.org);
+ 
+                if (org) {
+                    org.releases.push(this.latestReleaseId);
+                    org.latestRelease = org.releases[org.releases.length - 1];
+                    org.retryCount = 0;
+                } else {
+                    this.releaseChangelog.orgs.push({
+                        name: this.org,
+                        releases: [this.latestReleaseId],
+                        latestRelease: this.latestReleaseId,
+                        retryCount: 0,
+                    });
+                }
+            } else {
+                // for backwards-compatibility with pre-existing changelogs
+                this.releaseChangelog.orgs = [
+                    {
+                        name: this.org,
+                        releases: [this.latestReleaseId],
+                        latestRelease: this.latestReleaseId,
+                        retryCount: 0,
+                    },
+                ];
+            }
+            console.log(
+                `Updating ${this.org} org with`,
+                this.latestRelease.names[this.latestRelease.names.length - 1] +
+                    '-' +
+                    this.latestRelease.buildNumber +
+                    `(0)`
+            );
+        } else {
+            // Update orgs
+            let org = this.releaseChangelog.orgs.find((org) => org.name === this.org);
+ 
+            if (org) {
+                let indexOfReleaseToOrg = org.releases.findIndex(
+                    (orgRelease) => orgRelease.hashId === this.idOfReleaseWithMatchingHashId.hashId
+                );
+                if (org.latestRelease.hashId !== this.idOfReleaseWithMatchingHashId.hashId) {
+                    if (indexOfReleaseToOrg >= 0) {
+                        // Update release names in releases to org
+                        org.releases[indexOfReleaseToOrg] = this.idOfReleaseWithMatchingHashId;
+                        org.releases[indexOfReleaseToOrg].date = new Date().toUTCString();
+                    } else {
+                        // Add releaseId in releases to org
+                        org.releases.push(this.idOfReleaseWithMatchingHashId);
+                    }
+ 
+                    // Update latest release
+                    org.latestRelease = this.idOfReleaseWithMatchingHashId;
+                    org.retryCount = 0;
+                } else {
+                    if (lodash.isEqual(org.releases[indexOfReleaseToOrg], this.idOfReleaseWithMatchingHashId)) {
+                        org.retryCount++;
+                    } else {
+                        org.retryCount = 0;
+                    }
+ 
+                    // Update releases names in releases to org & latestRelease
+                    org.releases[indexOfReleaseToOrg] = this.idOfReleaseWithMatchingHashId;
+                    org.latestRelease = this.idOfReleaseWithMatchingHashId;
+                }
+ 
+                console.log(
+                    `Updating ${this.org} org with`,
+                    org.latestRelease.names[org.latestRelease.names.length - 1] +
+                        '-' +
+                        org.latestRelease.buildNumber +
+                        `(${org.retryCount})`
+                );
+            } else {
+                // new org
+                this.releaseChangelog.orgs.push({
+                    name: this.org,
+                    releases: [this.idOfReleaseWithMatchingHashId],
+                    latestRelease: this.idOfReleaseWithMatchingHashId,
+                    retryCount: 0,
+                });
+                console.log(
+                    `Updating ${this.org} org with`,
+                    `${this.idOfReleaseWithMatchingHashId.names[this.idOfReleaseWithMatchingHashId.names.length - 1]}-${
+                        this.idOfReleaseWithMatchingHashId.buildNumber
+                    }(0)`
+                );
+            }
+        }
+    }
+ 
+    /**
+     * Convert Release to Release Id
+     * @param release
+     * @returns
+     */
+    private convertReleaseToId(release: Release): ReleaseId {
+        let releaseNames = [...release.names]; // Shallow copy
+        return {
+            names: releaseNames,
+            buildNumber: release.buildNumber,
+            hashId: release.hashId,
+        };
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/impl/changelog/WorkItemUpdater.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/impl/changelog/WorkItemUpdater.ts.html new file mode 100644 index 000000000..e1462a0d6 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/impl/changelog/WorkItemUpdater.ts.html @@ -0,0 +1,208 @@ + + + + + + Code coverage report for impl/changelog/WorkItemUpdater.ts + + + + + + + + + +
+
+

All files / impl/changelog WorkItemUpdater.ts

+
+ +
+ 100% + Statements + 13/13 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 11/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +421x +  +  +  +1x +1x +  +  +  +  +  +  +  +2x +2x +  +  +  +24x +24x +  +  +  +3x +3x +  +1x +  +  +  +  +  +  +  +  +  +  +3x +  +  +  + 
import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import { Release } from './ReleaseChangelog';
+ 
+ 
+export default class WorkItemUpdater {
+    constructor(private latestRelease: Release, private workItemFilters: string[],private logger?:Logger) {}
+ 
+    /**
+     * Generate work items in latest release
+     */
+    update(): void {
+        for (const workItemFilter of this.workItemFilters) {
+     
+        let workItemFilterRegex: RegExp = RegExp(workItemFilter, 'gi');
+        SFPLogger.log(`Matching...${workItemFilterRegex}`,LoggerLevel.INFO,this.logger);
+ 
+        for (let artifact of this.latestRelease['artifacts']) {
+            for (let commit of artifact['commits']) {
+                let commitMessage: String = commit['message'] + '\n' + commit['body'];
+                let workItems: RegExpMatchArray = commitMessage.match(workItemFilterRegex);
+                if (workItems) {
+                    for (let item of workItems) {
+                        if (this.latestRelease['workItems'][item] == null) {
+                            this.latestRelease['workItems'][item] = new Set<string>();
+                            this.latestRelease['workItems'][item].add(commit['commitId'].slice(0, 8));
+                        } else {
+                            this.latestRelease['workItems'][item].add(commit['commitId'].slice(0, 8));
+                        }
+                    }
+                }
+            }
+        }
+       }
+ 
+        // Convert each work item Set to Array
+        // Enables JSON stringification of work item
+        for (let key in this.latestRelease['workItems']) {
+            this.latestRelease.workItems[key] = Array.from(this.latestRelease.workItems[key]);
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/impl/changelog/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/impl/changelog/index.html new file mode 100644 index 000000000..53eaeeb78 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/impl/changelog/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for impl/changelog + + + + + + + + + +
+
+

All files impl/changelog

+
+ +
+ 87.23% + Statements + 41/47 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 8/8 +
+ + +
+ 86.04% + Lines + 37/43 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
OrgsUpdater.ts +
+
82.35%28/34100%0/0100%6/681.25%26/32
WorkItemUpdater.ts +
+
100%13/13100%0/0100%2/2100%11/11
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/impl/parallelBuilder/UndirectedGraph.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/impl/parallelBuilder/UndirectedGraph.ts.html new file mode 100644 index 000000000..a1c5f37d3 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/impl/parallelBuilder/UndirectedGraph.ts.html @@ -0,0 +1,232 @@ + + + + + + Code coverage report for impl/parallelBuilder/UndirectedGraph.ts + + + + + + + + + +
+
+

All files / impl/parallelBuilder UndirectedGraph.ts

+
+ +
+ 96.55% + Statements + 28/29 +
+ + +
+ 88.88% + Branches + 8/9 +
+ + +
+ 100% + Functions + 7/7 +
+ + +
+ 100% + Lines + 21/21 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +501x +  +  +  +8x +  +  +  +3x +  +  +  +14x +1x +  +  +  +12x +11x +9x +  +8x +8x +  +  +  +  +  +  +  +4x +4x +4x +  +4x +19x +19x +18x +18x +18x +  +15x +  +  +  +  +3x +  +  + 
export default class UndirectedGraph {
+    private _adjacencyList: { [p: string]: string[] };
+ 
+    constructor() {
+        this._adjacencyList = {};
+    }
+ 
+    get adjacencyList() {
+        return this._adjacencyList;
+    }
+ 
+    addVertex(name: string) {
+        if (!this._adjacencyList[name]) this._adjacencyList[name] = [];
+        else throw new Error(`Vertex with name '${name}' already exists`);
+    }
+ 
+    addEdge(vertexA: string, vertexB: string): void {
+        if (vertexA === vertexB) throw new Error('Cannot add an edge to a single vertex');
+        if (!this._adjacencyList[vertexA]) throw new Error(`Vertex with name ${vertexA} does not exist`);
+        if (!this._adjacencyList[vertexB]) throw new Error(`Vertex with name ${vertexB} does not exist`);
+ 
+        if (!this._adjacencyList[vertexA].includes(vertexB)) this._adjacencyList[vertexA].push(vertexB);
+        if (!this._adjacencyList[vertexB].includes(vertexA)) this._adjacencyList[vertexB].push(vertexA);
+    }
+ 
+    /**
+     * Returns vertices in graph, using depth-first search from the starting vertex
+     * @param start
+     */
+    dfs(start: string): string[] {
+        const vertices: string[] = [];
+        const visited: { [p: string]: boolean } = {};
+        const adjacencyList = this._adjacencyList;
+ 
+        (function dfsHandler(vertex) {
+            Iif (!vertex) return null;
+            if (!adjacencyList[vertex]) throw new Error(`Vertex '${vertex}' does not exist`);
+            visited[vertex] = true;
+            vertices.push(vertex);
+            adjacencyList[vertex].forEach((neighbor) => {
+                if (!visited[neighbor]) {
+                    return dfsHandler(neighbor);
+                }
+            });
+        })(start);
+ 
+        return vertices;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/impl/parallelBuilder/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/impl/parallelBuilder/index.html new file mode 100644 index 000000000..8d42389df --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/impl/parallelBuilder/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for impl/parallelBuilder + + + + + + + + + +
+
+

All files impl/parallelBuilder

+
+ +
+ 96.55% + Statements + 28/29 +
+ + +
+ 88.88% + Branches + 8/9 +
+ + +
+ 100% + Functions + 7/7 +
+ + +
+ 100% + Lines + 21/21 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
UndirectedGraph.ts +
+
96.55%28/2988.88%8/9100%7/7100%21/21
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/impl/release/ReleaseDefinition.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/impl/release/ReleaseDefinition.ts.html new file mode 100644 index 000000000..5e969bf1b --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/impl/release/ReleaseDefinition.ts.html @@ -0,0 +1,322 @@ + + + + + + Code coverage report for impl/release/ReleaseDefinition.ts + + + + + + + + + +
+
+

All files / impl/release ReleaseDefinition.ts

+
+ +
+ 70% + Statements + 21/30 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 66.66% + Functions + 4/6 +
+ + +
+ 70% + Lines + 21/30 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80  +1x +1x +1x +1x +1x +  +1x +1x +  +1x +  +  +  +  +5x +5x +  +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +5x +  +  +  +  +  +5x +  +  +  +  +  +  +  +  +  +  +5x +  +  +  +  +5x +5x +  +  +  +4x +  +  +4x +6x +  +  +  +  +  +  +4x +  +  +  + 
import ReleaseDefinitionSchema from './ReleaseDefinitionSchema';
+import Ajv from 'ajv';
+const yaml = require('js-yaml');
+import lodash = require('lodash');
+import get18DigitSalesforceId from '../../utils/Get18DigitSalesforceId';
+import Git from '../../core/git/Git';
+import { ConsoleLogger } from '@flxblio/sfp-logger';
+const fs = require('fs-extra');
+const path = require('path');
+ 
+export default class ReleaseDefinition {
+    get releaseDefinition() {
+        // Return clone of releaseDefinition for immutability
+        return lodash.cloneDeep(this._releaseDefinitionSchema);
+    }
+    private constructor(private _releaseDefinitionSchema: ReleaseDefinitionSchema) {
+        this.validateReleaseDefinition(this._releaseDefinitionSchema);
+ 
+        // Workaround for jsonschema not supporting validation based on dependency value
+        if (this._releaseDefinitionSchema.baselineOrg && !this._releaseDefinitionSchema.skipIfAlreadyInstalled)
+            throw new Error("Release option 'skipIfAlreadyInstalled' must be true for 'baselineOrg'");
+ 
+        if (this._releaseDefinitionSchema.packageDependencies) {
+            this.convertPackageDependenciesIdTo18Digits(this._releaseDefinitionSchema.packageDependencies);
+        }
+    }
+ 
+    public static async loadReleaseDefinition(pathToReleaseDefinition: string) {
+        //Check whether path contains gitRef
+        let releaseDefinitionSchema: ReleaseDefinitionSchema;
+        try {
+            if (pathToReleaseDefinition.includes(':')) {
+                let git = await Git.initiateRepo();
+                await git.fetch();
+                let releaseFile = await git.show([pathToReleaseDefinition]);
+                releaseDefinitionSchema = yaml.load(releaseFile);
+            } else {
+                releaseDefinitionSchema = yaml.load(fs.readFileSync(pathToReleaseDefinition, 'UTF8'));
+            }
+        } catch (error) {
+            throw new Error(`Unable to read the release definition file due to ${JSON.stringify(error)}`);
+        }
+ 
+        let releaseDefinition = new ReleaseDefinition(releaseDefinitionSchema);
+        return releaseDefinition;
+    }
+ 
+    private convertPackageDependenciesIdTo18Digits(packageDependencies: { [p: string]: string }) {
+        for (let pkg in packageDependencies) {
+            packageDependencies[pkg] = get18DigitSalesforceId(packageDependencies[pkg]);
+        }
+    }
+ 
+    private validateReleaseDefinition(releaseDefinition: ReleaseDefinitionSchema): void {
+        let schema = fs.readJSONSync(
+            path.join(__dirname, '..', '..', '..', 'resources', 'schemas', 'releasedefinition.schema.json'),
+            { encoding: 'UTF-8' }
+        );
+ 
+        let validator = new Ajv({ allErrors: true }).compile(schema);
+        let validationResult = validator(releaseDefinition);
+ 
+        if (!validationResult) {
+            let errorMsg: string =
+                `Release definition does not meet schema requirements, ` +
+                `found ${validator.errors.length} validation errors:\n`;
+ 
+            validator.errors.forEach((error, errorNum) => {
+                errorMsg += `\n${errorNum + 1}: ${error.instancePath}: ${error.message} ${JSON.stringify(
+                    error.params,
+                    null,
+                    4
+                )}`;
+            });
+ 
+            throw new Error(errorMsg);
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/impl/release/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/impl/release/index.html new file mode 100644 index 000000000..1b35ad14b --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/impl/release/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for impl/release + + + + + + + + + +
+
+

All files impl/release

+
+ +
+ 70% + Statements + 21/30 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 66.66% + Functions + 4/6 +
+ + +
+ 70% + Lines + 21/30 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ReleaseDefinition.ts +
+
70%21/30100%3/366.66%4/670%21/30
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/index.html new file mode 100644 index 000000000..7f485b199 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/index.html @@ -0,0 +1,581 @@ + + + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 46.8% + Statements + 1172/2504 +
+ + +
+ 35.03% + Branches + 144/411 +
+ + +
+ 45.09% + Functions + 193/428 +
+ + +
+ 46.95% + Lines + 1117/2379 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
src +
+
100%26/26100%3/3100%7/7100%26/26
src/core/apex +
+
33.33%8/24100%0/033.33%2/636.36%8/22
src/core/apex/coverage +
+
61.29%19/3160%3/566.66%6/962.06%18/29
src/core/apextest +
+
43.13%22/5120%1/540%2/542.55%20/47
src/core/artifacts +
+
38.46%30/7853.84%7/1338.46%5/1337.33%28/75
src/core/display +
+
100%2/2100%0/0100%0/0100%2/2
src/core/git +
+
14.87%36/2424.87%2/4110.52%6/5714.09%32/227
src/core/metadata +
+
21.66%52/2400%0/4521.05%4/1921.75%52/239
src/core/org +
+
34.61%27/7841.17%7/1731.25%5/1637.5%27/72
src/core/org/packageQuery +
+
50%2/4100%0/00%0/150%2/4
src/core/package +
+
26.92%35/1309.52%2/2128.57%4/1428%35/125
src/core/package/analyser +
+
67.46%56/8360%6/1050%6/1266.66%52/78
src/core/package/components +
+
74.02%57/7750%2/482.35%14/1774.66%56/75
src/core/package/coverage +
+
93.67%74/7970%7/10100%15/1593.58%73/78
src/core/package/dependencies +
+
85.25%133/15674.35%29/3991.66%22/2484.56%126/149
src/core/package/deploymentFilters +
+
84.09%37/4442.85%3/766.66%2/390.24%37/41
src/core/package/diff +
+
36.44%82/22536.66%11/3036.84%7/1936.48%81/222
src/core/package/packageCreators +
+
14.43%28/1940%0/280%0/4015.73%28/178
src/core/package/propertyFetchers +
+
78.94%15/1966.66%4/6100%3/378.94%15/19
src/core/package/validators +
+
12.5%5/400%0/120%0/313.51%5/37
src/core/package/version +
+
75.92%41/5475%9/1257.14%4/775.55%34/45
src/core/permsets +
+
85.71%48/56100%3/3100%9/984.9%45/53
src/core/project +
+
74.1%83/11273.07%19/2679.31%23/2975%78/104
src/core/queryHelper +
+
100%23/23100%4/4100%3/3100%22/22
src/core/stats +
+
18.18%8/440%0/130%0/919.51%8/41
src/core/stats/nativeMetricSenderImpl +
+
21.05%12/570%0/20%0/1821.05%12/57
src/core/utils +
+
39.86%61/15320.68%6/2936.36%12/3340.41%59/146
src/impl/changelog +
+
87.5%56/64100%0/0100%11/1186.66%52/60
src/impl/dependency +
+
100%27/27100%0/0100%5/5100%26/26
src/impl/parallelBuilder +
+
93.75%45/4881.25%13/1685.71%12/1494.73%36/38
src/impl/release +
+
70%21/30100%3/366.66%4/670%21/30
src/utils +
+
7.69%1/130%0/70%0/18.33%1/12
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/prettify.css b/packages/sfpowerscripts-cli/coverage/lcov-report/prettify.css new file mode 100644 index 000000000..b317a7cda --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/prettify.js b/packages/sfpowerscripts-cli/coverage/lcov-report/prettify.js new file mode 100644 index 000000000..b3225238f --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/sort-arrow-sprite.png b/packages/sfpowerscripts-cli/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed68316eb3f65dec9063332d2f69bf3093bbfab GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qh}Z>jv*C{$p!i!8j}?a+@3A= zIAGwzjijN=FBi!|L1t?LM;Q;gkwn>2cAy-KV{dn nf0J1DIvEHQu*n~6U}x}qyky7vi4|9XhBJ7&`njxgN@xNA8m%nc literal 0 HcmV?d00001 diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/sorter.js b/packages/sfpowerscripts-cli/coverage/lcov-report/sorter.js new file mode 100644 index 000000000..2bb296a8c --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/sorter.js @@ -0,0 +1,196 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + if ( + row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()) + ) { + row.style.display = ''; + } else { + row.style.display = 'none'; + } + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/ProjectValidation.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/ProjectValidation.ts.html new file mode 100644 index 000000000..d677368f2 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/ProjectValidation.ts.html @@ -0,0 +1,346 @@ + + + + + + Code coverage report for src/ProjectValidation.ts + + + + + + + + + +
+
+

All files / src ProjectValidation.ts

+
+ +
+ 100% + Statements + 26/26 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 100% + Functions + 7/7 +
+ + +
+ 100% + Lines + 26/26 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +881x +1x +1x +1x +1x +1x +  +1x +  +  +  +  +  +8x +8x +8x +  +  +  +4x +4x +4x +  +1x +  +1x +7x +  +  +  +  +  +  +  +1x +1x +  +  +  +  +2x +4x +  +1x +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +2x +4x +  +4x +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import ProjectConfig from './core/project/ProjectConfig';
+import Ajv from 'ajv';
+import path = require('path');
+import * as fs from 'fs-extra';
+import { PackageType } from './core//package/SfpPackage';
+import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger';
+ 
+export default class ProjectValidation {
+    private readonly projectConfig;
+    private ajv: Ajv;
+    resourcesDir: string;
+ 
+    constructor() {
+        this.projectConfig = ProjectConfig.getSFDXProjectConfig(null);
+        this.ajv = new Ajv({ allErrors: true });
+        this.resourcesDir = path.join(__dirname, '..', 'resources', 'schemas');
+    }
+ 
+    public validateSFDXProjectJSON() {
+        let schema = fs.readJSONSync(path.join(this.resourcesDir, `sfdx-project.schema.json`), { encoding: 'UTF-8' });
+        let validator = this.ajv.compile(schema);
+        let isSchemaValid = validator(this.projectConfig);
+        if (!isSchemaValid) {
+            let errorMsg: string = `The sfdx-project.json is invalid, Please fix the following errors\n`;
+ 
+            validator.errors.forEach((error, errorNum) => {
+                errorMsg += `\n${errorNum + 1}: ${error.instancePath}: ${error.message} ${JSON.stringify(
+                    error.params,
+                    null,
+                    4
+                )}`;
+            });
+ 
+ 
+          SFPLogger.log(`The following attributes are not recognized by sfp, You might need to remove them`,LoggerLevel.WARN)
+          SFPLogger.log(errorMsg, LoggerLevel.WARN);
+        }
+    }
+ 
+    public validatePackageNames() {
+        ProjectConfig.getAllPackageDirectoriesFromConfig(this.projectConfig).forEach((pkg) => {
+            let name = pkg.package;
+            if ( name.length > 38) {
+                throw new Error(
+                    'sfdx-project.json validation failed for package "' +
+                    pkg['package'] +
+                        '".' +
+                    `Package name exceed maximum length of 38 characters.`
+                )
+            }else if( name.match(/^[a-zA-Z0-9-._~]+$/) === null ){
+                throw new Error(
+                    'sfdx-project.json validation failed for package "' +
+                    pkg['package'] +
+                        '".' +
+                    `Package names can only contain alphanumeric characters and the symbols - . _ ~.`
+                )
+            }
+        });
+    }
+ 
+ 
+    public validatePackageBuildNumbers() {
+        ProjectConfig.getAllPackageDirectoriesFromConfig(this.projectConfig).forEach((pkg) => {
+            let packageType = ProjectConfig.getPackageType(this.projectConfig, pkg.package);
+ 
+            let pattern: RegExp = /NEXT$|LATEST$/i;
+            if (
+                pkg.versionNumber.match(pattern) &&
+                (packageType === PackageType.Source || packageType === PackageType.Data)
+            ) {
+                throw new Error(
+                    'sfdx-project.json validation failed for package "' +
+                        pkg['package'] +
+                        '".' +
+                        ' Build-number keywords "NEXT" & "LATEST" are not supported for ' +
+                        packageType +
+                        ' packages.' +
+                        '\nTry the following:' +
+                        '\n - If package should be built as a ' +
+                        packageType +
+                        ' package, use 0 instead of NEXT/LATEST' +
+                        '\n - If package should be built as an Unlocked package, ensure the package has been created in the Devhub and the ID included in packageAliases of sfdx-project.json'
+                );
+            }
+        });
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/ApexClassFetcher.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/ApexClassFetcher.ts.html new file mode 100644 index 000000000..c2fe836af --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/ApexClassFetcher.ts.html @@ -0,0 +1,166 @@ + + + + + + Code coverage report for src/core/apex/ApexClassFetcher.ts + + + + + + + + + +
+
+

All files / src/core/apex ApexClassFetcher.ts

+
+ +
+ 33.33% + Statements + 4/12 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 33.33% + Functions + 1/3 +
+ + +
+ 36.36% + Lines + 4/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28  +1x +1x +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Connection } from '@salesforce/core';
+import chunkCollection from "../queryHelper/ChunkCollection";
+import QueryHelper from '../queryHelper/QueryHelper';
+ 
+export default class ApexClassFetcher {
+    constructor(private conn: Connection) {}
+ 
+    /**
+     * Query Apex Classes by Name
+     *
+     * @param classNames
+     * @returns
+     */
+    public async fetchApexClassByName(classNames: string[]): Promise<{ Id: string; Name: string }[]> {
+        let result: {Id: string; Name: string}[] = [];
+ 
+        const chunks = chunkCollection(classNames);
+        for (const chunk of chunks) {
+            const formattedChunk = chunk.map(elem => `'${elem}'`).toString(); // transform into formatted string for query
+            const query = `SELECT ID, Name FROM ApexClass WHERE Name IN (${formattedChunk})`;
+ 
+            const records = await QueryHelper.query<{ Id: string; Name: string }>(query, this.conn, false);
+            result = result.concat(records);
+        }
+ 
+        return result;
+    }
+}
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/ApexTriggerFetcher.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/ApexTriggerFetcher.ts.html new file mode 100644 index 000000000..0e93a90bc --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/ApexTriggerFetcher.ts.html @@ -0,0 +1,169 @@ + + + + + + Code coverage report for src/core/apex/ApexTriggerFetcher.ts + + + + + + + + + +
+
+

All files / src/core/apex ApexTriggerFetcher.ts

+
+ +
+ 33.33% + Statements + 4/12 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 33.33% + Functions + 1/3 +
+ + +
+ 36.36% + Lines + 4/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29  +1x +1x +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Connection } from '@salesforce/core';
+import chunkCollection from '../queryHelper/ChunkCollection';
+import QueryHelper from '../queryHelper/QueryHelper';
+ 
+export default class ApexTriggerFetcher {
+    constructor(private conn: Connection) {}
+ 
+    /**
+     * Query Triggers by Name
+     *
+     * @param triggerNames
+     * @returns
+     */
+    public async fetchApexTriggerByName(triggerNames: string[]): Promise<{ Id: string; Name: string }[]> {
+        let result: {Id: string, Name: string}[] = [];
+ 
+        const chunks = chunkCollection(triggerNames);
+        for (const chunk of chunks) {
+            const formattedChunk = chunk.map(elem => `'${elem}'`).toString(); // transform into formatted string for query
+            const query = `SELECT ID, Name FROM ApexTrigger WHERE Name IN (${formattedChunk})`;
+ 
+            const records = await QueryHelper.query<{ Id: string; Name: string }>(query, this.conn, false);
+            result = result.concat(records);
+        }
+ 
+        return result;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/coverage/ApexCodeCoverageAggregateFetcher.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/coverage/ApexCodeCoverageAggregateFetcher.ts.html new file mode 100644 index 000000000..7365d4376 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/coverage/ApexCodeCoverageAggregateFetcher.ts.html @@ -0,0 +1,211 @@ + + + + + + Code coverage report for src/core/apex/coverage/ApexCodeCoverageAggregateFetcher.ts + + + + + + + + + +
+
+

All files / src/core/apex/coverage ApexCodeCoverageAggregateFetcher.ts

+
+ +
+ 33.33% + Statements + 4/12 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 33.33% + Functions + 1/3 +
+ + +
+ 36.36% + Lines + 4/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43  +1x +1x +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Connection } from '@salesforce/core';
+import chunkCollection from '../../queryHelper/ChunkCollection';
+import QueryHelper from '../../queryHelper/QueryHelper';
+ 
+export default class ApexCodeCoverageAggregateFetcher {
+    constructor(private conn: Connection) {}
+ 
+    /**
+     * Query ApexCodeCoverageAggregate by list of ApexClassorTriggerId
+     * @param listOfApexClassOrTriggerId
+     * @returns
+     */
+    public async fetchACCAById(listOfApexClassOrTriggerId: string[]): Promise<{
+        ApexClassOrTriggerId: string;
+        NumLinesCovered: number;
+        NumLinesUncovered: number;
+        Coverage: any;
+    }[]> {
+        let result: {
+            ApexClassOrTriggerId: string;
+            NumLinesCovered: number;
+            NumLinesUncovered: number;
+            Coverage: any;
+        }[] = [];
+ 
+        const chunks = chunkCollection(listOfApexClassOrTriggerId);
+        for (const chunk of chunks) {
+            const formattedChunk = chunk.map(elem => `'${elem}'`).toString();
+            let query = `SELECT ApexClassorTriggerId, NumLinesCovered, NumLinesUncovered, Coverage FROM ApexCodeCoverageAggregate WHERE ApexClassorTriggerId IN (${formattedChunk})`;
+ 
+            const records = await QueryHelper.query<{
+                ApexClassOrTriggerId: string;
+                NumLinesCovered: number;
+                NumLinesUncovered: number;
+                Coverage: any;
+            }>(query, this.conn, true);
+            result = result.concat(records);
+        }
+ 
+        return result;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/coverage/IndividualClassCoverage.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/coverage/IndividualClassCoverage.ts.html new file mode 100644 index 000000000..693cd7836 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/coverage/IndividualClassCoverage.ts.html @@ -0,0 +1,313 @@ + + + + + + Code coverage report for src/core/apex/coverage/IndividualClassCoverage.ts + + + + + + + + + +
+
+

All files / src/core/apex/coverage IndividualClassCoverage.ts

+
+ +
+ 78.94% + Statements + 15/19 +
+ + +
+ 60% + Branches + 3/5 +
+ + +
+ 83.33% + Functions + 5/6 +
+ + +
+ 77.77% + Lines + 14/18 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +772x +  +2x +9x +  +  +  +  +  +2x +  +  +2x +8x +  +  +  +2x +  +  +  +  +  +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +4x +  +  +  +  +4x +13x +  +  +4x +2x +  +  +  +  +  +  +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import SFPLogger, { Logger, LoggerLevel } from "@flxblio/sfp-logger"
+ 
+export default class IndividualClassCoverage {
+    public constructor(private codeCoverage: any, private logger: Logger) {}
+ 
+    public getIndividualClassCoverage(classesToBeValidated?:string[]): ClassCoverage[] {
+        let individualClassCoverage: {
+            name: string;
+            coveredPercent: number;
+        }[] = [];
+ 
+        // Return every class in coverage json if test level is not RunAllTestsInPackage
+        individualClassCoverage = this.codeCoverage.map((cls) => {
+            return { name: cls.name, coveredPercent: cls.coveredPercent };
+        });
+ 
+         // Filter individualClassCoverage based on classesToBeValidated
+        Iif(classesToBeValidated && classesToBeValidated.length > 0)
+        individualClassCoverage = individualClassCoverage.filter((cls) => {
+             return classesToBeValidated.includes(cls.name);
+        });
+ 
+ 
+        return individualClassCoverage;
+    }
+ 
+    public validateIndividualClassCoverage(
+        individualClassCoverage: ClassCoverage[],
+        coverageThreshold?: number
+    ): {
+        result: boolean;
+        message: string;
+        classesCovered?: ClassCoverage[];
+        classesWithInvalidCoverage?: ClassCoverage[];
+    } {
+        if (coverageThreshold < 75) {
+            SFPLogger.log('Setting minimum coverage percentage to 75%.', LoggerLevel.INFO, this.logger);
+            coverageThreshold = 75;
+        }
+ 
+        SFPLogger.log(
+            `Validating individual classes for code coverage greater than ${coverageThreshold} percent`,
+            LoggerLevel.INFO,
+            this.logger
+        );
+        let classesWithInvalidCoverage = individualClassCoverage.filter((cls) => {
+            return cls.coveredPercent < coverageThreshold;
+        });
+ 
+        if (classesWithInvalidCoverage.length > 0) {
+            return {
+                result: false,
+                message: 'There are classes which do not satisfy the individual coverage requirements',
+                classesCovered: individualClassCoverage,
+                classesWithInvalidCoverage: classesWithInvalidCoverage,
+            };
+        } else
+            return {
+                result: true,
+                message: 'All classes in this test run meet the required coverage threshold',
+                classesCovered: individualClassCoverage,
+            };
+    }
+}
+ 
+export type CoverageOptions = {
+    isPackageCoverageToBeValidated: boolean;
+    isIndividualClassCoverageToBeValidated: boolean;
+    coverageThreshold: number;
+    classesToBeValidated?: string[];
+};
+ 
+export type ClassCoverage = {
+    name: string;
+    coveredPercent: number;
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/coverage/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/coverage/index.html new file mode 100644 index 000000000..a4c7908d8 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/coverage/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/core/apex/coverage + + + + + + + + + +
+
+

All files src/core/apex/coverage

+
+ +
+ 61.29% + Statements + 19/31 +
+ + +
+ 60% + Branches + 3/5 +
+ + +
+ 66.66% + Functions + 6/9 +
+ + +
+ 62.06% + Lines + 18/29 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ApexCodeCoverageAggregateFetcher.ts +
+
33.33%4/12100%0/033.33%1/336.36%4/11
IndividualClassCoverage.ts +
+
78.94%15/1960%3/583.33%5/677.77%14/18
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/index.html new file mode 100644 index 000000000..81becdc33 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apex/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/core/apex + + + + + + + + + +
+
+

All files src/core/apex

+
+ +
+ 33.33% + Statements + 8/24 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 33.33% + Functions + 2/6 +
+ + +
+ 36.36% + Lines + 8/22 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ApexClassFetcher.ts +
+
33.33%4/12100%0/033.33%1/336.36%4/11
ApexTriggerFetcher.ts +
+
33.33%4/12100%0/033.33%1/336.36%4/11
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apextest/ApexTestSuite.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apextest/ApexTestSuite.ts.html new file mode 100644 index 000000000..d626dc9d9 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apextest/ApexTestSuite.ts.html @@ -0,0 +1,175 @@ + + + + + + Code coverage report for src/core/apextest/ApexTestSuite.ts + + + + + + + + + +
+
+

All files / src/core/apextest ApexTestSuite.ts

+
+ +
+ 100% + Statements + 16/16 +
+ + +
+ 100% + Branches + 1/1 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 14/14 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31  +1x +1x +1x +1x +  +1x +3x +  +  +3x +  +  +  +  +3x +  +3x +  +2x +  +  +1x +  +1x +1x +1x +  +  +  + 
 
+const fs = require('fs-extra');
+import path from 'path';
+import xml2json from '../utils/xml2json';
+import { globSync } from 'glob';
+ 
+export default class ApexTestSuite {
+    public constructor(private sourceDir: string, private suiteName: string) {}
+ 
+    public async getConstituentClasses(): Promise<string[]> {
+        let testSuitePaths: string[] = globSync(`**${this.suiteName}.testSuite-meta.xml`, {
+            cwd: this.sourceDir,
+            absolute: true,
+        });
+ 
+        console.log('testSuitePaths',testSuitePaths);
+ 
+        if (!testSuitePaths[0]) throw new Error(`Apex Test Suite ${this.suiteName} not found`);
+ 
+        let apex_test_suite: any = await xml2json(fs.readFileSync(path.resolve(testSuitePaths[0])));
+ 
+        if (Array.isArray(apex_test_suite.ApexTestSuite.testClassName)) {
+            return apex_test_suite.ApexTestSuite.testClassName;
+        } else {
+            let testClassess = new Array<string>();
+            testClassess.push(apex_test_suite.ApexTestSuite.testClassName);
+            return testClassess;
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apextest/ImpactedApexTestClassFetcher.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apextest/ImpactedApexTestClassFetcher.ts.html new file mode 100644 index 000000000..8f05f1a9a --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apextest/ImpactedApexTestClassFetcher.ts.html @@ -0,0 +1,355 @@ + + + + + + Code coverage report for src/core/apextest/ImpactedApexTestClassFetcher.ts + + + + + + + + + +
+
+

All files / src/core/apextest ImpactedApexTestClassFetcher.ts

+
+ +
+ 17.14% + Statements + 6/35 +
+ + +
+ 0% + Branches + 0/4 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 18.18% + Lines + 6/33 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +911x +1x +  +1x +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import * as _ from 'lodash';
+import ApexDepedencyCheckImpl from "@flxblio/apexlink/lib/ApexDepedencyCheckImpl"
+import Component from '../dependency/Component';
+import SFPLogger, { COLOR_KEY_MESSAGE, COLOR_WARNING, Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import SfpPackage, { PackageType } from '../package/SfpPackage';
+import path from 'path';
+ 
+export default class ImpactedApexTestClassFetcher {
+    public constructor(
+        private sfpPackage: SfpPackage,
+        private changedComponents: Component[],
+        private logger: Logger,
+        private loglevel?: LoggerLevel
+    ) {}
+ 
+    public async getImpactedTestClasses(): Promise<string[]> {
+    
+        let invalidatedClasses = [];
+        let invalidatedTestClasses = [];
+ 
+        try
+        {
+        let validatedChangedComponents = this.changedComponents.filter(
+            (component) => component.package == this.sfpPackage.packageName
+        );
+ 
+        SFPLogger.log(`Computing impacted apex class and associated tests`, LoggerLevel.INFO, this.logger);
+        SFPLogger.log(`Changed components ${JSON.stringify(validatedChangedComponents)}`, LoggerLevel.INFO, this.logger);
+ 
+    
+       
+        let apexLinkImpl = new ApexDepedencyCheckImpl(this.logger,path.join(this.sfpPackage.workingDirectory, this.sfpPackage.packageDirectory));
+        let dependencies = (await apexLinkImpl.execute()).dependencies;
+ 
+        if(dependencies.length==0)
+        {
+            //go for another attempt
+            SFPLogger.log(`No dependencies found, retrying with apexlink,Retrying again`, LoggerLevel.INFO,this.logger);
+            apexLinkImpl = new ApexDepedencyCheckImpl(this.logger,this.sfpPackage.workingDirectory);
+            dependencies = (await apexLinkImpl.execute()).dependencies;
+        }
+ 
+        SFPLogger.log(`Dependencies: ${JSON.stringify(dependencies)}`, LoggerLevel.INFO,this.logger);
+ 
+        //compute invalidated apex classes
+        for (const changedComponent of validatedChangedComponents) {
+            //If the component is a permset or profile, add every test class
+            //There is a change in security model, add all test classes as invalidated
+            // Temoorarily disabled this check as it is not working as expected
+            if (this.sfpPackage.packageType != PackageType.Diff && _.includes(['Profile', 'PermissionSet', 'SharingRules'], changedComponent.type)) {
+                SFPLogger.log(
+                    COLOR_WARNING(`Change in Security Model, pushing all test classes through`),
+                    LoggerLevel.INFO,
+                    this.logger
+                );
+                invalidatedClasses = invalidatedClasses.concat(this.sfpPackage.apexTestClassses);
+                break;
+            }
+ 
+            for (const apexClass of dependencies) {
+                // push any apex class or test class that is changed, which would then get filtered during subsequent matching with test class
+                Iif (apexClass.name == changedComponent.fullName) invalidatedClasses.push(apexClass.name);
+ 
+                // push any apex class or test class who is dependent on the changed entity
+                for (const dependsOn of apexClass.dependencies) {
+                    Iif (changedComponent.fullName == dependsOn) invalidatedClasses.push(apexClass.name);
+                }
+            }
+        }
+ 
+        SFPLogger.log(`Impacted classes: ${COLOR_KEY_MESSAGE(invalidatedClasses)}`, LoggerLevel.INFO, this.logger);
+        //Filter all apex classes by means of whats is detected in test classes list
+        invalidatedTestClasses = _.intersection(invalidatedClasses, this.sfpPackage.apexTestClassses);
+        SFPLogger.log(
+            `Impacted test classes: ${COLOR_KEY_MESSAGE(invalidatedTestClasses)}`,
+            LoggerLevel.INFO,
+            this.logger
+        );
+        }catch(error)
+        {
+            SFPLogger.log(
+                `Unable to compute impacted test classes, defaulting to all test classes due to error ${error}`,
+                LoggerLevel.ERROR,
+                this.logger
+            );
+            invalidatedClasses = this.sfpPackage.apexTestClassses;
+        }
+        return invalidatedTestClasses;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apextest/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apextest/index.html new file mode 100644 index 000000000..d1dbe87a1 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/apextest/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/core/apextest + + + + + + + + + +
+
+

All files src/core/apextest

+
+ +
+ 43.13% + Statements + 22/51 +
+ + +
+ 20% + Branches + 1/5 +
+ + +
+ 40% + Functions + 2/5 +
+ + +
+ 42.55% + Lines + 20/47 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ApexTestSuite.ts +
+
100%16/16100%1/1100%2/2100%14/14
ImpactedApexTestClassFetcher.ts +
+
17.14%6/350%0/40%0/318.18%6/33
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/artifacts/ArtifactFetcher.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/artifacts/ArtifactFetcher.ts.html new file mode 100644 index 000000000..a597a3f7c --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/artifacts/ArtifactFetcher.ts.html @@ -0,0 +1,757 @@ + + + + + + Code coverage report for src/core/artifacts/ArtifactFetcher.ts + + + + + + + + + +
+
+

All files / src/core/artifacts ArtifactFetcher.ts

+
+ +
+ 38.46% + Statements + 30/78 +
+ + +
+ 53.84% + Branches + 7/13 +
+ + +
+ 38.46% + Functions + 5/13 +
+ + +
+ 37.33% + Lines + 28/75 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +2251x +1x +1x +1x +1x +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +  +1x +  +  +3x +  +  +  +  +3x +1x +1x +1x +1x +2x +  +  +  +  +  +  +  +  +1x +4x +4x +  +  +1x +1x +4x +4x +  +4x +  +  +  +  +1x +1x +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import path = require('path');
+import * as fs from 'fs-extra';
+import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import { globSync } from 'glob';
+import AdmZip = require('adm-zip');
+import semver = require('semver');
+import tar = require('tar');
+ 
+export default class ArtifactFetcher {
+    /**
+     * Decider for which artifact retrieval method to use
+     * Returns empty array if no artifacts are found
+     * @param artifactDirectory
+     * @param sfdx_package
+     */
+    public static fetchArtifacts(artifactDirectory: string, sfdx_package?: string, logger?: Logger): Artifact[] {
+        let result: Artifact[] = [];
+ 
+        if (!fs.existsSync(artifactDirectory)) {
+            throw new Error(`Artifact directory ${path.resolve(artifactDirectory)} does not exist`);
+        }
+ 
+        let artifacts: string[] = this.findArtifacts(artifactDirectory, sfdx_package);
+ 
+        SFPLogger.log(`Artifacts: ${JSON.stringify(artifacts)}`, LoggerLevel.TRACE, logger);
+ 
+        for (let artifact of artifacts) {
+            let artifactFilePaths: Artifact;
+            if (path.extname(artifact) === '.zip') {
+                artifactFilePaths = ArtifactFetcher.fetchArtifactFilePathsFromZipFile(artifact);
+            } else if (path.extname(artifact) === '.tgz') {
+                artifactFilePaths = ArtifactFetcher.fetchArtifactFilePathsFromTarball(artifact);
+            } else {
+                throw new Error(`Unhandled artifact format ${artifact}, neither tar or zip file`);
+            }
+            result.push(artifactFilePaths);
+        }
+ 
+        return result;
+    }
+ 
+    /**
+     * Helper method for retrieving the ArtifactFilePaths of an artifact folder
+     * @param packageMetadataFilePath
+     */
+    private static fetchArtifactFilePathsFromFolder(packageMetadataFilePath: string): Artifact {
+        let sourceDirectory = path.join(path.dirname(packageMetadataFilePath), `source`);
+ 
+        let changelogFilePath = path.join(path.dirname(packageMetadataFilePath), `changelog.json`);
+ 
+        let artifactFilePaths: Artifact = {
+            packageMetadataFilePath: packageMetadataFilePath,
+            sourceDirectoryPath: sourceDirectory,
+            changelogFilePath: changelogFilePath,
+        };
+ 
+        ArtifactFetcher.existsArtifactFilepaths(artifactFilePaths);
+ 
+        return artifactFilePaths;
+    }
+ 
+    /**
+     * Helper method for retrieving ArtifactFilePaths of an artifact zip
+     * @param artifact
+     */
+    private static fetchArtifactFilePathsFromZipFile(artifact: string): Artifact {
+        let unzippedArtifactsDirectory: string = `.sfp/unzippedArtifacts/${this.makefolderid(8)}`;
+ 
+        fs.mkdirpSync(unzippedArtifactsDirectory);
+        let zip = new AdmZip(artifact);
+ 
+        // Overwrite existing files
+        zip.extractAllTo(unzippedArtifactsDirectory, true);
+ 
+        let artifactName: string = path.basename(artifact).match(/.*sfp_artifact/)?.[0];
+        if (artifactName == null) {
+            throw new Error(`Failed to fetch artifact file paths for ${artifact}`);
+        }
+ 
+        let packageMetadataFilePath = path.join(unzippedArtifactsDirectory, artifactName, 'artifact_metadata.json');
+ 
+        let sourceDirectory = path.join(unzippedArtifactsDirectory, artifactName, `source`);
+ 
+        let changelogFilePath = path.join(unzippedArtifactsDirectory, artifactName, `changelog.json`);
+ 
+        let artifactFilePaths: Artifact = {
+            packageMetadataFilePath: packageMetadataFilePath,
+            sourceDirectoryPath: sourceDirectory,
+            changelogFilePath: changelogFilePath,
+        };
+ 
+        ArtifactFetcher.existsArtifactFilepaths(artifactFilePaths);
+ 
+        return artifactFilePaths;
+    }
+ 
+    /**
+     * Helper method for retrieving ArtifactFilePaths of a tarball
+     * @param artifact
+     */
+    private static fetchArtifactFilePathsFromTarball(artifact: string): Artifact {
+        let unzippedArtifactsDirectory: string = `.sfp/unzippedArtifacts/${this.makefolderid(8)}`;
+        fs.mkdirpSync(unzippedArtifactsDirectory);
+ 
+        tar.x({
+            file: artifact,
+            cwd: unzippedArtifactsDirectory,
+            sync: true,
+        });
+ 
+        let packageMetadataFilePath = path.join(unzippedArtifactsDirectory, 'package', 'artifact_metadata.json');
+ 
+        let sourceDirectory = path.join(unzippedArtifactsDirectory, 'package', `source`);
+ 
+        let changelogFilePath = path.join(unzippedArtifactsDirectory, 'package', `changelog.json`);
+ 
+        let artifactFilePaths: Artifact = {
+            packageMetadataFilePath: packageMetadataFilePath,
+            sourceDirectoryPath: sourceDirectory,
+            changelogFilePath: changelogFilePath,
+        };
+ 
+        ArtifactFetcher.existsArtifactFilepaths(artifactFilePaths);
+ 
+        return artifactFilePaths;
+    }
+ 
+    /**
+     * Find zip and tarball artifacts
+     * Artifact format/s:
+     * sfp_artifact_<version>.zip,
+     * [sfdx_package]_sfp_artifact_[version].zip,
+     * [sfdx_package]_sfp_artifact_[version].tgz
+     */
+    public static findArtifacts(artifactDirectory: string, sfdx_package?: string): string[] {
+        let pattern: string;
+        if (sfdx_package) {
+            pattern = `**/*${sfdx_package}_sfp_artifact*.@(zip|tgz)`;
+        } else {
+            pattern = `**/*sfp_artifact*.@(zip|tgz)`;
+        }
+ 
+        let artifacts: string[] = globSync(pattern, {
+            cwd: artifactDirectory,
+            absolute: true,
+        });
+ 
+        if (sfdx_package && artifacts.length > 1) {
+            SFPLogger.log(`Found more than one artifact for ${sfdx_package}`, LoggerLevel.INFO);
+            let latestArtifact: string = ArtifactFetcher.getLatestArtifact(artifacts);
+            SFPLogger.log(`Using latest artifact ${latestArtifact}`, LoggerLevel.INFO);
+            return [latestArtifact];
+        } else return artifacts;
+    }
+ 
+    /**
+     * Get the artifact with the latest semantic version
+     * @param artifacts
+     */
+    private static getLatestArtifact(artifacts: string[]) {
+        // Consider zip & tarball artifacts only
+        artifacts = artifacts.filter((artifact) => {
+            let ext: string = path.extname(artifact);
+            return ext === '.zip' || ext === '.tgz';
+        });
+ 
+        let pattern = new RegExp('(?:^.*)(?:_sfp_artifact[_-])(?<version>.*)(?:\\.zip|\\.tgz)$');
+        let versions: string[] = artifacts.map((artifact) => {
+            let match: RegExpMatchArray = path.basename(artifact).match(pattern);
+            let version = match?.groups.version;
+ 
+            if (version) return version;
+            else Ethrow new Error('Corrupted artifact detected with no version number');
+        });
+ 
+        // Pick artifact with latest semantic version
+        let sortedVersions: string[] = semver.sort(versions);
+        let latestVersion: string = sortedVersions.pop();
+ 
+        return artifacts.find((artifact) => artifact.includes(latestVersion));
+    }
+ 
+    /**
+     * Verify that artifact filepaths exist on the file system
+     * @param artifactFilePaths
+     */
+    private static existsArtifactFilepaths(artifactFilePaths: Artifact): void {
+        Object.values(artifactFilePaths).forEach((filepath) => {
+            Iif (!fs.existsSync(filepath)) throw new Error(`Artifact filepath ${filepath} does not exist`);
+        });
+    }
+ 
+    /**
+     * Decider for task outcome if the artifact cannot be found
+     * @param artifacts_filepaths
+     * @param isToSkipOnMissingArtifact
+     */
+    public static missingArtifactDecider(artifacts: Artifact[], isToSkipOnMissingArtifact: boolean): boolean {
+        if (artifacts.length === 0 && !isToSkipOnMissingArtifact) {
+            throw new Error(`Artifact not found, Please check the inputs`);
+        } else if (artifacts.length === 0 && isToSkipOnMissingArtifact) {
+            SFPLogger.log(
+                `Skipping task as artifact is missing, and 'Skip If no artifact is found' ${isToSkipOnMissingArtifact}`
+            );
+            return true;
+        }
+    }
+ 
+    private static makefolderid(length): string {
+        var result = '';
+        var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+        var charactersLength = characters.length;
+        for (var i = 0; i < length; i++) {
+            result += characters.charAt(Math.floor(Math.random() * charactersLength));
+        }
+        return result;
+    }
+}
+ 
+export interface Artifact {
+    packageMetadataFilePath: string;
+    sourceDirectoryPath?: string;
+    changelogFilePath?: string;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/artifacts/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/artifacts/index.html new file mode 100644 index 000000000..291805c44 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/artifacts/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/core/artifacts + + + + + + + + + +
+
+

All files src/core/artifacts

+
+ +
+ 38.46% + Statements + 30/78 +
+ + +
+ 53.84% + Branches + 7/13 +
+ + +
+ 38.46% + Functions + 5/13 +
+ + +
+ 37.33% + Lines + 28/75 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ArtifactFetcher.ts +
+
38.46%30/7853.84%7/1338.46%5/1337.33%28/75
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/display/TableConstants.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/display/TableConstants.ts.html new file mode 100644 index 000000000..2924471c4 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/display/TableConstants.ts.html @@ -0,0 +1,199 @@ + + + + + + Code coverage report for src/core/display/TableConstants.ts + + + + + + + + + +
+
+

All files / src/core/display TableConstants.ts

+
+ +
+ 100% + Statements + 2/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 2/2 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
 
+export const ZERO_BORDER_TABLE = {
+    top: ' ',
+    'top-mid': ' ',
+    'top-left': ' ',
+    'top-right': ' ',
+    bottom: ' ',
+    'bottom-mid': ' ',
+    'bottom-left': ' ',
+    'bottom-right': ' ',
+    left: '',
+    'left-mid': '',
+    mid: '',
+    'mid-mid': '',
+    right: '',
+    'right-mid': '',
+    middle: ' ',
+};
+ 
+ 
+ 
+export const COLON_MIDDLE_BORDER_TABLE = {
+    top: '',
+    'top-mid': '',
+    'top-left': '',
+    'top-right': '',
+    bottom: '',
+    'bottom-mid': '',
+    'bottom-left': '',
+    'bottom-right': '',
+    left: '',
+    'left-mid': '',
+    mid: '',
+    'mid-mid': '',
+    right: '',
+    'right-mid': '',
+    middle: ':',
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/display/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/display/index.html new file mode 100644 index 000000000..c0e6dc658 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/display/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/core/display + + + + + + + + + +
+
+

All files src/core/display

+
+ +
+ 100% + Statements + 2/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 2/2 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
TableConstants.ts +
+
100%2/2100%0/0100%0/0100%2/2
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/Git.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/Git.ts.html new file mode 100644 index 000000000..0e2e5312a --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/Git.ts.html @@ -0,0 +1,823 @@ + + + + + + Code coverage report for src/core/git/Git.ts + + + + + + + + + +
+
+

All files / src/core/git Git.ts

+
+ +
+ 6% + Statements + 6/100 +
+ + +
+ 0% + Branches + 0/15 +
+ + +
+ 0% + Functions + 0/26 +
+ + +
+ 6.38% + Lines + 6/94 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +2471x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import simplegit, { SimpleGit } from 'simple-git';
+import fs = require('fs-extra');
+import GitIdentity from './GitIdentity';
+const tmp = require('tmp');
+ 
+//Git Abstraction
+export default class Git {
+    private _git: SimpleGit;
+    private repositoryLocation: string;
+    private tempRepoLocation: any;
+    private _isATemporaryRepo: boolean = false;
+ 
+    private constructor(private projectDir?: string, private logger?: Logger) {
+        if (this.projectDir) {
+            this._git = simplegit(this.projectDir);
+            this.repositoryLocation = this.projectDir;
+        } else {
+            this._git = simplegit();
+            this.repositoryLocation = process.cwd();
+        }
+    }
+ 
+    async fetch() {
+        return this._git.fetch('origin');
+    }
+ 
+    async getHeadCommit(): Promise<string> {
+        return this._git.revparse(['HEAD']);
+    }
+ 
+    async show(options: string[]): Promise<string> {
+        return this._git.show(options);
+    }
+ 
+    async tag(options: string[]): Promise<string[]> {
+        let tagResult = await this._git.tag(options);
+ 
+        let temp: string[] = tagResult.split('\n');
+        temp.pop();
+ 
+        return temp;
+    }
+ 
+    async diff(options: string[]): Promise<string[]> {
+        let diffResult = await this._git.diff(options);
+ 
+        let temp: string[] = diffResult.split('\n');
+        temp.pop();
+ 
+        return temp;
+    }
+ 
+    async log(options: string[]): Promise<string[]> {
+        let gitLogResult = await this._git.log(options);
+ 
+        return gitLogResult['all'][0]['hash'].split('\n');
+    }
+ 
+    public async getRemoteOriginUrl(overrideOriginURL?: string): Promise<string> {
+        let remoteOriginURL;
+        if (!overrideOriginURL) {
+            remoteOriginURL = (await this._git.getConfig('remote.origin.url')).value;
+            if (!remoteOriginURL) {
+                remoteOriginURL = (await this._git.getConfig('remote.origin.url')).value;
+            }
+            SFPLogger.log(`Fetched Remote URL ${remoteOriginURL}`, LoggerLevel.DEBUG);
+        } else remoteOriginURL = overrideOriginURL;
+ 
+        Iif (!remoteOriginURL) throw new Error('Remote origin must be set in repository');
+ 
+        return remoteOriginURL;
+    }
+ 
+    public async commitFile(pathToFiles: string[], message = `[skip ci] Autogenerated commit by sfp`) {
+        try {
+            await new GitIdentity(this._git).setUsernameAndEmail();
+            await this._git.add(pathToFiles);
+            await this._git.commit(message);
+            SFPLogger.log(`Committed File ${pathToFiles}`);
+        } catch (error) {
+            SFPLogger.log(
+                `Unable to commit file, probably due to no change or something else,Please try manually`,
+                LoggerLevel.ERROR
+            );
+            throw error;
+        }
+    }
+ 
+    async pushTags(tags?: string[]) {
+        if (!tags) await this._git.pushTags();
+        else {
+            for (let tag of tags) {
+                await this._git.push('origin', tag);
+            }
+        }
+    }
+ 
+    async deleteTags(tags?: string[]) {
+        Iif (tags) await this._git.push('origin', '--delete', tags);
+    }
+ 
+    async addAnnotatedTag(tagName: string, annotation: string, commitId?: string) {
+        try {
+            await new GitIdentity(this._git).setUsernameAndEmail();
+            if (!commitId) {
+                await this._git.addAnnotatedTag(tagName, annotation);
+            } else {
+                const commands = ['tag', tagName, commitId, '-m', annotation];
+                await this._git.raw(commands);
+            }
+        } catch (error) {
+            SFPLogger.log(
+                `Unable to commit file, probably due to no change or something else,Please try manually`,
+                LoggerLevel.ERROR
+            );
+            throw error;
+        }
+    }
+ 
+    public async isBranchExists(branch: string): Promise<boolean> {
+        const listOfBranches = await this._git.branch(['-la']);
+ 
+        return listOfBranches.all.find((elem) => elem.endsWith(branch)) ? true : false;
+    }
+ 
+    static async initiateRepoAtTempLocation(logger: Logger, commitRef?: string, branch?: string): Promise<Git> {
+        let locationOfCopiedDirectory = tmp.dirSync({ unsafeCleanup: true });
+ 
+        SFPLogger.log(`Copying the repository to ${locationOfCopiedDirectory.name}`, LoggerLevel.INFO, logger);
+        let repoDir = locationOfCopiedDirectory.name;
+ 
+        // Copy source directory to temp dir
+        fs.copySync(process.cwd(), repoDir);
+ 
+        //Initiate git on new repo on using the abstracted object
+        let git = new Git(repoDir, logger);
+        git._isATemporaryRepo = true;
+        git.tempRepoLocation = locationOfCopiedDirectory;
+ 
+        await git.addSafeConfig(repoDir);
+        await git.getRemoteOriginUrl();
+        await git.fetch();
+        if (branch) {
+            await git.createBranch(branch);
+        }
+        if (commitRef) {
+            await git.checkout(commitRef, true);
+        }
+ 
+        SFPLogger.log(
+            `Successfully created temporary repository at ${repoDir} with commit ${commitRef ? commitRef : 'HEAD'}`,
+            LoggerLevel.INFO,
+            logger
+        );
+        return git;
+    }
+ 
+    static async initiateRepo(logger?: Logger, projectDir?: string) {
+        let git = new Git(projectDir, logger);
+        if (projectDir) await git.addSafeConfig(projectDir);
+        else {
+            await git.addSafeConfig(process.cwd());
+        }
+        await git.getRemoteOriginUrl();
+        return git;
+    }
+ 
+    public getRepositoryPath() {
+        return this.repositoryLocation;
+    }
+ 
+    async deleteTempoRepoIfAny() {
+        Iif (this.tempRepoLocation) this.tempRepoLocation.removeCallback();
+    }
+ 
+    async addSafeConfig(repoDir: string) {
+        try
+        {
+        //add workaround for safe directory (https://github.com/actions/runner/issues/2033)
+        await this._git.addConfig('safe.directory', repoDir, false, 'global');
+        }catch(error)
+        {
+            //ignore error
+            SFPLogger.log(`Unable to set safe.directory`,LoggerLevel.TRACE)
+        }
+    }
+ 
+    async pushToRemote(branch: string, isForce: boolean) {
+        Iif (!branch) branch = (await this._git.branch()).current;
+        SFPLogger.log(`Pushing ${branch}`, LoggerLevel.INFO, this.logger);
+        if (process.env.sfp_OVERRIDE_ORIGIN_URL) {
+            await this._git.removeRemote('origin');
+            await this._git.addRemote('origin', process.env.sfp_OVERRIDE_ORIGIN_URL);
+        }
+ 
+        if (isForce) {
+            await this._git.push('origin', branch, [`--force`]);
+        } else {
+            await this._git.push('origin', branch);
+        }
+    }
+ 
+    isATemporaryRepo(): boolean {
+        return this._isATemporaryRepo;
+    }
+ 
+    async getCurrentCommitId() {
+        return this._git.revparse(['HEAD']);
+    }
+ 
+    async checkout(commitRef: string, isForce?: boolean) {
+        if (isForce) {
+            return this._git.checkout(commitRef, [`--force`]);
+        } else return this._git.checkout(commitRef, {});
+    }
+ 
+    async checkoutPath(commitRef: string, path: string, isForce?: boolean) {
+        if (isForce) {
+            return this._git.checkout(commitRef, [path, `--force`]);
+        } else return this._git.checkout(commitRef, [path]);
+    }
+ 
+    async stageChangedFiles(path: string): Promise<boolean> {
+        try {
+            await this._git.add(path);
+            return true;
+        } catch (error) {
+            SFPLogger.log(`Nothing to add, ignoring`, LoggerLevel.INFO, this.logger);
+            return false;
+        }
+    }
+    async createBranch(branch: string) {
+        if (await this.isBranchExists(branch)) {
+            await this._git.checkout(branch, ['-f']);
+            try {
+                // For ease-of-use when running locally and local branch exists
+                await this._git.merge([`refs/remotes/origin/${branch}`]);
+            } catch (error) {
+                SFPLogger.log(`Unable to find remote`, LoggerLevel.TRACE, this.logger);
+            }
+        } else {
+            await this._git.checkout(['-b', branch]);
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/GitDiffUtil.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/GitDiffUtil.ts.html new file mode 100644 index 000000000..a326847ca --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/GitDiffUtil.ts.html @@ -0,0 +1,610 @@ + + + + + + Code coverage report for src/core/git/GitDiffUtil.ts + + + + + + + + + +
+
+

All files / src/core/git GitDiffUtil.ts

+
+ +
+ 12.67% + Statements + 9/71 +
+ + +
+ 0% + Branches + 0/14 +
+ + +
+ 0% + Functions + 0/9 +
+ + +
+ 12.85% + Lines + 9/70 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +1761x +1x +1x +  +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import * as path from 'path';
+import * as fs from 'fs-extra';
+import * as _ from 'lodash';
+ 
+import { LoggerLevel } from '@salesforce/core';
+import simplegit, { SimpleGit } from 'simple-git';
+import SFPLogger, { Logger } from '@flxblio/sfp-logger';
+const SEP = /\/|\\/;
+ 
+export interface DiffFileStatus {
+    revisionFrom: string;
+    revisionTo: string;
+    path: string;
+    renamedPath?: string;
+}
+ 
+export interface DiffFile {
+    deleted: DiffFileStatus[];
+    addedEdited: DiffFileStatus[];
+}
+ 
+const git: SimpleGit = simplegit();
+ 
+export default class GitDiffUtils {
+    private gitTreeRevisionTo: {
+        revision: string;
+        path: string;
+    }[];
+ 
+    public async isFileIncludesContent(diffFile: DiffFileStatus, content: string): Promise<boolean> {
+        let fileAsString = await git.show(['--raw', diffFile.revisionFrom]);
+        let result = fileAsString.includes(content);
+        return result;
+    }
+ 
+    public async fetchFileListRevisionTo(revisionTo: string, logger: Logger) {
+        SFPLogger.log('Fetching file list from target revision ' + revisionTo, LoggerLevel.TRACE, logger);
+        this.gitTreeRevisionTo = [];
+        let revisionTree = await git.raw(['ls-tree', '-r', revisionTo]);
+        const sepRegex = /\n|\r/;
+        let lines = revisionTree.split(sepRegex);
+        for (let i = 0; i < lines.length; i++) {
+            Iif (lines[i] === '') continue;
+            let fields = lines[i].split(/\t/);
+            let pathStr = fields[1];
+            let revisionSha = fields[0].split(/\s/)[2];
+            let fileMetadata = {
+                revision: revisionSha,
+                path: path.join('.', pathStr),
+            };
+            this.gitTreeRevisionTo.push(fileMetadata);
+        }
+        return this.gitTreeRevisionTo;
+    }
+ 
+    public async copyFile(filePath: string, outputFolder: string, logger: Logger) {
+        SFPLogger.log(`Copying file ${filePath} from git to ${outputFolder}`, LoggerLevel.TRACE, logger);
+        if (fs.existsSync(path.join(outputFolder, filePath))) {
+            SFPLogger.log(`File ${filePath}  already in output folder. `, LoggerLevel.TRACE, logger);
+            return;
+        }
+ 
+        let gitFiles: {
+            revision: string;
+            path: string;
+        }[] = [];
+        this.gitTreeRevisionTo.forEach((file) => {
+            if (file.path === filePath) {
+                gitFiles.push(file);
+            }
+        });
+ 
+        Iif(gitFiles.length==0)
+          throw new Error(`Unable to find the required file  ${filePath} in Git.., Did you really commit the file?`)
+ 
+        let copyOutputFolder = outputFolder;
+        for (let i = 0; i < gitFiles.length; i++) {
+            outputFolder = copyOutputFolder;
+            let gitFile = gitFiles[i];
+ 
+            SFPLogger.log(
+                `Associated file ${i}: ${gitFile.path}  Revision: ${gitFile.revision}`,
+                LoggerLevel.TRACE,
+                logger
+            );
+ 
+            let outputPath = path.join(outputFolder, gitFile.path);
+ 
+            let filePathParts = gitFile.path.split(SEP);
+ 
+            if (fs.existsSync(outputFolder) == false) {
+                fs.mkdirSync(outputFolder);
+            }
+            // Create folder structure
+            for (let i = 0; i < filePathParts.length - 1; i++) {
+                let folder = filePathParts[i].replace('"', '');
+                outputFolder = path.join(outputFolder, folder);
+                if (fs.existsSync(outputFolder) == false) {
+                    fs.mkdirSync(outputFolder);
+                }
+            }
+            let fileContent = await git.binaryCatFile(['-p', gitFile.revision]);
+            fs.writeFileSync(outputPath, fileContent);
+        }
+    }
+ 
+    public async copyFolder(folderPath: string, outputFolder: string, logger: Logger) {
+        SFPLogger.log(`Copying folder ${folderPath} from git to ${outputFolder}`, LoggerLevel.TRACE, logger);
+        if (fs.existsSync(path.join(outputFolder, folderPath))) {
+            SFPLogger.log(`Folder ${folderPath}  already in output folder. `, LoggerLevel.TRACE, logger);
+            return;
+        }
+ 
+        this.gitTreeRevisionTo.forEach((file) => {
+            let fileToCompare = file.path;
+            if (fileToCompare.startsWith(folderPath)) {
+                this.copyFile(fileToCompare, outputFolder, logger);
+            }
+        });
+    }
+ 
+    public getChangedOrAdded(list1: any[], list2: any[], key: string) {
+        let result: any = {
+            addedEdited: [],
+            deleted: [],
+        };
+ 
+        //Ensure array
+        if (!_.isNil(list1) && !Array.isArray(list1)) {
+            list1 = [list1];
+        }
+        if (!_.isNil(list2) && !Array.isArray(list2)) {
+            list2 = [list2];
+        }
+ 
+        if (_.isNil(list1) && !_.isNil(list2) && list2.length > 0) {
+            result.addedEdited.push(...list2);
+        }
+ 
+        if (_.isNil(list2) && !_.isNil(list1) && list1.length > 0) {
+            result.deleted.push(...list1);
+        }
+ 
+        if (!_.isNil(list1) && !_.isNil(list2)) {
+            list1.forEach((elem1) => {
+                let found = false;
+                for (let i = 0; i < list2.length; i++) {
+                    let elem2 = list2[i];
+                    if (elem1[key] === elem2[key]) {
+                        //check if edited
+                        if (!_.isEqual(elem1, elem2)) {
+                            result.addedEdited.push(elem2);
+                        }
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    result.deleted.push(elem1);
+                }
+            });
+ 
+            //Check for added elements
+ 
+            let addedElement = _.differenceWith(list2, list1, function (element1: any, element2: any) {
+                return element1[key] === element2[key];
+            });
+ 
+            if (!_.isNil(addedElement)) {
+                result.addedEdited.push(...addedElement);
+            }
+        }
+        return result;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/GitIdentity.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/GitIdentity.ts.html new file mode 100644 index 000000000..c6699ec0f --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/GitIdentity.ts.html @@ -0,0 +1,187 @@ + + + + + + Code coverage report for src/core/git/GitIdentity.ts + + + + + + + + + +
+
+

All files / src/core/git GitIdentity.ts

+
+ +
+ 10% + Statements + 1/10 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 10% + Lines + 1/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { SimpleGit } from 'simple-git/promise';
+ 
+export default class GitIdentity {
+    constructor(private git: SimpleGit) {}
+ 
+    async setUsernameAndEmail(): Promise<void> {
+        await this.setUsername();
+        await this.setEmail();
+    }
+ 
+    private async setUsername(): Promise<void> {
+        let username: string;
+ 
+        if (process.env.sfp_GIT_USERNAME) {
+            username = process.env.sfp_GIT_USERNAME;
+        } else {
+            username = 'sfp';
+        }
+ 
+        await this.git.addConfig('user.name', username);
+    }
+ 
+    private async setEmail(): Promise<void> {
+        let email: string;
+ 
+        if (process.env.sfp_GIT_EMAIL) {
+            email = process.env.sfp_GIT_EMAIL;
+        } else {
+            email = 'sfp@flxblio.io';
+        }
+ 
+        await this.git.addConfig('user.email', email);
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/GitTags.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/GitTags.ts.html new file mode 100644 index 000000000..a0f830cca --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/GitTags.ts.html @@ -0,0 +1,538 @@ + + + + + + Code coverage report for src/core/git/GitTags.ts + + + + + + + + + +
+
+

All files / src/core/git GitTags.ts

+
+ +
+ 32.78% + Statements + 20/61 +
+ + +
+ 16.66% + Branches + 2/12 +
+ + +
+ 33.33% + Functions + 6/18 +
+ + +
+ 30.18% + Lines + 16/53 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152  +1x +  +1x +3x +  +  +  +  +  +  +  +  +3x +  +  +  +  +  +  +3x +1x +  +  +  +  +2x +  +  +  +  +2x +  +  +  +  +  +2x +  +2x +2x +  +  +  +24x +  +  +2x +8x +  +  +  +8x +  +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import Git from './Git';
+import child_process = require('child_process');
+ 
+export default class GitTags {
+    constructor(private git: Git, private sfdx_package: string) {}
+ 
+    /***
+     * Returns list of sorted tags, belonging to package, that are reachable from HEAD and
+     * follow the first parent on merge commits.
+     * If there are no tags, returns empty array
+     * @param sfdx_package
+     */
+    async listTagsOnBranch(): Promise<string[]> {
+        let tags: string[] = await this.git.tag([
+            `-l`,
+            `${this.sfdx_package}_v*`,
+            `--sort=creatordate`,
+            `--merged`,
+        ]);
+ 
+        if (tags.length > 0) return this.filterTagsAgainstBranch(tags);
+        else return tags;
+    }
+ 
+    private async filterTagsAgainstBranch(tags: string[]): Promise<string[]> {
+        // Get full-length commit ID's on the current branch, following the first parent on merge commits
+        let commits: string[] = await this.git.log([`--pretty=format:%H`, `--first-parent`]);
+ 
+        // Get the tags' associated commit ID
+        // Dereference (-d) tags into object IDs
+        //TODO: Remove this direct usage
+        let gitShowRefTagsBuffer = child_process.execSync(`git show-ref --tags -d | grep "${this.sfdx_package}_v*"`, {
+            maxBuffer: 5 * 1024 * 1024,
+            stdio: 'pipe',
+            cwd: this.git.getRepositoryPath()
+        });
+ 
+        let gitShowRefTags = gitShowRefTagsBuffer.toString();
+ 
+        let refTags: string[] = gitShowRefTags.split('\n');
+        refTags.pop(); // Remove last empty element
+ 
+        // Filter ref tags, only including tags that point to the branch
+        // By checking whether all 40 digits in the tag commit ID matches an ID in the branch's commit log
+        let refTagsPointingToBranch: string[] = refTags.filter((refTag) => commits.includes(refTag.substring(0, 40)));
+ 
+        // Only match the name of the tags pointing to the branch
+        refTagsPointingToBranch = refTagsPointingToBranch.map(
+            (refTagPointingToBranch) => refTagPointingToBranch.match(/(?:refs\/tags\/)(.*)((?:-ALIGN)|(?:\^{}))/)[1]
+        );
+ 
+        // Filter the sorted tags - only including tags that point to the branch
+        let tagsPointingToBranch: string[] = tags.filter((tag) => refTagsPointingToBranch.includes(tag));
+ 
+        return tagsPointingToBranch;
+    }
+ 
+    public async getVersionFromLatestTag(): Promise<string> {
+        let version: string;
+ 
+        let tags = await this.listTagsOnBranch();
+        let latestTag = tags.pop();
+        if (latestTag) {
+            let match: RegExpMatchArray = latestTag.match(
+                /^.*_v(?<version>[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+|\.LATEST|\.NEXT)?(\-ALIGN)?)$/
+            );
+            if (match) version = this.substituteBuildNumberWithPreRelease(match.groups.version);
+            else throw new Error(`Failed to find valid tag for ${this.sfdx_package}`);
+        } else throw new Error(`Failed to find latest tag for ${this.sfdx_package}`);
+ 
+        return version;
+    }
+ 
+    private substituteBuildNumberWithPreRelease(packageVersionNumber: string) {
+        let segments = packageVersionNumber.split('.');
+        //Strip ALIGN
+        if (segments.length == 4 && segments[3].includes('ALIGN')) {
+            segments[3] = segments[3].substring(0, segments[3].indexOf('-'));
+        }
+ 
+        if (segments.length === 4) {
+            packageVersionNumber = segments.reduce((version, segment, segmentsIdx) => {
+                if (segmentsIdx === 3) return version + '-' + segment;
+                else return version + '.' + segment;
+            });
+        }
+ 
+        return packageVersionNumber;
+    }
+ 
+ 
+    public async limitTags(limit: number): Promise<string[]>{
+        let rawTags = await this.listTagsOnBranch();
+ 
+        if (rawTags.length <= limit) {
+            return [];
+        }
+ 
+        const tags:string [] = rawTags.slice(0, Math.abs(limit) * -1);
+        return tags;
+    }
+ 
+ 
+    public async filteredOldTags(daysToKeep: number, limit?: number): Promise<string[]> {
+        const currentTimestamp = Math.floor(Date.now() / 1000);
+ 
+        let rawTags: string[];
+        if (limit) {
+            rawTags = await this.limitTags(limit);
+        } else {
+            rawTags = await this.listTagsOnBranch();
+        }
+ 
+        if (rawTags.length < 0) {
+            return [];
+        }
+ 
+        let tags: string[] = await this.getTagsWithTimestamps(rawTags);
+ 
+        const filteredTags = tags
+          .map(tagStr => {
+            const [name, timestampStr] = tagStr.split(' ');
+            const timestamp = parseInt(timestampStr, 10);
+            return { name, timestamp };
+          })
+          .filter(tag => {
+            const daysSinceTag = (currentTimestamp - tag.timestamp) / 86400;
+            return tag.name && daysSinceTag > daysToKeep;
+          });
+ 
+        return filteredTags.map(tag => tag.name);
+    }
+ 
+    private async getTagsWithTimestamps(tags: string[]): Promise<string[]> {
+        const timestampPromises: Promise<number>[] = [];
+ 
+        // Create an array of promises that will get the tagger date for each tag
+        tags.forEach((tag: string) => {
+        timestampPromises.push(
+            this.git.log(['--format=%at', `refs/tags/${tag}`])
+            .then((output: string[]) => parseInt(output[0].trim(), 10))
+        );
+        });
+ 
+        // Wait for all promises to resolve and format the output
+        const timestamps: number[] = await Promise.all(timestampPromises);
+        const tagsWithTimestamp = tags.map((tag: string, index: number) => `${tag} ${timestamps[index]}`);
+        return tagsWithTimestamp
+    }
+ 
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/index.html new file mode 100644 index 000000000..d13bfd48b --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/git/index.html @@ -0,0 +1,161 @@ + + + + + + Code coverage report for src/core/git + + + + + + + + + +
+
+

All files src/core/git

+
+ +
+ 14.87% + Statements + 36/242 +
+ + +
+ 4.87% + Branches + 2/41 +
+ + +
+ 10.52% + Functions + 6/57 +
+ + +
+ 14.09% + Lines + 32/227 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
Git.ts +
+
6%6/1000%0/150%0/266.38%6/94
GitDiffUtil.ts +
+
12.67%9/710%0/140%0/912.85%9/70
GitIdentity.ts +
+
10%1/10100%0/00%0/410%1/10
GitTags.ts +
+
32.78%20/6116.66%2/1233.33%6/1830.18%16/53
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/MetadataFiles.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/MetadataFiles.ts.html new file mode 100644 index 000000000..c37923a46 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/MetadataFiles.ts.html @@ -0,0 +1,1114 @@ + + + + + + Code coverage report for src/core/metadata/MetadataFiles.ts + + + + + + + + + +
+
+

All files / src/core/metadata MetadataFiles.ts

+
+ +
+ 6.43% + Statements + 11/171 +
+ + +
+ 0% + Branches + 0/25 +
+ + +
+ 0% + Functions + 0/13 +
+ + +
+ 6.47% + Lines + 11/170 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344  +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
//TODO: Replace with SDR
+import * as path from 'path';
+import { MetadataInfo, METADATA_INFO, MetadataDescribe, SOURCE_EXTENSION_REGEX } from './MetadataInfo';
+import FileUtils from '../utils/Fileutils';
+import * as _ from 'lodash';
+import ignore from 'ignore';
+import * as fs from 'fs-extra';
+import ProjectConfig from '../project/ProjectConfig';
+import { globSync } from 'glob';
+ 
+const SEP = /\/|\\/;
+ 
+export default class MetadataFiles {
+    public static sourceOnly: boolean = false;
+    forceignore: any;
+    public constructor() {
+        if (fs.existsSync('.forceignore')) {
+            this.forceignore = ignore().add(fs.readFileSync('.forceignore', 'utf8').toString());
+        } else {
+            this.forceignore = ignore();
+        }
+    }
+    static getFullApiName(fileName: string): string {
+        let fullName = '';
+        let metadateType = MetadataInfo.getMetadataName(fileName);
+        let splitFilepath = fileName.split(SEP);
+        let isObjectChild = METADATA_INFO.CustomObject.childXmlNames.includes(metadateType);
+        if (isObjectChild) {
+            let objectName = splitFilepath[splitFilepath.length - 3];
+            let fieldName = splitFilepath[splitFilepath.length - 1].split('.')[0];
+            fullName = objectName.concat('.' + fieldName);
+        } else {
+            fullName = splitFilepath[splitFilepath.length - 1].split('.')[0];
+        }
+        return fullName;
+    }
+    static getFullApiNameWithExtension(fileName: string): string {
+        let fullName = '';
+        let metadateType = MetadataInfo.getMetadataName(fileName);
+        let splitFilepath = fileName.split(SEP);
+        let isObjectChild = METADATA_INFO.CustomObject.childXmlNames.includes(metadateType);
+        if (isObjectChild) {
+            let objectName = splitFilepath[splitFilepath.length - 3];
+            let fieldName = splitFilepath[splitFilepath.length - 1];
+            fullName = objectName.concat('.' + fieldName);
+        } else {
+            fullName = splitFilepath[splitFilepath.length - 1];
+        }
+        return fullName;
+    }
+ 
+    public static isCustomMetadata(filepath: string, name: string): boolean {
+        let result = true;
+        let splitFilepath = filepath.split(SEP);
+        let componentName = splitFilepath[splitFilepath.length - 1];
+        componentName = componentName.substring(0, componentName.indexOf('.'));
+        if (name === METADATA_INFO.CustomField.xmlName || name === METADATA_INFO.CustomObject.xmlName) {
+            //Custom Field or Custom Object
+            result = componentName.endsWith('__c') || componentName.endsWith('__mdt');
+        }
+        return result;
+    }
+    public static getMemberNameFromFilepath(filepath: string, name: string): string {
+        let member: string;
+        let splitFilepath = filepath.split(SEP);
+        let lastIndex = splitFilepath.length - 1;
+        let isObjectChild = METADATA_INFO.CustomObject.childXmlNames.includes(name);
+        let metadataDescribe: MetadataDescribe = METADATA_INFO[name];
+        if (isObjectChild) {
+            let objectName = splitFilepath[lastIndex - 2];
+            let fieldName = splitFilepath[lastIndex].split('.')[0];
+            member = objectName.concat('.' + fieldName);
+        } else if (metadataDescribe.inFolder) {
+            let baseName = metadataDescribe.directoryName;
+            let baseIndex = filepath.indexOf(baseName) + baseName.length;
+            let cmpPath = filepath.substring(baseIndex + 1); // add 1 to remove the path seperator
+            cmpPath = cmpPath.substring(0, cmpPath.indexOf('.'));
+            member = cmpPath.replace(SEP, '/');
+        } else {
+            if (SOURCE_EXTENSION_REGEX.test(splitFilepath[lastIndex])) {
+                member = splitFilepath[lastIndex].replace(SOURCE_EXTENSION_REGEX, '');
+            } else {
+                const auraRegExp = new RegExp('aura');
+                const lwcRegExp = new RegExp('lwc');
+                const staticResourceRegExp = new RegExp('staticresources');
+                const experienceBundleRegExp = new RegExp('experiences');
+                if (auraRegExp.test(filepath) || lwcRegExp.test(filepath)) {
+                    member = splitFilepath[lastIndex - 1];
+                } else if (staticResourceRegExp.test(filepath)) {
+                    //Return the fileName
+                    let baseName = 'staticresources';
+                    let baseIndex = filepath.indexOf(baseName) + baseName.length;
+                    let cmpPath = filepath.substring(baseIndex + 1); // add 1 to remove the path seperator
+                    member = cmpPath.split(SEP)[0];
+                    let extension = path.parse(member).ext;
+ 
+                    member = member.replace(new RegExp(extension + '$'), '');
+                } else if (experienceBundleRegExp.test(filepath)) {
+                    //Return the fileName
+                    let baseName = 'experiences';
+                    let baseIndex = filepath.indexOf(baseName) + baseName.length;
+                    let cmpPath = filepath.substring(baseIndex + 1); // add 1 to remove the path seperator
+                    member = cmpPath.split(SEP)[0];
+                    let extension = path.parse(member).ext;
+ 
+                    member = member.replace(new RegExp(extension + '$'), '');
+                } else {
+                    let extension = path.parse(splitFilepath[lastIndex]).ext;
+                    member = splitFilepath[lastIndex].replace(new RegExp(extension + '$'), '');
+                }
+            }
+        }
+        return member;
+    }
+ 
+    public loadComponents(srcFolder: string, checkIgnore = true): void {
+        var metadataFiles: string[] = FileUtils.getAllFilesSync(srcFolder);
+        let keys = Object.keys(METADATA_INFO);
+        if (Array.isArray(metadataFiles) && metadataFiles.length > 0) {
+            metadataFiles.forEach((metadataFile) => {
+                let found = false;
+ 
+                for (let i = 0; i < keys.length; i++) {
+                    let match = false;
+                    if (metadataFile.endsWith(METADATA_INFO[keys[i]].sourceExtension)) {
+                        match = true;
+                    } else if (
+                        METADATA_INFO[keys[i]].inFolder &&
+                        metadataFile.endsWith(METADATA_INFO[keys[i]].folderExtension)
+                    ) {
+                        match = true;
+                    }
+                    if (match) {
+                        if (_.isNil(METADATA_INFO[keys[i]].files)) {
+                            METADATA_INFO[keys[i]].files = [];
+                            METADATA_INFO[keys[i]].components = [];
+                        }
+                        if (!checkIgnore || (checkIgnore && this.accepts(metadataFile))) {
+                            METADATA_INFO[keys[i]].files.push(metadataFile);
+ 
+                            let name = FileUtils.getFileNameWithoutExtension(
+                                metadataFile,
+                                METADATA_INFO[keys[i]].sourceExtension
+                            );
+ 
+                            if (METADATA_INFO[keys[i]].isChildComponent) {
+                                let fileParts = metadataFile.split(SEP);
+                                let parentName = fileParts[fileParts.length - 3];
+                                name = parentName + '.' + name;
+                            }
+ 
+                            METADATA_INFO[keys[i]].components.push(name);
+                        }
+                        found = true;
+                        break;
+                    }
+                }
+ 
+                if (!found) {
+                    const auraRegExp = new RegExp('aura');
+                    if (auraRegExp.test(metadataFile) && SOURCE_EXTENSION_REGEX.test(metadataFile)) {
+                        if (_.isNil(METADATA_INFO.AuraDefinitionBundle.files)) {
+                            METADATA_INFO.AuraDefinitionBundle.files = [];
+                            METADATA_INFO.AuraDefinitionBundle.components = [];
+                        }
+                        if (!checkIgnore || (checkIgnore && this.accepts(metadataFile))) {
+                            METADATA_INFO.AuraDefinitionBundle.files.push(metadataFile);
+ 
+                            let name = FileUtils.getFileNameWithoutExtension(metadataFile);
+                            METADATA_INFO.AuraDefinitionBundle.components.push(name);
+                        }
+                    }
+                }
+            });
+        } else {
+            keys.forEach((key) => {
+                if (_.isNil(METADATA_INFO[key].files)) {
+                    METADATA_INFO[key].files = [];
+                    METADATA_INFO[key].components = [];
+                }
+            });
+        }
+    }
+    //Check if a component is accepted by forceignore.
+    public accepts(filePath: string) {
+        return !this.forceignore.ignores(path.relative(process.cwd(), filePath));
+    }
+ 
+    public async isInModuleFolder(filePath: string) {
+        const packageDirectories = ProjectConfig.getSFDXProjectConfig(null).packageDirectories.map((elem) => elem.path);
+        if (!packageDirectories || packageDirectories.length == 0) {
+            return false;
+        }
+        const moduleFolder = packageDirectories.find((packageFolder) => {
+            let packageFolderNormalized = path.relative('', packageFolder);
+            return filePath.startsWith(packageFolderNormalized);
+        });
+        return moduleFolder !== undefined;
+    }
+ 
+    /**
+     * Copy a file to an outpu directory. If the filePath is a Metadata file Path,
+     * All the metadata requirement are also copied. For example MyApexClass.cls-meta.xml will also copy MyApexClass.cls.
+     * Enforcing the .forceignore to ignire file ignored in the project.
+     * @param filePath
+     * @param outputFolder
+     */
+    public static copyFile(filePath: string, outputFolder: string) {
+        console.log(`Copying file ${filePath} from file system to ${outputFolder}`);
+        const LWC_IGNORE_FILES = ['jsconfig.json', '.eslintrc.json'];
+        const pairStatResources = METADATA_INFO.StaticResource.directoryName;
+        const pairStatResourcesRegExp = new RegExp(pairStatResources);
+        const pairAuaraRegExp = new RegExp(METADATA_INFO.AuraDefinitionBundle.directoryName);
+ 
+        let copyOutputFolder = outputFolder;
+ 
+        if (!fs.existsSync(filePath)) {
+            return;
+        }
+ 
+        let exists = fs.existsSync(path.join(outputFolder, filePath));
+        if (exists) {
+            return;
+        }
+ 
+        if (filePath.startsWith('.')) {
+            let parts = path.parse(filePath);
+            if (parts.dir === '') {
+                fs.copyFileSync(filePath, path.join(outputFolder, filePath));
+                return;
+            }
+        }
+ 
+        let fileName = path.parse(filePath).base;
+        //exclude lwc ignored files
+        if (LWC_IGNORE_FILES.includes(fileName)) {
+            return;
+        }
+ 
+        let filePathParts = filePath.split(SEP);
+ 
+        if (fs.existsSync(outputFolder) == false) {
+            fs.mkdirSync(outputFolder);
+        }
+        // Create folder structure
+        for (let i = 0; i < filePathParts.length - 1; i++) {
+            let folder = filePathParts[i].replace('"', '');
+            outputFolder = path.join(outputFolder, folder);
+            if (fs.existsSync(outputFolder) == false) {
+                fs.mkdirSync(outputFolder);
+            }
+        }
+ 
+        // Copy all file with same base name
+        let associatedFilePattern = '';
+        if (SOURCE_EXTENSION_REGEX.test(filePath)) {
+            associatedFilePattern = filePath.replace(SOURCE_EXTENSION_REGEX, '.*');
+        } else {
+            let extension = path.parse(filePath).ext;
+            associatedFilePattern = filePath.replace(extension, '.*');
+        }
+        let files = globSync(associatedFilePattern);
+        for (let i = 0; i < files.length; i++) {
+            if (fs.lstatSync(files[i]).isDirectory() == false) {
+                let oneFilePath = path.join('.', files[i]);
+                let oneFilePathParts = oneFilePath.split(SEP);
+                fileName = oneFilePathParts[oneFilePathParts.length - 1];
+                let outputPath = path.join(outputFolder, fileName);
+                fs.copyFileSync(files[i], outputPath);
+            }
+        }
+ 
+        // Hadle ObjectTranslations
+        // If a file fieldTranslation is copied, make sure the ObjectTranslation File is also copied
+        if (filePath.endsWith('Translation-meta.xml') && filePath.indexOf('globalValueSet') < 0) {
+            let parentFolder = filePathParts[filePathParts.length - 2];
+            let objectTranslation = parentFolder + METADATA_INFO.CustomObjectTranslation.sourceExtension;
+            let outputPath = path.join(outputFolder, objectTranslation);
+            let sourceFile = filePath.replace(fileName, objectTranslation);
+            if (fs.existsSync(sourceFile) == true) {
+                fs.copyFileSync(sourceFile, outputPath);
+            }
+        }
+ 
+        //FOR STATIC RESOURCES - WHERE THE CORRESPONDING DIRECTORY + THE ROOT META FILE HAS TO BE INCLUDED
+        if (pairStatResourcesRegExp.test(filePath)) {
+            outputFolder = path.join('.', copyOutputFolder);
+            let srcFolder = '.';
+            let staticRecourceRoot = '';
+            let resourceFile = '';
+            for (let i = 0; i < filePathParts.length; i++) {
+                outputFolder = path.join(outputFolder, filePathParts[i]);
+                srcFolder = path.join(srcFolder, filePathParts[i]);
+                if (filePathParts[i] === METADATA_INFO.StaticResource.directoryName) {
+                    let fileOrDirname = filePathParts[i + 1];
+                    let fileOrDirnameParts = fileOrDirname.split('.');
+                    srcFolder = path.join(srcFolder, fileOrDirnameParts[0]);
+                    outputFolder = path.join(outputFolder, fileOrDirnameParts[0]);
+                    resourceFile = srcFolder + METADATA_INFO.StaticResource.sourceExtension;
+                    METADATA_INFO.StaticResource.sourceExtension;
+                    staticRecourceRoot = outputFolder + METADATA_INFO.StaticResource.sourceExtension;
+                    if (fs.existsSync(srcFolder)) {
+                        if (fs.existsSync(outputFolder) == false) {
+                            fs.mkdirSync(outputFolder);
+                        }
+                    }
+                    break;
+                }
+            }
+            if (fs.existsSync(srcFolder)) {
+                FileUtils.copyRecursiveSync(srcFolder, outputFolder);
+            }
+            if (fs.existsSync(resourceFile)) {
+                fs.copyFileSync(resourceFile, staticRecourceRoot);
+            }
+        }
+        //FOR AURA components and LWC components
+        if (pairAuaraRegExp.test(filePath)) {
+            outputFolder = path.join('.', copyOutputFolder);
+            let srcFolder = '.';
+            for (let i = 0; i < filePathParts.length; i++) {
+                outputFolder = path.join(outputFolder, filePathParts[i]);
+                srcFolder = path.join(srcFolder, filePathParts[i]);
+                if (filePathParts[i] === 'aura' || filePathParts[i] === 'lwc') {
+                    let fileOrDirname = filePathParts[i + 1];
+                    let fileOrDirnameParts = fileOrDirname.split('.');
+                    srcFolder = path.join(srcFolder, fileOrDirnameParts[0]);
+                    outputFolder = path.join(outputFolder, fileOrDirnameParts[0]);
+ 
+                    if (fs.existsSync(srcFolder)) {
+                        if (fs.existsSync(outputFolder) == false) {
+                            fs.mkdirSync(outputFolder);
+                        }
+                    }
+                    break;
+                }
+            }
+            if (fs.existsSync(srcFolder)) {
+                FileUtils.copyRecursiveSync(srcFolder, outputFolder);
+            }
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/MetadataInfo.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/MetadataInfo.ts.html new file mode 100644 index 000000000..b4b92d17a --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/MetadataInfo.ts.html @@ -0,0 +1,727 @@ + + + + + + Code coverage report for src/core/metadata/MetadataInfo.ts + + + + + + + + + +
+
+

All files / src/core/metadata MetadataInfo.ts

+
+ +
+ 61.4% + Statements + 35/57 +
+ + +
+ 0% + Branches + 0/20 +
+ + +
+ 75% + Functions + 3/4 +
+ + +
+ 61.4% + Lines + 35/57 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215  +1x +1x +1x +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +1x +1x +1x +1x +149x +  +  +1x +1x +  +1x +1x +  +  +149x +  +4x +  +  +  +4x +  +  +  +  +9x +25x +  +9x +9x +9x +9x +9x +9x +9x +9x +9x +  +  +  +149x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +  +  +  +  +  +  +  +1x + 
//TODO: Replace with SDR
+import * as _ from 'lodash';
+import * as path from 'path';
+import * as fs from 'fs-extra';
+ 
+export const SOURCE_EXTENSION_REGEX = /\.[a-zA-Z]+-meta\.xml/;
+const SPLITED_TYPES = {
+    CustomField: {
+        suffix: 'field',
+        folder: 'fields',
+    },
+    BusinessProcess: {
+        suffix: 'businessProcess',
+        folder: 'businessProcesses',
+    },
+    CompactLayout: {
+        suffix: 'compactLayout',
+        folder: 'compactLayouts',
+    },
+    FieldSet: {
+        suffix: 'fieldSet',
+        folder: 'fieldSets',
+    },
+    RecordType: {
+        suffix: 'recordType',
+        folder: 'recordTypes',
+    },
+    ListView: {
+        suffix: 'listView',
+        folder: 'listViews',
+    },
+    SharingReason: {
+        suffix: 'sharingReason',
+        folder: 'sharingReasons',
+    },
+    ValidationRule: {
+        suffix: 'validationRule',
+        folder: 'validationRules',
+    },
+    WebLink: {
+        suffix: 'webLink',
+        folder: 'webLinks',
+    },
+};
+ 
+export interface MetadataDescribe {
+    directoryName?: string;
+    inFolder?: boolean;
+    metaFile?: boolean;
+    suffix?: string;
+    xmlName?: string;
+    sourceExtension?: string;
+    childXmlNames?: string[];
+    folderExtension?: string;
+    files?: string[];
+    components?: string[];
+    isChildComponent?: boolean;
+}
+ 
+export interface MetadataInfo {
+    CustomApplication?: MetadataDescribe;
+    ApexClass?: MetadataDescribe;
+    ApexPage?: MetadataDescribe;
+    CustomField?: MetadataDescribe;
+    CustomObject?: MetadataDescribe;
+    CustomPermission?: MetadataDescribe;
+    ExternalDataSource?: MetadataDescribe;
+    ExperienceBundle?: MetadataDescribe;
+    Flow?: MetadataDescribe;
+    RecordType?: MetadataDescribe;
+    ListView?: MetadataDescribe;
+    WebLink?: MetadataDescribe;
+    ValidationRule?: MetadataDescribe;
+    CompactLayout?: MetadataDescribe;
+    BujsinessProcess?: MetadataDescribe;
+    CustomTab?: MetadataDescribe;
+    Layout?: MetadataDescribe;
+    Profile?: MetadataDescribe;
+    Translations?: MetadataDescribe;
+    CustomLabel?: MetadataDescribe;
+    CustomLabels?: MetadataDescribe;
+    GlobalValueSet?: MetadataDescribe;
+    CustomMetadata?: MetadataDescribe;
+    Document?: MetadataDescribe;
+    Queue?: MetadataDescribe;
+    Group?: MetadataDescribe;
+    Role?: MetadataDescribe;
+    Report?: MetadataDescribe;
+    Dashboard?: MetadataDescribe;
+    EmailTemplate?: MetadataDescribe;
+    CustomSite?: MetadataDescribe;
+    PermissionSet?: MetadataDescribe;
+    StaticResource?: MetadataDescribe;
+    CustomObjectTranslation?: MetadataDescribe;
+    AuraDefinitionBundle?: MetadataDescribe;
+    Workflow?: MetadataDescribe;
+    SharingRules?: MetadataDescribe;
+    LightningComponentBundle?: MetadataDescribe;
+}
+ 
+export class MetadataInfo {
+    static loadMetadataInfo(): MetadataInfo {
+        let metadataInfo: MetadataInfo = {};
+        let resourcePath = path.join(__dirname, '..', '..', '..', 'resources', 'metadatainfo.json');
+        const fileData = fs.readFileSync(resourcePath, 'utf8');
+        let metadataInfoJSON = JSON.parse(fileData);
+        metadataInfoJSON.metadataObjects.forEach((metadata) => {
+            let metadataDescribe = metadata as MetadataDescribe;
+            if (_.isNil(metadata.suffix)) {
+                if (metadata.xmlName === 'AuraDefinitionBundle') {
+                    metadata.suffix = 'cmp';
+                    metadataDescribe.suffix = 'cmp';
+                } else if (metadata.xmlName == 'LightningComponentBundle') {
+                    metadata.suffix = 'js';
+                    metadataDescribe.suffix = 'js';
+                }
+            }
+            metadataDescribe.sourceExtension = `.${metadata.suffix}-meta.xml`;
+            if (metadata.inFolder) {
+                let folderExtensionPrefix = metadata.suffix;
+                if (_.isNil(metadata.suffix)) {
+                    folderExtensionPrefix = metadata.xmlName.charAt(0).toLowerCase + metadata.xmlName.slice(1);
+                }
+                metadataDescribe.folderExtension = `.${folderExtensionPrefix}Folder-meta.xml`;
+            }
+ 
+            //Generate Describe of cheildItems if exists
+            if (!_.isNil(metadata.childXmlNames)) {
+                metadata.childXmlNames.forEach((element) => {
+                    let splitedElement = SPLITED_TYPES[element];
+                    if (!_.isNil(splitedElement)) {
+                        let childDescribe: MetadataDescribe = {};
+                        childDescribe.directoryName = SPLITED_TYPES[element].folder;
+                        childDescribe.suffix = SPLITED_TYPES[element].suffix;
+                        childDescribe.xmlName = element;
+                        childDescribe.inFolder = false;
+                        childDescribe.metaFile = false;
+                        childDescribe.isChildComponent = true;
+                        childDescribe.sourceExtension = `.${SPLITED_TYPES[element].suffix}-meta.xml`;
+                        metadataInfo[childDescribe.xmlName] = childDescribe;
+                    }
+                });
+            }
+            metadataInfo[metadataDescribe.xmlName] = metadataDescribe;
+        });
+        return metadataInfo;
+    }
+ 
+    static getMetadataName(metadataFile: string, validateSourceExtension = true): string {
+        let matcher = metadataFile.match(SOURCE_EXTENSION_REGEX);
+        let extension = '';
+        if (matcher) {
+            extension = matcher[0];
+        } else {
+            extension = path.parse(metadataFile).ext;
+        }
+        //SfPowerKit.ux.log(extension);
+        let metadataName = '';
+ 
+        const auraRegExp = new RegExp('aura');
+        const lwcRegExp = new RegExp('lwc');
+        const staticResourceRegExp = new RegExp('staticresources');
+        const experienceBundleRegExp = new RegExp('experiences');
+        const documentRegExp = new RegExp('documents');
+        if (auraRegExp.test(metadataFile) && (SOURCE_EXTENSION_REGEX.test(metadataFile) || !validateSourceExtension)) {
+            metadataName = METADATA_INFO.AuraDefinitionBundle.xmlName;
+        } else if (
+            lwcRegExp.test(metadataFile) &&
+            (SOURCE_EXTENSION_REGEX.test(metadataFile) || !validateSourceExtension)
+        ) {
+            metadataName = METADATA_INFO.LightningComponentBundle.xmlName;
+        } else if (
+            staticResourceRegExp.test(metadataFile) &&
+            (SOURCE_EXTENSION_REGEX.test(metadataFile) || !validateSourceExtension)
+        ) {
+            metadataName = METADATA_INFO.StaticResource.xmlName;
+        } else if (
+            experienceBundleRegExp.test(metadataFile) &&
+            (SOURCE_EXTENSION_REGEX.test(metadataFile) || !validateSourceExtension)
+        ) {
+            metadataName = METADATA_INFO.ExperienceBundle.xmlName;
+        } else if (
+            documentRegExp.test(metadataFile) &&
+            (SOURCE_EXTENSION_REGEX.test(metadataFile) || !validateSourceExtension)
+        ) {
+            metadataName = METADATA_INFO.Document.xmlName;
+        } else {
+            let keys = Object.keys(METADATA_INFO);
+            for (let i = 0; i < keys.length; i++) {
+                let metaDescribe = METADATA_INFO[keys[i]];
+                if (
+                    metaDescribe.sourceExtension === extension ||
+                    ('.' + metaDescribe.suffix === extension && !validateSourceExtension) ||
+                    metaDescribe.folderExtension === extension
+                ) {
+                    metadataName = metaDescribe.xmlName;
+                    break;
+                }
+            }
+        }
+        return metadataName;
+    }
+}
+ 
+export const METADATA_INFO = MetadataInfo.loadMetadataInfo();
+export const UNSPLITED_METADATA = [
+    METADATA_INFO.Workflow,
+    METADATA_INFO.SharingRules,
+    METADATA_INFO.CustomLabels,
+    METADATA_INFO.Profile,
+    METADATA_INFO.PermissionSet,
+];
+ 
+export const PROFILE_PERMISSIONSET_EXTENSION = [METADATA_INFO.Profile, METADATA_INFO.PermissionSet];
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/SettingsFetcher.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/SettingsFetcher.ts.html new file mode 100644 index 000000000..8e45990c9 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/SettingsFetcher.ts.html @@ -0,0 +1,151 @@ + + + + + + Code coverage report for src/core/metadata/SettingsFetcher.ts + + + + + + + + + +
+
+

All files / src/core/metadata SettingsFetcher.ts

+
+ +
+ 50% + Statements + 6/12 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 50% + Functions + 1/2 +
+ + +
+ 50% + Lines + 6/12 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +231x +  +1x +1x +1x +  +1x +  +5x +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import SFPOrg from '../org/SFPOrg';
+const fs = require('fs-extra');
+import { XMLParser } from 'fast-xml-parser';
+import MetadataFetcher from './MetadataFetcher';
+ 
+export default class SettingsFetcher extends MetadataFetcher {
+    constructor(logger: Logger) {
+        super(logger);
+    }
+ 
+    public async getSetttingMetadata(org: SFPOrg, setting: string) {
+        SFPLogger.log(`Fetching ${setting}Settings from Org`, LoggerLevel.INFO, this.logger);
+        let retriveLocation = (await this.fetchPackageFromOrg(org, {
+            types: { name: 'Settings', members: setting },
+        })).unzippedLocation;
+        let resultFile = `${retriveLocation}/settings/${setting}.settings`;
+        const parser = new XMLParser();
+        let parsedSettings = parser.parse(fs.readFileSync(resultFile).toString())[`${setting}Settings`];
+        return parsedSettings;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/index.html new file mode 100644 index 000000000..f33f44008 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/metadata/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for src/core/metadata + + + + + + + + + +
+
+

All files src/core/metadata

+
+ +
+ 21.66% + Statements + 52/240 +
+ + +
+ 0% + Branches + 0/45 +
+ + +
+ 21.05% + Functions + 4/19 +
+ + +
+ 21.75% + Lines + 52/239 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
MetadataFiles.ts +
+
6.43%11/1710%0/250%0/136.47%11/170
MetadataInfo.ts +
+
61.4%35/570%0/2075%3/461.4%35/57
SettingsFetcher.ts +
+
50%6/12100%0/050%1/250%6/12
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/SFPOrg.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/SFPOrg.ts.html new file mode 100644 index 000000000..3554dd9a5 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/SFPOrg.ts.html @@ -0,0 +1,898 @@ + + + + + + Code coverage report for src/core/org/SFPOrg.ts + + + + + + + + + +
+
+

All files / src/core/org SFPOrg.ts

+
+ +
+ 34.61% + Statements + 27/78 +
+ + +
+ 41.17% + Branches + 7/17 +
+ + +
+ 31.25% + Functions + 5/16 +
+ + +
+ 37.5% + Lines + 27/72 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +2724x +4x +  +  +4x +4x +4x +4x +  +4x +  +  +  +  +6x +  +6x +  +  +  +  +5x +  +1x +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +3x +  +3x +  +  +  +  +  +  +  +  +  +3x +  +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +  +  +  +  +  +  +  +  +2x +  +  +  +3x +  +3x +  +  +  +  +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +2x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Org } from '@salesforce/core';
+import SFPLogger, { COLOR_KEY_MESSAGE, Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import Package2Detail from '../package/Package2Detail';
+import SfpPackage from '../package/SfpPackage';
+import QueryHelper from '../queryHelper/QueryHelper';
+import { convertUsernameToAlias } from '../utils/AliasList';
+import ObjectCRUDHelper from '../utils/ObjectCRUDHelper';
+import InstalledPackagesQueryExecutor from './packageQuery/InstalledPackagesQueryExecutor';
+ 
+export default class SFPOrg extends Org {
+    /**
+     * Get list of all artifacts in an org
+     */
+    public async getInstalledArtifacts(orderBy: string = `CreatedDate`,logger?:Logger) {
+        let records=[]
+        try {
+             records = await QueryHelper.query<sfpArtifact2__c>(
+                `SELECT Id, Name, CommitId__c, Version__c, Tag__c FROM sfpArtifact2__c ORDER BY ${orderBy} ASC`,
+                this.getConnection(),
+                false
+            );
+            return records;
+        } catch (error) {
+            SFPLogger.log(
+                'Unable to fetch any sfp artifacts in the org\n' +
+                    '1. sfp package is not installed in the org\n' +
+                    '2. The required prerequisite object is not deployed to this org\n',
+                LoggerLevel.WARN,
+                logger
+            );
+        }
+        return records;
+    }
+    /**
+     * Check whether an artifact is installed in a Org
+     * @param  {Logger} logger
+     * @param  {SfpPackage} sfpPackage
+     */
+    public async isArtifactInstalledInOrg(
+        logger: Logger,
+        sfpPackage: SfpPackage
+    ): Promise<{ isInstalled: boolean; versionNumber?: string }> {
+        let result: { isInstalled: boolean; versionNumber?: string } = {
+            isInstalled: false,
+        };
+        try {
+            SFPLogger.log(`Querying for version of ${sfpPackage.packageName} in the Org.`, LoggerLevel.TRACE, logger);
+            result.isInstalled = false;
+            let installedArtifacts = await this.getInstalledArtifacts();
+            let packageName = sfpPackage.packageName;
+            for (const artifact of installedArtifacts) {
+                if (artifact.Name === packageName) {
+                    result.versionNumber = artifact.Version__c;
+                    if (artifact.Version__c === sfpPackage.package_version_number) {
+                        result.isInstalled = true;
+                        return result;
+                    }
+                }
+            }
+        } catch (error) {
+            SFPLogger.log(
+                'Unable to fetch any sfp artifacts in the org\n' +
+                    '1. sfp package is not installed in the org\n' +
+                    '2. The required prerequisite object is not deployed to this org\n',
+                LoggerLevel.WARN,
+                logger
+            );
+        }
+        return result;
+    }
+    /**
+     * Updates or Create information about an artifact in the org
+     * @param  {Logger} logger
+     * @param  {SfpPackage} sfpPackage
+     */
+    public async updateArtifactInOrg(logger: Logger, sfpPackage: SfpPackage): Promise<string> {
+        let artifactId = await this.getArtifactRecordId(sfpPackage);
+ 
+        SFPLogger.log(
+            COLOR_KEY_MESSAGE(
+                `Existing artifact record id for ${sfpPackage.packageName} in Org for ${
+                    sfpPackage.package_version_number
+                }: ${artifactId ? artifactId : 'N/A'}`
+            ),
+            LoggerLevel.INFO,
+            logger
+        );
+ 
+        let packageName = sfpPackage.package_name;
+ 
+        if (artifactId == null) {
+            artifactId = await ObjectCRUDHelper.createRecord(
+                this.getConnection(),
+                'sfpArtifact2__c',
+                {
+                    Name: packageName,
+                    Tag__c: sfpPackage.tag,
+                    Version__c: sfpPackage.package_version_number,
+                    CommitId__c: sfpPackage.sourceVersion,
+                }
+            );
+        } else {
+            artifactId = await ObjectCRUDHelper.updateRecord(
+                this.getConnection(),
+                'sfpArtifact2__c',
+                {
+                    Id: artifactId,
+                    Name: packageName,
+                    Tag__c: sfpPackage.tag,
+                    Version__c: sfpPackage.package_version_number,
+                    CommitId__c: sfpPackage.sourceVersion,
+                }
+            );
+        }
+ 
+        SFPLogger.log(
+            COLOR_KEY_MESSAGE(
+                `Updated Org with new Artifact ${packageName} ${sfpPackage.package_version_number} ${
+                    artifactId ? artifactId : ''
+                }`
+            ),
+            LoggerLevel.INFO,
+            logger
+        );
+        return artifactId;
+    }
+ 
+    private async getArtifactRecordId(sfpPackage: SfpPackage): Promise<string> {
+        let installedArtifacts = await this.getInstalledArtifacts();
+ 
+        let packageName = sfpPackage.packageName;
+        for (const artifact of installedArtifacts) {
+            if (artifact.Name === packageName) {
+                return artifact.Id;
+            }
+        }
+        return null;
+    }
+    /**
+     * Retrieves all packages(recognized by Salesforce) installed in the org
+     */
+    public async getAllInstalled2GPPackages(): Promise<Package2Detail[]> {
+        const installedPackages: Package2Detail[] = [];
+ 
+        let records = await InstalledPackagesQueryExecutor.exec(this.getConnection());
+ 
+        records.forEach((record) => {
+            let packageVersionNumber = `${record.SubscriberPackageVersion.MajorVersion}.${record.SubscriberPackageVersion.MinorVersion}.${record.SubscriberPackageVersion.PatchVersion}.${record.SubscriberPackageVersion.BuildNumber}`;
+ 
+            let packageDetails: Package2Detail = {
+                name: record.SubscriberPackage.Name,
+                package2Id: record.SubscriberPackageId,
+                namespacePrefix: record.SubscriberPackage.NamespacePrefix,
+                subscriberPackageVersionId: record.SubscriberPackageVersion.Id,
+                versionNumber: packageVersionNumber,
+                type: record.SubscriberPackageVersion.Package2ContainerOptions,
+                isOrgDependent: record.SubscriberPackageVersion.IsOrgDependent,
+            };
+ 
+            installedPackages.push(packageDetails);
+        });
+ 
+        return installedPackages;
+    }
+ 
+    /**
+     * Retrives all managed packages in the org
+     */
+    public async getAllInstalledManagedPackages(): Promise<Package2Detail[]> {
+        const installedPackages = await this.getAllInstalled2GPPackages();
+        return installedPackages.filter((installedPackage) => installedPackage.type === 'Managed');
+    }
+    /**
+     *  List all the packages created in DevHub, will throw an error, if its not a DevHub
+     */
+    public async listAllPackages() {
+        if (await this.determineIfDevHubOrg(true)) {
+            let records = await QueryHelper.query<PackageTypeInfo>(packageQuery, this.getConnection(), true);
+            records.forEach((record) => {
+                record.IsOrgDependent =
+                    record.ContainerOptions === 'Managed' ? 'N/A' : record.IsOrgDependent === true ? 'Yes' : 'No';
+            });
+ 
+            return records;
+        } else Ethrow new Error('Package Type Information can only be fetched from a DevHub');
+    }
+ 
+    public async getAlias(): Promise<string> {
+        return await convertUsernameToAlias(this.getUsername());
+    }
+ 
+    /**
+     *  Return all artifacts including sfp as well as external unlocked/managed
+     */
+    public async getAllInstalledArtifacts():Promise<InstalledArtifact[]> {
+        let artifacts = await this.getInstalledArtifacts(`Name`);
+        let installedArtifacts: InstalledArtifact[]=[];
+        let installed2GPPackages = await this.getAllInstalled2GPPackages();
+ 
+        artifacts.forEach((artifact) => {
+            let installedArtifact: InstalledArtifact = {
+                name: artifact.Name,
+                version: artifact.Version__c,
+                commitId:artifact.CommitId__c,
+                isInstalledBysfp: true,
+            };
+            let packageFound = installed2GPPackages.find((elem) => elem.name == artifact.Name);
+            if (packageFound) {
+                installedArtifact.subscriberVersion = packageFound.subscriberPackageVersionId;
+                if (packageFound.isOrgDependent) installedArtifact.type = `OrgDependendent`;
+                else installedArtifact.type = `Unlocked`;
+            } else {
+                installedArtifact.subscriberVersion = `N/A`;
+                installedArtifact.type = `Source/Data`;
+            }
+            installedArtifacts.push(installedArtifact);
+        });
+ 
+        installed2GPPackages.forEach((installed2GPPackage) => {
+            let packageFound = installedArtifacts.find((elem) => elem.name == installed2GPPackage.name);
+            if (!packageFound) {
+                let installedArtifact: InstalledArtifact = {
+                    name: installed2GPPackage.name,
+                    version: installed2GPPackage.versionNumber,
+                    commitId: `N/A`,
+                };
+                if (installed2GPPackage.isOrgDependent) installedArtifact.type = `OrgDependendent`;
+                else if (installed2GPPackage.type == `Managed`) installedArtifact.type = `Managed`;
+                else installedArtifact.type = `Unlocked`;
+ 
+                installedArtifact.subscriberVersion = installed2GPPackage.subscriberPackageVersionId;
+                installedArtifact.isInstalledBysfp = false;
+                installedArtifacts.push(installedArtifact);
+            }
+        });
+        return installedArtifacts;
+    }
+}
+ 
+const packageQuery =
+    'SELECT Id,Name, Description, NamespacePrefix, ContainerOptions, IsOrgDependent ' +
+    'FROM Package2 ' +
+    'WHERE IsDeprecated != true ' +
+    'ORDER BY NamespacePrefix, Name';
+ 
+ 
+export interface InstalledArtifact {
+    name: string;
+    version: string;
+    commitId?: string;
+    subscriberVersion?: string;
+    type?: string;
+    isInstalledBysfp?: boolean;
+}
+ 
+export interface sfpArtifact2__c {
+    Id?: string;
+    Name: string;
+    Tag__c: string;
+    Version__c: string;
+    CommitId__c: string;
+}
+ 
+export interface PackageTypeInfo {
+    Id: string;
+    Name: string;
+    Description: string;
+    NamespacePrefix: string;
+    ContainerOptions: string;
+    IsOrgDependent: boolean | string;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/index.html new file mode 100644 index 000000000..28e9e7a85 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/core/org + + + + + + + + + +
+
+

All files src/core/org

+
+ +
+ 34.61% + Statements + 27/78 +
+ + +
+ 41.17% + Branches + 7/17 +
+ + +
+ 31.25% + Functions + 5/16 +
+ + +
+ 37.5% + Lines + 27/72 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
SFPOrg.ts +
+
34.61%27/7841.17%7/1731.25%5/1637.5%27/72
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/packageQuery/InstalledPackagesQueryExecutor.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/packageQuery/InstalledPackagesQueryExecutor.ts.html new file mode 100644 index 000000000..a40d88bea --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/packageQuery/InstalledPackagesQueryExecutor.ts.html @@ -0,0 +1,127 @@ + + + + + + Code coverage report for src/core/org/packageQuery/InstalledPackagesQueryExecutor.ts + + + + + + + + + +
+
+

All files / src/core/org/packageQuery InstalledPackagesQueryExecutor.ts

+
+ +
+ 50% + Statements + 2/4 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 50% + Lines + 2/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15  +4x +  +4x +  +  +  +  +  +  +  +  +  +  + 
import { Connection } from '@salesforce/core';
+import QueryHelper from '../../queryHelper/QueryHelper';
+ 
+export default class InstalledPackagesQueryExecutor {
+    static async exec(conn: Connection) {
+        const installedPackagesQuery =
+            'SELECT Id, SubscriberPackageId, SubscriberPackage.NamespacePrefix, SubscriberPackage.Name, ' +
+            'SubscriberPackageVersion.Id, SubscriberPackageVersion.Name, SubscriberPackageVersion.MajorVersion, SubscriberPackageVersion.MinorVersion, ' +
+            'SubscriberPackageVersion.PatchVersion, SubscriberPackageVersion.BuildNumber, SubscriberPackageVersion.Package2ContainerOptions, SubscriberPackageVersion.IsOrgDependent FROM InstalledSubscriberPackage ' +
+            'ORDER BY SubscriberPackageId';
+ 
+        return QueryHelper.query<any>(installedPackagesQuery, conn, true);
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/packageQuery/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/packageQuery/index.html new file mode 100644 index 000000000..bb8791528 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/org/packageQuery/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/core/org/packageQuery + + + + + + + + + +
+
+

All files src/core/org/packageQuery

+
+ +
+ 50% + Statements + 2/4 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 50% + Lines + 2/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
InstalledPackagesQueryExecutor.ts +
+
50%2/4100%0/00%0/150%2/4
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/SfpPackage.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/SfpPackage.ts.html new file mode 100644 index 000000000..fc657e941 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/SfpPackage.ts.html @@ -0,0 +1,514 @@ + + + + + + Code coverage report for src/core/package/SfpPackage.ts + + + + + + + + + +
+
+

All files / src/core/package SfpPackage.ts

+
+ +
+ 47.82% + Statements + 11/23 +
+ + +
+ 100% + Branches + 2/2 +
+ + +
+ 50% + Functions + 4/8 +
+ + +
+ 47.82% + Lines + 11/23 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +14413x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +13x +  +  +  +  +  +  +12x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +8x +  +  +  +7x +  +  +  +  +  +  +  +12x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +13x +13x +13x +13x +13x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import _ from 'lodash';
+import { ApexSortedByType } from '../apex/parser/ApexTypeFetcher';
+ 
+export type ApexClasses = Array<string>;
+ 
+class PackageInfo {
+    id?: string;
+    package_name: string;
+    package_version_number?: string;
+    package_version_id?: string;
+    package_type?: string;
+    test_coverage?: number;
+    has_passed_coverage_check?: boolean;
+    repository_url?: string;
+    sourceVersion?: string;
+    branch?: string;
+    apextestsuite?: string;
+    isApexFound?: boolean;
+    assignPermSetsPreDeployment?: string[];
+    assignPermSetsPostDeployment?: string[];
+    apexTestClassses?: string[];
+    isPickListsFound?: boolean;
+    isTriggerAllTests?: boolean;
+    isProfilesFound?: boolean;
+    isPermissionSetGroupFound?: boolean;
+    isPromoted?: boolean;
+    tag?: string;
+    isDependencyValidated?: boolean;
+    destructiveChanges?: any;
+    destructiveChangesPath?: string;
+    payload?: any;
+    metadataCount?: number;
+    sourceDir?: string;
+    dependencies?: any;
+    reconcileProfiles?: boolean;
+    isPayLoadContainTypesSupportedByProfiles?: boolean;
+    creation_details?: { creation_time?: number; timestamp?: number };
+    deployments?: { target_org: string; sub_directory?: string; installation_time?: number; timestamp?: number }[];
+    apiVersion?: string;
+    postDeploymentScript?: string;
+    preDeploymentScript?: string;
+    apexClassWithOutTestClasses?: ApexClasses;
+    triggers?: ApexClasses;
+    configFilePath?: string;
+    packageDescriptor?: any;
+    commitSHAFrom?:string;
+    commitSHATo?:string;
+    packageDirectory?: string;
+    apexClassesSortedByTypes?: ApexSortedByType;
+    projectConfig?: any;
+    changelogFilePath?: string;
+}
+ 
+export default class SfpPackage extends PackageInfo {
+    public projectDirectory: string;
+    public workingDirectory: string;
+    public mdapiDir: string;
+    public destructiveChangesPath: string;
+    public resolvedPackageDirectory: string;
+ 
+    public version: string = '5';
+ 
+    //Just a few helpers to resolve api differene
+    public get packageName(): string {
+        return this.package_name;
+    }
+ 
+    public get versionNumber(): string {
+        return this.package_version_number;
+    }
+ 
+    public set versionNumber(versionNumber:string)
+    {
+        this.package_version_number = versionNumber;
+    }
+ 
+    public get packageType(): string {
+        return this.package_type.toLocaleLowerCase();
+    }
+ 
+    public set packageType(packageType: string) {
+        this.package_type = packageType;
+    }
+    /**
+     * Do not use this constructor directly, use SfPPackageBuilder
+     * to build a package
+     *
+     */
+    public constructor() {
+        super();
+    }
+ 
+    toJSON(): PackageInfo {
+        let castToPackageMetadata = _.cloneDeep(this);
+        delete castToPackageMetadata.workingDirectory;
+        delete castToPackageMetadata.mdapiDir;
+        delete castToPackageMetadata.projectConfig;
+        delete castToPackageMetadata.packageDescriptor;
+        delete castToPackageMetadata.projectDirectory;
+        delete castToPackageMetadata.resolvedPackageDirectory;
+        delete castToPackageMetadata.isTriggerAllTests;
+        return castToPackageMetadata;
+    }
+}
+ 
+ 
+export enum PackageType {
+    Unlocked = "unlocked",
+    Source = "source",
+    Data = "data",
+    Diff = "diff"
+}
+ 
+export interface DiffPackageMetadata {
+ 
+ 
+    sourceVersionFrom?: string;
+    sourceVersionTo?: string;
+    isProfilesFound?: boolean;
+    apexTestClassses?: string[];
+    isApexFound?: boolean;
+    isPicklistFound?: boolean;
+    isPermissionSetGroupFound?: boolean;
+    isPermissionSetFound?: boolean;
+    payload?: any;
+    metadataCount?: number;
+    profilesToReconcile?: number;
+    destructiveChanges?: any;
+    sourceDir?: string;
+    invalidatedTestClasses?: ApexClasses;
+    isPayLoadContainTypesSupportedByProfiles?:boolean;
+}
+export interface SfpPackageParams {
+    overridePackageTypeWith?: string;
+    branch?: string;
+    packageVersionNumber?: string;
+    repositoryUrl?: string;
+    sourceVersion?: string;
+    configFilePath?: string;
+    pathToReplacementForceIgnore?: string;
+    revisionFrom?: string;
+    revisionTo?: string;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/SfpPackageBuilder.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/SfpPackageBuilder.ts.html new file mode 100644 index 000000000..d95dcd7c0 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/SfpPackageBuilder.ts.html @@ -0,0 +1,919 @@ + + + + + + Code coverage report for src/core/package/SfpPackageBuilder.ts + + + + + + + + + +
+
+

All files / src/core/package SfpPackageBuilder.ts

+
+ +
+ 22.42% + Statements + 24/107 +
+ + +
+ 0% + Branches + 0/19 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 23.52% + Lines + 24/102 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +2791x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +  +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +1x +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import ApexTypeFetcher, { ApexSortedByType } from '../apex/parser/ApexTypeFetcher';
+import ProjectConfig from '../project/ProjectConfig';
+import SfpPackageContentGenerator from './generators/SfpPackageContentGenerator';
+import SourceToMDAPIConvertor from './packageFormatConvertors/SourceToMDAPIConvertor';
+import PackageManifest from './components/PackageManifest';
+import MetadataCount from './components/MetadataCount';
+import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import * as fs from 'fs-extra';
+import path from 'path';
+import { Artifact } from '../artifacts/ArtifactFetcher';
+import SfpPackage, { DiffPackageMetadata, PackageType, SfpPackageParams } from './SfpPackage';
+import PropertyFetcher from './propertyFetchers/PropertyFetcher';
+import AssignPermissionSetFetcher from './propertyFetchers/AssignPermissionSetFetcher';
+import DestructiveManifestPathFetcher from './propertyFetchers/DestructiveManifestPathFetcher';
+import ReconcilePropertyFetcher from './propertyFetchers/ReconcileProfilePropertyFetcher';
+import CreateUnlockedPackageImpl from './packageCreators/CreateUnlockedPackageImpl';
+import CreateSourcePackageImpl from './packageCreators/CreateSourcePackageImpl';
+import CreateDataPackageImpl from './packageCreators/CreateDataPackageImpl';
+import lodash = require('lodash');
+import { EOL } from 'os';
+import PackageVersionUpdater from './version/PackageVersionUpdater';
+import { AnalyzerRegistry } from './analyser/AnalyzerRegistry';
+import { ComponentSet } from '@salesforce/source-deploy-retrieve';
+import CreateDiffPackageImp from './packageCreators/CreateDiffPackageImpl';
+import { COLOR_WARNING } from '@flxblio/sfp-logger';
+ 
+export default class SfpPackageBuilder {
+    public static async buildPackageFromProjectDirectory(
+        logger: Logger,
+        projectDirectory: string,
+        sfdx_package: string,
+        params?: SfpPackageParams,
+        packageCreationParams?: PackageCreationParams,
+        projectConfig?: any
+    ) {
+        if (!projectConfig) {
+            projectConfig = ProjectConfig.getSFDXProjectConfig(projectDirectory);
+        } else {
+            // Clone the projectConfig to prevent mutation
+            projectConfig = lodash.cloneDeep(projectConfig);
+        }
+ 
+        let propertyFetchers: PropertyFetcher[] = [
+            new AssignPermissionSetFetcher(),
+            new DestructiveManifestPathFetcher(),
+            new ReconcilePropertyFetcher(),
+        ];
+ 
+        let startTime = Date.now;
+        let sfpPackage: SfpPackage = new SfpPackage();
+        sfpPackage.package_name = sfdx_package;
+        sfpPackage.projectConfig = projectConfig;
+        sfpPackage.apiVersion = sfpPackage.projectConfig.sourceApiVersion;
+        sfpPackage.packageDescriptor = ProjectConfig.getPackageDescriptorFromConfig(
+            sfdx_package,
+            sfpPackage.projectConfig
+        );
+        sfpPackage.projectDirectory = projectDirectory?projectDirectory:'';
+        sfpPackage.packageDirectory = sfpPackage.packageDescriptor.path;
+        //Set Default Version Number
+        sfpPackage.versionNumber = sfpPackage.packageDescriptor.versionNumber;
+ 
+        //set additional options
+        sfpPackage.sourceVersion = params?.sourceVersion;
+        sfpPackage.branch = params?.branch;
+        sfpPackage.repository_url = params?.repositoryUrl;
+        if (params?.configFilePath == null) sfpPackage.configFilePath = 'config/project-scratch-def.json';
+        else sfpPackage.configFilePath = params?.configFilePath;
+ 
+        for (const propertyFetcher of propertyFetchers) {
+            await propertyFetcher.getsfpProperties(sfpPackage, logger);
+        }
+ 
+        //Get Package Type
+        sfpPackage.package_type = ProjectConfig.getPackageType(projectConfig, sfdx_package);
+ 
+        sfpPackage = SfpPackageBuilder.handleVersionNumber(params, sfpPackage, packageCreationParams);
+ 
+        // Requires destructiveChangesPath which is set by the property fetcher
+        sfpPackage.workingDirectory = await SfpPackageContentGenerator.generateSfpPackageDirectory(
+            logger,
+            sfpPackage.projectDirectory,
+            sfpPackage.projectConfig,
+            sfpPackage.packageName,
+            sfpPackage.packageDescriptor.path,
+            sfpPackage.versionNumber,
+            sfpPackage.destructiveChangesPath,
+            sfpPackage.configFilePath,
+            params?.pathToReplacementForceIgnore
+        );
+ 
+        sfpPackage.resolvedPackageDirectory = path.join(sfpPackage.workingDirectory, sfpPackage.packageDescriptor.path);
+ 
+        //Don't proceed further if packageType is Data
+        if (sfpPackage.package_type != PackageType.Data) {
+            let sourceToMdapiConvertor = new SourceToMDAPIConvertor(
+                sfpPackage.workingDirectory,
+                sfpPackage.packageDescriptor.path,
+                ProjectConfig.getSFDXProjectConfig(sfpPackage.workingDirectory).sourceApiVersion,
+                logger
+            );
+            sfpPackage.mdapiDir = (await sourceToMdapiConvertor.convert()).packagePath;
+            const packageManifest: PackageManifest = await PackageManifest.create(sfpPackage.mdapiDir);
+ 
+            sfpPackage.payload = packageManifest.manifestJson;
+            sfpPackage.triggers = packageManifest.fetchTriggers();
+            sfpPackage.isApexFound = packageManifest.isApexInPackage();
+            sfpPackage.isProfilesFound = packageManifest.isProfilesInPackage();
+            sfpPackage.isPermissionSetGroupFound = packageManifest.isPermissionSetGroupsFoundInPackage();
+            sfpPackage.isPayLoadContainTypesSupportedByProfiles = packageManifest.isPayLoadContainTypesSupportedByProfiles();
+ 
+            let apexFetcher: ApexTypeFetcher = new ApexTypeFetcher(sfpPackage.mdapiDir);
+            sfpPackage.apexClassesSortedByTypes = apexFetcher.getClassesClassifiedByType();
+            sfpPackage.apexTestClassses = apexFetcher.getTestClasses();
+            sfpPackage.metadataCount = await MetadataCount.getMetadataCount(
+                sfpPackage.workingDirectory,
+                sfpPackage.packageDescriptor.path
+            );
+            sfpPackage.apexClassWithOutTestClasses = apexFetcher.getClassesOnlyExcludingTestsAndInterfaces();
+ 
+            sfpPackage.isTriggerAllTests = this.isAllTestsToBeTriggered(sfpPackage, logger);
+ 
+            //Load component Set
+            let componentSet = ComponentSet.fromSource(
+                path.resolve(sfpPackage.workingDirectory, sfpPackage.projectDirectory, sfpPackage.packageDirectory)
+            );
+ 
+            //Run through all analyzers
+            let analyzers = AnalyzerRegistry.getAnalyzers();
+            for (const analyzer of analyzers) {
+                Iif (analyzer.isEnabled(sfpPackage, logger)) sfpPackage = await analyzer.analyze(sfpPackage,componentSet, logger);
+            }
+        }
+ 
+        //Create the actual package
+        let createPackage;
+ 
+        Iif (!packageCreationParams) packageCreationParams = { breakBuildIfEmpty: true };
+ 
+        let packageType = sfpPackage.package_type;
+        Iif (params?.overridePackageTypeWith) packageType = params?.overridePackageTypeWith.toLocaleLowerCase();
+ 
+        //Get Implementors
+        switch (packageType) {
+            case PackageType.Unlocked:
+                createPackage = new CreateUnlockedPackageImpl(
+                    sfpPackage.workingDirectory,
+                    sfpPackage,
+                    packageCreationParams,
+                    logger,
+                    params
+                );
+                break;
+            case PackageType.Source:
+                createPackage = new CreateSourcePackageImpl(
+                    sfpPackage.workingDirectory,
+                    sfpPackage,
+                    packageCreationParams,
+                    logger,
+                    params
+                );
+                break;
+            case PackageType.Data:
+                createPackage = new CreateDataPackageImpl(
+                    sfpPackage.workingDirectory,
+                    sfpPackage,
+                    packageCreationParams,
+                    logger,
+                    params
+                );
+                break;
+            case PackageType.Diff:
+                packageCreationParams.revisionFrom = params.revisionFrom;
+                packageCreationParams.revisionTo = params.revisionTo; 
+                createPackage = new CreateDiffPackageImp(
+                    sfpPackage.workingDirectory,
+                    sfpPackage,
+                    packageCreationParams,
+                    logger,
+                    params
+                );
+                break;
+        }
+ 
+        return createPackage.exec();
+    }
+ 
+    /*
+     *  Handle version Numbers of package
+     *  If VersionNumber is explcitly passed, use that
+     * else allow autosubstitute using buildNumber for Source and Data if available
+     */
+    private static handleVersionNumber(
+        params: SfpPackageParams,
+        sfpPackage: SfpPackage,
+        packageCreationParams: PackageCreationParams
+    ) {
+        if (params?.packageVersionNumber) {
+            sfpPackage.versionNumber = params.packageVersionNumber;
+        } else if (packageCreationParams?.buildNumber) {
+            if (sfpPackage.packageType != PackageType.Unlocked) {
+                let versionUpdater: PackageVersionUpdater = new PackageVersionUpdater();
+                sfpPackage.versionNumber = versionUpdater.substituteBuildNumber(
+                    sfpPackage,
+                    packageCreationParams.buildNumber
+                );
+            }
+        } else {
+            sfpPackage.versionNumber = sfpPackage.packageDescriptor.versionNumber;
+        }
+        return sfpPackage;
+    }
+ 
+    public static async buildPackageFromArtifact(artifact: Artifact, logger: Logger): Promise<SfpPackage> {
+        //Read artifact metadata
+        let sfpPackage = new SfpPackage();
+        Object.assign(sfpPackage, fs.readJSONSync(artifact.packageMetadataFilePath, { encoding: 'utf8' }));
+        sfpPackage.sourceDir = artifact.sourceDirectoryPath;
+        sfpPackage.changelogFilePath = artifact.changelogFilePath;
+ 
+        sfpPackage.projectConfig = ProjectConfig.getSFDXProjectConfig(artifact.sourceDirectoryPath);
+        sfpPackage.packageDescriptor = ProjectConfig.getSFDXPackageDescriptor(
+            artifact.sourceDirectoryPath,
+            sfpPackage.package_name
+        );
+        sfpPackage.projectDirectory = artifact.sourceDirectoryPath;
+        sfpPackage.packageDirectory = sfpPackage.packageDescriptor.path;
+        sfpPackage.isTriggerAllTests = this.isAllTestsToBeTriggered(sfpPackage, logger);
+ 
+        return sfpPackage;
+    }
+ 
+  
+ 
+    private static isAllTestsToBeTriggered(sfpPackage: SfpPackage, logger: Logger) {
+        if (
+            this.isOptimizedDeploymentForSourcePackage(sfpPackage) == false ||
+            (sfpPackage.packageType == PackageType.Source &&
+                sfpPackage.isApexFound == true &&
+                sfpPackage.apexTestClassses == null)
+        ) {
+            SFPLogger.printHeaderLine('WARNING!  NON OPTIMAL DEPLOYMENT',COLOR_WARNING,LoggerLevel.INFO,logger);
+            SFPLogger.log(
+                    `This package has apex classes/triggers, In order to deploy optimally, each class need to have a minimum` +
+                    `75% test coverage,We are unable to find any test classes in the given package, hence will be deploying` +
+                    `via triggering all local tests,This definitely is not optimal approach on large orgs` +
+                    `Please consider adding test classes for the classes in the package`,
+                LoggerLevel.INFO,
+                logger
+            );
+            SFPLogger.printHeaderLine('',COLOR_WARNING,LoggerLevel.INFO,logger);
+            return true;
+        } else return false;
+    }
+ 
+    // Allow individual packages to use non optimized path
+    private static isOptimizedDeploymentForSourcePackage(pkgDescriptor: any): boolean {
+        if (pkgDescriptor['isOptimizedDeployment'] == null) return true;
+        else return pkgDescriptor['isOptimizedDeployment'];
+    }
+}
+ 
+// Options while creating package
+export class PackageCreationParams {
+    breakBuildIfEmpty: boolean = true;
+    devHub?: string;
+    installationkeybypass?: boolean;
+    installationkey?: string;
+    waitTime?: string;
+    isCoverageEnabled?: boolean;
+    isSkipValidation?: boolean;
+    isComputeDiffPackage?: boolean;
+    baseBranch?: string;
+    buildNumber?: string;
+    useSelectiveBuildOnly?: boolean;
+    revisionFrom?:string;
+    revisionTo?:string;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/AnalyzerRegistry.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/AnalyzerRegistry.ts.html new file mode 100644 index 000000000..d7a4ed43d --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/AnalyzerRegistry.ts.html @@ -0,0 +1,145 @@ + + + + + + Code coverage report for src/core/package/analyser/AnalyzerRegistry.ts + + + + + + + + + +
+
+

All files / src/core/package/analyser AnalyzerRegistry.ts

+
+ +
+ 33.33% + Statements + 4/12 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 33.33% + Lines + 4/12 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +211x +1x +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import FHTAnalyser from './FHTAnalyzer';
+import FTAnalyser from './FTAnalyzer';
+import { PackageAnalyzer } from './PackageAnalyzer';
+import PicklistAnalyzer from './PicklistAnalyzer';
+ 
+export class AnalyzerRegistry {
+    static getAnalyzers(): PackageAnalyzer[] {
+        let packageAnalyzers: PackageAnalyzer[] = [];
+ 
+        //TODO: Make dynamic
+        let fhtAnalyzer = new FHTAnalyser();
+        let ftAnalyser = new FTAnalyser();
+        let picklistAnalyzer = new PicklistAnalyzer();
+        packageAnalyzers.push(fhtAnalyzer);
+        packageAnalyzers.push(ftAnalyser);
+        packageAnalyzers.push(picklistAnalyzer);
+ 
+        return packageAnalyzers;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/FHTAnalyzer.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/FHTAnalyzer.ts.html new file mode 100644 index 000000000..81ab4b224 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/FHTAnalyzer.ts.html @@ -0,0 +1,313 @@ + + + + + + Code coverage report for src/core/package/analyser/FHTAnalyzer.ts + + + + + + + + + +
+
+

All files / src/core/package/analyser FHTAnalyzer.ts

+
+ +
+ 88.88% + Statements + 24/27 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 75% + Functions + 3/4 +
+ + +
+ 88% + Lines + 22/25 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +772x +2x +2x +2x +2x +  +2x +  +2x +  +  +  +  +  +  +  +  +  +  +4x +  +  +4x +  +  +  +  +  +  +  +  +  +3x +  +  +  +  +4x +  +  +4x +4x +  +  +  +  +  +4x +  +  +  +  +  +  +4x +  +  +  +  +  +  +3x +  +2x +2x +2x +  +  +4x +  +  +  +3x +1x +  +  + 
import path from 'path';
+import * as fs from 'fs-extra';
+import * as yaml from 'js-yaml';
+import { ComponentSet, registry } from '@salesforce/source-deploy-retrieve';
+import SfpPackage, { PackageType } from '../SfpPackage';
+import { PackageAnalyzer } from './PackageAnalyzer';
+import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+ 
+export default class FHTAnalyser implements PackageAnalyzer {
+   
+   public getName() {
+       return "Field History Tracking Analyzer"
+    }
+ 
+    
+ 
+    public async analyze(sfpPackage: SfpPackage, componentSet:ComponentSet, logger:Logger): Promise<SfpPackage> {
+        try {
+ 
+            let fhtFields: { [key: string]: Array<string> } = {};
+ 
+            //read the yaml
+            let fhtYamlPath = path.join(
+                sfpPackage.workingDirectory,
+                sfpPackage.projectDirectory,
+                sfpPackage.packageDirectory,
+                '/postDeploy/history-tracking.yml'
+            );
+ 
+            //read components mentioned in yaml
+            if (fs.existsSync(fhtYamlPath)) {
+                //convert yaml to json
+                fhtFields = yaml.load(fs.readFileSync(fhtYamlPath, { encoding: 'utf-8' })) as {[key: string]: string[]};
+            }
+ 
+ 
+            //filter the components in the package
+            fhtFields = await this.addFieldsFromComponentSet(fhtFields, componentSet);
+ 
+            if (Object.keys(fhtFields).length>0) {
+                sfpPackage['isFHTFieldFound'] = true;
+                sfpPackage['fhtFields'] = fhtFields;
+            }
+        } catch (error) {
+            //Ignore error for now
+            SFPLogger.log(`Unable to process Field History Tracking due to ${error.message}`,LoggerLevel.TRACE,logger);
+        }
+        return sfpPackage;
+    }
+ 
+    private async addFieldsFromComponentSet(
+        fhtFields: { [key: string]: Array<string> },
+        componentSet: ComponentSet
+    ): Promise<Record<string, Array<string>>> {
+        let sourceComponents = componentSet.getSourceComponents().toArray();
+ 
+        for (const sourceComponent of sourceComponents) {
+            if (sourceComponent.type.name !== registry.types.customobject.children.types.customfield.name) {
+                continue;
+            }
+ 
+            let customField = sourceComponent.parseXmlSync().CustomField;
+            if (customField['trackHistory'] == 'true') {
+                let objName = sourceComponent.parent.fullName;
+                if (!fhtFields[objName]) fhtFields[objName] = [];
+                fhtFields[objName].push(sourceComponent.name);
+            }
+        }
+        return fhtFields;
+    }
+ 
+    public async isEnabled(sfpPackage: SfpPackage,logger:Logger): Promise<boolean> {
+        if (sfpPackage.packageType != PackageType.Data) return true;
+        else return false;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/FTAnalyzer.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/FTAnalyzer.ts.html new file mode 100644 index 000000000..3e5afcd7b --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/FTAnalyzer.ts.html @@ -0,0 +1,307 @@ + + + + + + Code coverage report for src/core/package/analyser/FTAnalyzer.ts + + + + + + + + + +
+
+

All files / src/core/package/analyser FTAnalyzer.ts

+
+ +
+ 88.88% + Statements + 24/27 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 75% + Functions + 3/4 +
+ + +
+ 88% + Lines + 22/25 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +752x +2x +2x +2x +2x +  +2x +  +2x +  +  +  +  +  +  +  +  +4x +  +  +4x +  +  +  +  +  +  +  +  +  +3x +  +  +  +  +4x +  +  +4x +4x +  +  +  +  +  +4x +  +  +  +  +  +  +4x +  +  +  +  +  +  +3x +  +2x +2x +2x +  +  +4x +  +  +  +3x +1x +  +  + 
import path from 'path';
+import * as fs from 'fs-extra';
+import * as yaml from 'js-yaml';
+import { ComponentSet, registry } from '@salesforce/source-deploy-retrieve';
+import SfpPackage, { PackageType } from '../SfpPackage';
+import { PackageAnalyzer } from './PackageAnalyzer';
+import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+ 
+export default class FTAnalyser implements PackageAnalyzer {
+ 
+    public getName(): string {
+        return "Feed Tracking Analyzer";
+    };
+ 
+    public async analyze(sfpPackage: SfpPackage, componentSet:ComponentSet, logger:Logger): Promise<SfpPackage> {
+        try {
+ 
+            let ftFields: { [key: string]: Array<string> } = {};
+ 
+            //read the yaml
+            let ftYamlPath = path.join(
+                sfpPackage.workingDirectory,
+                sfpPackage.projectDirectory,
+                sfpPackage.packageDirectory,
+                '/postDeploy/feed-tracking.yml'
+            );
+ 
+            //read components mentioned in yaml
+            if (fs.existsSync(ftYamlPath)) {
+                //convert yaml to json
+                ftFields = yaml.load(fs.readFileSync(ftYamlPath, { encoding: 'utf-8' })) as {[key: string]: string[]};
+            }
+ 
+ 
+            //filter the components in the package
+            ftFields = await this.addFieldsFromComponentSet(ftFields, componentSet);
+ 
+            if (Object.keys(ftFields).length>0) {
+                sfpPackage['isFTFieldFound'] = true;
+                sfpPackage['ftFields'] = ftFields;
+            }
+        } catch (error) {
+            //Ignore error for now
+            SFPLogger.log(`Unable to process Feed Tracking due to ${error.message}`,LoggerLevel.TRACE,logger);
+        }
+        return sfpPackage;
+    }
+ 
+    private async addFieldsFromComponentSet(
+        ftFields: { [key: string]: Array<string> },
+        componentSet: ComponentSet
+    ): Promise<Record<string, Array<string>>> {
+        let sourceComponents = componentSet.getSourceComponents().toArray();
+ 
+        for (const sourceComponent of sourceComponents) {
+            if (sourceComponent.type.name !== registry.types.customobject.children.types.customfield.name) {
+                continue;
+            }
+ 
+            let customField = sourceComponent.parseXmlSync().CustomField;
+            if (customField['trackFeedHistory'] == 'true') {
+                let objName = sourceComponent.parent.fullName;
+                if (!ftFields[objName]) ftFields[objName] = [];
+                ftFields[objName].push(sourceComponent.name);
+            }
+        }
+        return ftFields;
+    }
+ 
+    public async isEnabled(sfpPackage: SfpPackage,logger:Logger): Promise<boolean> {
+        if (sfpPackage.packageType != PackageType.Data) return true;
+        else return false;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/PicklistAnalyzer.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/PicklistAnalyzer.ts.html new file mode 100644 index 000000000..9cc8a8baf --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/PicklistAnalyzer.ts.html @@ -0,0 +1,241 @@ + + + + + + Code coverage report for src/core/package/analyser/PicklistAnalyzer.ts + + + + + + + + + +
+
+

All files / src/core/package/analyser PicklistAnalyzer.ts

+
+ +
+ 23.52% + Statements + 4/17 +
+ + +
+ 0% + Branches + 0/4 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 25% + Lines + 4/16 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +531x +1x +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { ComponentSet, registry } from '@salesforce/source-deploy-retrieve';
+import SfpPackage, { PackageType } from '../SfpPackage';
+import { PackageAnalyzer } from './PackageAnalyzer';
+import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+ 
+export default class PicklistAnalyzer implements PackageAnalyzer {
+ 
+    public getName() {
+        return "Picklist Analyzer"
+     }
+ 
+     
+     
+    public async analyze(sfpPackage: SfpPackage, componentSet:ComponentSet, logger:Logger): Promise<SfpPackage> {
+        try {
+            let sourceComponents = componentSet.getSourceComponents().toArray();
+            let components = [];
+ 
+            for (const sourceComponent of sourceComponents) {
+                if (sourceComponent.type.name == registry.types.customobject.name) {
+                    //issues/1367
+                    //this can add child elements that are not custom fields..
+                    components.push(...sourceComponent.getChildren());
+                }
+ 
+                if (sourceComponent.type.name == registry.types.customobject.children.types.customfield.name) {
+                    components.push(sourceComponent);
+                }
+            }
+ 
+            if (components) {
+                for (const fieldComponent of components) {
+                    let customField = fieldComponent.parseXmlSync().CustomField;
+                    //issues/1367
+                    //if the component isn't a field customField will be undefined..so check
+                    if (customField && customField['type'] == 'Picklist') {
+                        sfpPackage.isPickListsFound= true;
+                        break;
+                    }
+                }
+            }
+        } catch (error) {
+            SFPLogger.log(`Unable to process Picklist update due to ${error.message}`,LoggerLevel.TRACE,logger);
+        }
+        return sfpPackage;
+    }
+ 
+    public async isEnabled(sfpPackage: SfpPackage,logger:Logger): Promise<boolean> {
+        if (sfpPackage.packageType != PackageType.Data) return true;
+        else return false;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/index.html new file mode 100644 index 000000000..a5dc40752 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/analyser/index.html @@ -0,0 +1,161 @@ + + + + + + Code coverage report for src/core/package/analyser + + + + + + + + + +
+
+

All files src/core/package/analyser

+
+ +
+ 67.46% + Statements + 56/83 +
+ + +
+ 60% + Branches + 6/10 +
+ + +
+ 50% + Functions + 6/12 +
+ + +
+ 66.66% + Lines + 52/78 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
AnalyzerRegistry.ts +
+
33.33%4/12100%0/00%0/133.33%4/12
FHTAnalyzer.ts +
+
88.88%24/27100%3/375%3/488%22/25
FTAnalyzer.ts +
+
88.88%24/27100%3/375%3/488%22/25
PicklistAnalyzer.ts +
+
23.52%4/170%0/40%0/325%4/16
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/components/PackageManifest.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/components/PackageManifest.ts.html new file mode 100644 index 000000000..32b806077 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/components/PackageManifest.ts.html @@ -0,0 +1,898 @@ + + + + + + Code coverage report for src/core/package/components/PackageManifest.ts + + + + + + + + + +
+
+

All files / src/core/package/components PackageManifest.ts

+
+ +
+ 82.08% + Statements + 55/67 +
+ + +
+ 50% + Branches + 2/4 +
+ + +
+ 93.33% + Functions + 14/15 +
+ + +
+ 81.81% + Lines + 54/66 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +2722x +2x +  +2x +2x +  +2x +  +  +  +  +  +  +  +3x +  +  +  +  +  +  +3x +  +  +  +  +  +  +  +  +  +  +6x +  +6x +  +6x +6x +  +6x +  +  +  +  +  +  +  +  +  +6x +  +6x +  +  +  +  +  +6x +150x +  +  +6x +  +  +42x +  +  +  +42x +  +  +  +6x +  +  +  +6x +  +  +  +6x +6x +  +6x +  +  +  +  +  +  +  +  +1x +1x +  +1x +  +  +  +1x +  +1x +  +  +  +  +  +  +  +2x +  +  +  +  +  +1x +1x +  +  +  +  +  +  +  +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +  +  +  +1x +1x +  +  +  +  +  +2x +  +  +  +  +  +  +  +1x +  +  +  +  +  +1x +1x +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +2x +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +2x +  +  +  +1x +  +  +  +  +1x +1x +  +  +  +  +  +  +1x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +1x +1x +  +  +  +  +  +  +1x +  +  + 
import path from 'path';
+import * as fs from 'fs-extra';
+import { ApexClasses } from '../SfpPackage';
+import xml2json from '../../utils/xml2json';
+const xml2js = require('xml2js');
+ 
+export default class PackageManifest {
+    private _manifestJson;
+    private _manifestXml: string;
+ 
+    /**
+     * Getter for package manifest JSON
+     */
+    get manifestJson() {
+        return this._manifestJson;
+    }
+ 
+    /**
+     * Getter for package manifest XML
+     */
+    get manifestXml(): string {
+        return this._manifestXml;
+    }
+ 
+    private constructor() {}
+ 
+    /**
+     * Factory method
+     * @param mdapiDir directory containing package.xml
+     * @returns instance of PackageManifest
+     */
+    static async create(mdapiDir: string): Promise<PackageManifest> {
+        const packageManifest = new PackageManifest();
+ 
+        const packageXml: string = fs.readFileSync(path.join(mdapiDir, 'package.xml'), 'utf8');
+ 
+        packageManifest._manifestXml = packageXml;
+        packageManifest._manifestJson = await xml2json(packageXml);
+ 
+        return packageManifest;
+    }
+ 
+    /**
+     * Factory method
+     * @param components
+     * @param apiVersion
+     * @returns intance of PackageManifest
+     */
+    static createFromScratch(components: { fullName: string; type: string }[], apiVersion: string): PackageManifest {
+        const packageManifest = new PackageManifest();
+ 
+        const packageJson = {
+            $: { xmlns: 'http://soap.sforce.com/2006/04/metadata' },
+            types: [],
+            version: apiVersion,
+        };
+ 
+        components.forEach((component) => {
+            const type = packageJson.types.find((type) => type.name === component.type);
+            if (type) {
+                // Add member to existing type
+                type.members.push(component.fullName);
+            } else {
+                // create new type
+                const newType = {
+                    name: component.type,
+                    members: [component.fullName],
+                };
+                packageJson.types.push(newType);
+            }
+        });
+ 
+        const builder = new xml2js.Builder({
+            xmldec: { version: '1.0', encoding: 'UTF-8' },
+        });
+ 
+        let packageObj = {
+            Package: packageJson,
+        };
+ 
+        packageManifest._manifestXml = builder.buildObject(packageObj);
+        packageManifest._manifestJson = packageObj;
+ 
+        return packageManifest;
+    }
+ 
+    /**
+     * Factory method
+     * @param manifest package JSON
+     * @returns instance of PackageManifest
+     */
+    static async createWithJSONManifest(manifest: any): Promise<PackageManifest> {
+        const packageManifest = new PackageManifest();
+        packageManifest._manifestJson = manifest;
+ 
+        const builder = new xml2js.Builder({
+            xmldec: { version: '1.0', encoding: 'UTF-8' },
+        });
+ 
+        packageManifest._manifestXml = builder.buildObject(manifest);
+ 
+        return packageManifest;
+    }
+ 
+    /**
+     *
+     * @returns true or false, for whether there are profiles
+     */
+    public isProfilesInPackage(): boolean {
+        let isProfilesFound = false;
+ 
+        if (this._manifestJson.Package.types) {
+            if (Array.isArray(this._manifestJson.Package.types)) {
+                for (const type of this._manifestJson.Package.types) {
+                    if (type.name === 'Profile') {
+                        isProfilesFound = true;
+                        break;
+                    }
+                }
+            } else if (this.manifestJson.Package.types.name === 'Profile') {
+                isProfilesFound = true;
+            }
+        }
+ 
+        return isProfilesFound;
+    }
+ 
+    /**
+     *
+     * @returns true or false, for whether there are profiles
+     */
+     public isPermissionSetsInPackage(): boolean {
+        let isPermissionSetFound = false;
+ 
+        if (this._manifestJson.Package.types) {
+            if (Array.isArray(this._manifestJson.Package.types)) {
+                for (const type of this._manifestJson.Package.types) {
+                    if (type.name === 'PermissionSet') {
+                        isPermissionSetFound = true;
+                        break;
+                    }
+                }
+            } else if (this.manifestJson.Package.types.name === 'PermissionSet') {
+                isPermissionSetFound = true;
+            }
+        }
+ 
+        return isPermissionSetFound;
+    }
+ 
+    public isPermissionSetGroupsFoundInPackage(): boolean {
+        let isPermissionSetGroupFound = false;
+        if (Array.isArray(this._manifestJson?.Package?.types)) {
+            for (let type of this._manifestJson.Package.types) {
+                if (type.name === 'PermissionSetGroup') {
+                    isPermissionSetGroupFound = true;
+                    break;
+                }
+            }
+        } else if (this._manifestJson?.Package?.types?.name === 'PermissionSetGroup') {
+            isPermissionSetGroupFound = true;
+        }
+        return isPermissionSetGroupFound;
+    }
+ 
+    /**
+     *
+     * @returns true or false, for whether there are Apex classes and/or triggers
+     */
+    public isApexInPackage(): boolean {
+        let isApexFound = false;
+ 
+        if (this._manifestJson.Package.types) {
+            if (Array.isArray(this._manifestJson.Package.types)) {
+                for (const type of this._manifestJson.Package.types) {
+                    if (type.name === 'ApexClass' || type.name === 'ApexTrigger') {
+                        isApexFound = true;
+                        break;
+                    }
+                }
+            } else if (
+                this._manifestJson.Package.types.name === 'ApexClass' ||
+                this._manifestJson.Package.types.name === 'ApexTrigger'
+            ) {
+                isApexFound = true;
+            }
+        }
+ 
+        return isApexFound;
+    }
+ 
+    /**
+     *
+     * @returns Apex triggers if there are any, otherwise returns undefined
+     */
+    public fetchTriggers(): ApexClasses {
+        let triggers: string[];
+ 
+        let types;
+        if (this._manifestJson.Package.types) {
+            if (this._manifestJson.Package.types instanceof Array) {
+                types = this._manifestJson.Package.types;
+            } else {
+                // Create array with single type
+                types = [this._manifestJson.Package.types];
+            }
+        }
+ 
+        if (types) {
+            for (const type of types) {
+                if (type.name === 'ApexTrigger') {
+                    if (type.members instanceof Array) {
+                        triggers = type.members;
+                    } else {
+                        // Create array with single member
+                        triggers = [type.members];
+                    }
+                    break;
+                }
+            }
+        }
+ 
+        return triggers;
+    }
+ 
+    public isPayloadContainTypesOtherThan(providedType: string): boolean {
+        let anyOtherType = false;
+        if (this._manifestJson.Package.types) {
+            if (Array.isArray(this._manifestJson.Package.types)) {
+                for (const type of this._manifestJson.Package.types) {
+                    if (type.name !== providedType) {
+                        anyOtherType = true;
+                        break;
+                    }
+                }
+            } else if (this._manifestJson.Package.types.name !== providedType) {
+                anyOtherType = true;
+            }
+        }
+        return anyOtherType;
+    }
+ 
+    public isPayLoadContainTypesSupportedByProfiles(): boolean {
+        const profileSupportedMetadataTypes = [
+            'ApexClass',
+            'CustomApplication',
+            'CustomObject',
+            'CustomField',
+            'Layout',
+            'ApexPage',
+            'CustomTab',
+            'RecordType',
+            'SystemPermissions',
+        ];
+ 
+        let containsProfileSupportedType = false;
+        if (this._manifestJson.Package.types) {
+            if (Array.isArray(this._manifestJson.Package.types)) {
+                for (const type of this._manifestJson.Package.types) {
+                    if (profileSupportedMetadataTypes.includes(type.name)) {
+                        containsProfileSupportedType = true;
+                        break;
+                    }
+                }
+            } else if (profileSupportedMetadataTypes.includes(this._manifestJson.Package.types.name)) {
+                containsProfileSupportedType = true;
+            }
+        }
+        return containsProfileSupportedType;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/components/PackageToComponent.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/components/PackageToComponent.ts.html new file mode 100644 index 000000000..a163fc21e --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/components/PackageToComponent.ts.html @@ -0,0 +1,169 @@ + + + + + + Code coverage report for src/core/package/components/PackageToComponent.ts + + + + + + + + + +
+
+

All files / src/core/package/components PackageToComponent.ts

+
+ +
+ 20% + Statements + 2/10 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 22.22% + Lines + 2/9 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +291x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { ComponentSet } from '@salesforce/source-deploy-retrieve';
+import Component from '../../dependency/Component';
+ 
+ 
+export default class PackageToComponent {
+    public constructor(private packageName:string,private packageDirectory:string) {}
+ 
+    public generateComponents() {
+        const components: Component[] = [];
+ 
+        let componentSet = ComponentSet.fromSource(this.packageDirectory);
+ 
+        let componentSetArray = componentSet.getSourceComponents().toArray();
+ 
+        for (const individualComponentFromComponentSet of componentSetArray) {
+            const component: Component = {
+                id: undefined,
+                fullName: individualComponentFromComponentSet.fullName,
+                type: individualComponentFromComponentSet.type.name,
+                files: [individualComponentFromComponentSet.xml],
+                package: this.packageName,
+            };
+            components.push(component);
+        }
+ 
+       return components;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/components/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/components/index.html new file mode 100644 index 000000000..641259122 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/components/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/core/package/components + + + + + + + + + +
+
+

All files src/core/package/components

+
+ +
+ 74.02% + Statements + 57/77 +
+ + +
+ 50% + Branches + 2/4 +
+ + +
+ 82.35% + Functions + 14/17 +
+ + +
+ 74.66% + Lines + 56/75 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
PackageManifest.ts +
+
82.08%55/6750%2/493.33%14/1581.81%54/66
PackageToComponent.ts +
+
20%2/10100%0/00%0/222.22%2/9
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/coverage/PackageTestCoverage.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/coverage/PackageTestCoverage.ts.html new file mode 100644 index 000000000..d9cf86e4c --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/coverage/PackageTestCoverage.ts.html @@ -0,0 +1,904 @@ + + + + + + Code coverage report for src/core/package/coverage/PackageTestCoverage.ts + + + + + + + + + +
+
+

All files / src/core/package/coverage PackageTestCoverage.ts

+
+ +
+ 93.67% + Statements + 74/79 +
+ + +
+ 70% + Branches + 7/10 +
+ + +
+ 100% + Functions + 15/15 +
+ + +
+ 93.58% + Lines + 73/78 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +2741x +1x +1x +  +1x +1x +1x +  +1x +  +7x +  +  +7x +7x +7x +7x +  +7x +  +  +  +7x +7x +  +7x +  +  +  +  +  +7x +7x +  +  +19x +19x +  +  +  +7x +  +7x +  +1x +  +1x +1x +  +  +7x +  +1x +  +1x +1x +  +  +  +1x +  +  +  +  +1x +1x +2x +  +1x +  +  +  +7x +7x +7x +  +  +  +  +  +  +  +  +  +  +  +5x +  +5x +  +5x +  +  +3x +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +  +  +  +  +  +  +  +3x +  +3x +  +  +  +  +  +2x +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +8x +  +8x +8x +  +8x +  +  +  +  +  +  +  +20x +  +  +  +  +  +  +8x +  +  +  +  +  +  +  +  +2x +2x +  +2x +  +  +  +8x +  +  +  +  +  +2x +2x +  +2x +  +  +8x +  +  +  +  +  +  +  +  +  +  +15x +15x +  +  +  +12x +  +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +15x +15x +  +  +  +27x +  +  +3x +  +  +  +  +  +  +  +  +  +  +  +15x +  +  +63x +  +  +  +  +  +  +12x +  +  +  +  +  +  +  +15x +  +  + 
import SFPLogger, { COLOR_WARNING, Logger } from '@flxblio/sfp-logger';
+import IndividualClassCoverage from '../../apex/coverage/IndividualClassCoverage';
+import SfpPackage, { PackageType } from '../SfpPackage';
+import { Connection } from '@salesforce/core';
+import ApexClassFetcher from '../../apex/ApexClassFetcher';
+import ApexCodeCoverageAggregateFetcher from '../../apex/coverage/ApexCodeCoverageAggregateFetcher';
+import ApexTriggerFetcher from '../../apex/ApexTriggerFetcher';
+ 
+export default class PackageTestCoverage {
+    private individualClassCoverage: IndividualClassCoverage;
+    private packageTestCoverage: number = -1; // Set inital value
+ 
+    public constructor(
+        private pkg: SfpPackage,
+        private codeCoverage: any,
+        private logger: Logger,
+        private readonly conn: Connection
+    ) {
+        this.individualClassCoverage = new IndividualClassCoverage(this.codeCoverage, this.logger);
+    }
+ 
+    public async getCurrentPackageTestCoverage(): Promise<number> {
+        let packageClasses: string[] = this.pkg.apexClassWithOutTestClasses;
+        let triggers: string[] = this.pkg.triggers;
+ 
+        let filteredCodeCoverage = this.filterCodeCoverageToPackageClassesAndTriggers(
+            this.codeCoverage,
+            packageClasses,
+            triggers
+        );
+ 
+        let totalLines: number = 0;
+        let totalCovered: number = 0;
+        for (let classCoverage of filteredCodeCoverage) {
+            if (classCoverage.coveredPercent !== null) {
+                totalLines += classCoverage.totalLines;
+                totalCovered += classCoverage.totalCovered;
+            }
+        }
+ 
+        let listOfApexClassOrTriggerId: string[] = [];
+ 
+        let classesNotTouchedByTestClass = this.getClassesNotTouchedByTestClass(packageClasses, this.codeCoverage);
+        if (classesNotTouchedByTestClass.length > 0) {
+            let apexClassIds = (
+                await new ApexClassFetcher(this.conn).fetchApexClassByName(classesNotTouchedByTestClass)
+            ).map((apexClass) => apexClass.Id);
+            listOfApexClassOrTriggerId = listOfApexClassOrTriggerId.concat(apexClassIds);
+        }
+ 
+        let triggersNotTouchedByTestClass = this.getTriggersNotTouchedByTestClass(triggers, this.codeCoverage);
+        if (triggersNotTouchedByTestClass.length > 0) {
+            let triggerIds = (
+                await new ApexTriggerFetcher(this.conn).fetchApexTriggerByName(triggersNotTouchedByTestClass)
+            ).map((trigger) => trigger.Id);
+            listOfApexClassOrTriggerId = listOfApexClassOrTriggerId.concat(triggerIds);
+        }
+ 
+        if (listOfApexClassOrTriggerId.length > 0) {
+            let recordsOfApexCodeCoverageAggregate = await new ApexCodeCoverageAggregateFetcher(
+                this.conn
+            ).fetchACCAById(listOfApexClassOrTriggerId);
+ 
+            if (recordsOfApexCodeCoverageAggregate.length > 0) {
+                let numLinesUncovered: number = 0; // aggregate number of unconvered lines for classes & triggers that are not touched by any test classes
+                recordsOfApexCodeCoverageAggregate.forEach((record) => {
+                    numLinesUncovered += record.NumLinesUncovered;
+                });
+                totalLines += numLinesUncovered;
+            }
+        }
+ 
+        let testCoverage = Math.floor((totalCovered / totalLines) * 100);
+        this.packageTestCoverage = testCoverage;
+        return testCoverage;
+    }
+ 
+    public async validateTestCoverage(
+        coverageThreshold?: number
+    ): Promise<{
+        result: boolean;
+        message?: string;
+        packageTestCoverage: number;
+        classesCovered?: { name: string; coveredPercent: number }[];
+        classesWithInvalidCoverage?: { name: string; coveredPercent: number }[];
+    }> {
+        if (this.packageTestCoverage == -1)
+            //No Value available
+            await this.getCurrentPackageTestCoverage();
+ 
+        let classesCovered = this.getIndividualClassCoverageByPackage(this.codeCoverage);
+ 
+        if (coverageThreshold == undefined || coverageThreshold < 75) {
+            SFPLogger.log('Setting minimum coverage percentage to 75%.');
+            coverageThreshold = 75;
+        }
+ 
+        
+      
+        if (this.pkg.packageType === PackageType.Unlocked) {
+            if (this.packageTestCoverage < coverageThreshold) {
+                // Coverage inadequate, set result to false
+                return {
+                    result: false, // Had earlier Changed to warning in Apr-22, due to unstable coverage, now reverting
+                    packageTestCoverage: this.packageTestCoverage,
+                    classesCovered: classesCovered,
+                    message: `${COLOR_WARNING(
+                        `The package has an overall coverage of ${this.packageTestCoverage}%, which does not meet the required overall coverage of ${coverageThreshold}%`
+                    )}`,
+                };
+            } else {
+                return {
+                    result: true,
+                    packageTestCoverage: this.packageTestCoverage,
+                    classesCovered: classesCovered,
+                    message: `Package overall coverage is greater than ${coverageThreshold}%`,
+                };
+            }
+        } else if (this.pkg.packageType === PackageType.Source || this.pkg.packageType === PackageType.Diff) {
+            SFPLogger.log("Package type is Source. Validating individual class coverage");
+ 
+            let individualClassValidationResults = this.individualClassCoverage.validateIndividualClassCoverage(
+                this.getIndividualClassCoverageByPackage(this.codeCoverage),
+                coverageThreshold
+            );
+ 
+            if (individualClassValidationResults.result) {
+                return {
+                    result: true,
+                    packageTestCoverage: this.packageTestCoverage,
+                    classesCovered: classesCovered,
+                    classesWithInvalidCoverage: individualClassValidationResults.classesWithInvalidCoverage,
+                    message: `Individidual coverage of classes is greater than ${coverageThreshold}%`,
+                };
+            } else {
+                return {
+                    result: false,
+                    packageTestCoverage: this.packageTestCoverage,
+                    classesCovered: classesCovered,
+                    classesWithInvalidCoverage: individualClassValidationResults.classesWithInvalidCoverage,
+                    message: `There are classes that do not satisfy the minimum code coverage of ${coverageThreshold}%`,
+                };
+            }
+        } else {
+            throw new Error('Unhandled package type');
+        }
+    }
+ 
+    private getIndividualClassCoverageByPackage(codeCoverageReport: any): { name: string; coveredPercent: number }[] {
+        let individualClassCoverage: {
+            name: string;
+            coveredPercent: number;
+        }[] = [];
+ 
+        let packageClasses: string[] = this.pkg.apexClassWithOutTestClasses;
+        let triggers: string[] = this.pkg.triggers;
+ 
+        codeCoverageReport = this.filterCodeCoverageToPackageClassesAndTriggers(
+            codeCoverageReport,
+            packageClasses,
+            triggers
+        );
+ 
+        for (let classCoverage of codeCoverageReport) {
+            if (classCoverage['coveredPercent'] !== null) {
+                individualClassCoverage.push({
+                    name: classCoverage['name'],
+                    coveredPercent: classCoverage['coveredPercent'],
+                });
+            }
+        }
+ 
+        let namesOfClassesWithoutTest: string[] = this.getClassesNotTouchedByTestClass(
+            packageClasses,
+            codeCoverageReport
+        );
+ 
+        if (namesOfClassesWithoutTest.length > 0) {
+            let classesWithoutTest: {
+                name: string;
+                coveredPercent: number;
+            }[] = namesOfClassesWithoutTest.map((className) => {
+                return { name: className, coveredPercent: 0 };
+            });
+            individualClassCoverage = individualClassCoverage.concat(classesWithoutTest);
+        }
+ 
+        // Check for triggers with no test class
+        let namesOfTriggersWithoutTest: string[] = this.getTriggersNotTouchedByTestClass(triggers, codeCoverageReport);
+ 
+        if (namesOfTriggersWithoutTest.length > 0) {
+            let triggersWithoutTest: {
+                name: string;
+                coveredPercent: number;
+            }[] = namesOfTriggersWithoutTest.map((triggerName) => {
+                return { name: triggerName, coveredPercent: 0 };
+            });
+            individualClassCoverage = individualClassCoverage.concat(triggersWithoutTest);
+        }
+ 
+        return individualClassCoverage;
+    }
+ 
+    /**
+     * Returns names of triggers in the package that are not triggered by the execution of any test classes
+     * Returns empty array if triggers is null or undefined
+     * @param triggers
+     * @param codeCoverageReport
+     * @returns
+     */
+    private getTriggersNotTouchedByTestClass(triggers: string[], codeCoverageReport: any): string[] {
+        if (triggers != null) {
+            return triggers.filter((trigger) => {
+                for (let classCoverage of codeCoverageReport) {
+                    if (classCoverage['name'] === trigger) {
+                        // Filter out triggers if accounted for in coverage json
+                        return false;
+                    }
+                }
+                return true;
+            });
+        } else Ereturn [];
+    }
+ 
+    /**
+     * Returns name of classes in the package that are not touched by the execution of any test classes
+     * Returns empty array if packageClasses is null or undefined
+     * @param packageClasses
+     * @param codeCoverageReport
+     * @returns
+     */
+    private getClassesNotTouchedByTestClass(packageClasses: string[], codeCoverageReport: any): string[] {
+        if (packageClasses != null) {
+            return packageClasses.filter((packageClass) => {
+                for (let classCoverage of codeCoverageReport) {
+                    if (classCoverage['name'] === packageClass) {
+                        // Filter out package class if accounted for in coverage json
+                        return false;
+                    }
+                }
+                return true;
+            });
+        } else Ereturn [];
+    }
+ 
+    /**
+     * Filter code coverage to classes and triggers in the package
+     * @param codeCoverage
+     * @param packageClasses
+     * @param triggers
+     */
+    private filterCodeCoverageToPackageClassesAndTriggers(codeCoverage, packageClasses: string[], triggers: string[]) {
+        let filteredCodeCoverage = codeCoverage.filter((classCoverage) => {
+            if (packageClasses != null) {
+                for (let packageClass of packageClasses) {
+                    if (packageClass === classCoverage['name']) return true;
+                }
+            }
+ 
+            if (triggers != null) {
+                for (let trigger of triggers) {
+                    if (trigger === classCoverage['name']) {
+                        return true;
+                    }
+                }
+            }
+ 
+            return false;
+        });
+ 
+        return filteredCodeCoverage;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/coverage/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/coverage/index.html new file mode 100644 index 000000000..3e23e8d58 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/coverage/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/core/package/coverage + + + + + + + + + +
+
+

All files src/core/package/coverage

+
+ +
+ 93.67% + Statements + 74/79 +
+ + +
+ 70% + Branches + 7/10 +
+ + +
+ 100% + Functions + 15/15 +
+ + +
+ 93.58% + Lines + 73/78 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
PackageTestCoverage.ts +
+
93.67%74/7970%7/10100%15/1593.58%73/78
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/ExternalPackage2DependencyResolver.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/ExternalPackage2DependencyResolver.ts.html new file mode 100644 index 000000000..bb3b45099 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/ExternalPackage2DependencyResolver.ts.html @@ -0,0 +1,394 @@ + + + + + + Code coverage report for src/core/package/dependencies/ExternalPackage2DependencyResolver.ts + + + + + + + + + +
+
+

All files / src/core/package/dependencies ExternalPackage2DependencyResolver.ts

+
+ +
+ 54.28% + Statements + 19/35 +
+ + +
+ 62.5% + Branches + 5/8 +
+ + +
+ 75% + Functions + 3/4 +
+ + +
+ 50% + Lines + 16/32 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104  +2x +  +2x +  +  +  +  +  +2x +  +2x +  +  +  +  +  +  +2x +  +  +2x +  +  +  +  +  +  +  +2x +2x +  +  +  +  +  +  +  +  +  +12x +  +  +  +10x +4x +  +  +  +  +  +  +  +  +2x +2x +  +  +  +  +  +  +  +  +  +  +2x +  +  +  +  +  +2x +  +  +  +  +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Connection } from '@salesforce/core';
+import PackageDependencyResolver from './PackageDependencyResolver';
+import _ from 'lodash';
+import Package2VersionFetcher from '../version/Package2VersionFetcher';
+import Package2Detail from '../Package2Detail';
+ 
+/**
+ * Resolves external package dependency versions to their subscriber version
+ */
+export default class ExternalPackage2DependencyResolver {
+    //TOOD: Finalize Keys
+    constructor(private conn: Connection, private projectConfig, private keys) {}
+ 
+    public async resolveExternalPackage2DependenciesToVersions(
+        packagesToBeResolved?: string[],
+        packagesToBeSkipped?: string[],
+        isDependencyValidated?: boolean
+    ): Promise<Package2Detail[]> {
+        if (isDependencyValidated == undefined) isDependencyValidated = true;
+        //Do a dependency resolution first only for external dependencies
+        //Resolve .LATEST to exact version numbers
+        let revisedProjectConfig = await new PackageDependencyResolver(
+            this.conn,
+            this.projectConfig,
+            packagesToBeSkipped,
+            null,
+            isDependencyValidated
+        ).resolvePackageDependencyVersions();
+ 
+        let packageVersions: Package2Detail[] = [];
+        let packageVersionFetcher = new Package2VersionFetcher(this.conn);
+ 
+        let packagesToKeys: { [p: string]: string };
+        if (this.keys) {
+            packagesToKeys = this.parseKeys(this.keys);
+        }
+ 
+        //Resolve provided version Number to SubscriberVersionId
+        for (const sfdxPackage of revisedProjectConfig.packageDirectories) {
+           
+            Iif(packagesToBeResolved && !packagesToBeResolved.includes(sfdxPackage.package))
+              continue;
+ 
+            if (sfdxPackage.dependencies && Array.isArray(sfdxPackage.dependencies)) {
+                for (let i = 0; i < sfdxPackage.dependencies.length; i++) {
+                    let dependency = sfdxPackage.dependencies[i];
+ 
+                    if (packagesToBeSkipped && packagesToBeSkipped.includes(dependency.package)) 
+                    {
+                        let dependendentPackage: Package2Detail = { name: dependency.package };
+                        packageVersions.push(dependendentPackage);
+                        continue;
+                    }
+ 
+                    if (!packageVersions.find((elem) => elem.name == dependency.package)) {
+                        let dependendentPackage: Package2Detail = { name: dependency.package };
+                        if (dependency.versionNumber) {
+                            dependendentPackage.versionNumber = dependency.versionNumber;
+                            let packageVersion = await packageVersionFetcher.fetchByPackage2Id(
+                                revisedProjectConfig.packageAliases[dependendentPackage.name],
+                                dependendentPackage.versionNumber,
+                                true
+                            );
+                            dependendentPackage.subscriberPackageVersionId =
+                                packageVersion[0].SubscriberPackageVersionId;
+                        } else {
+                            dependendentPackage.subscriberPackageVersionId =
+                                revisedProjectConfig.packageAliases[dependendentPackage.name];
+                        }
+                        if (packagesToKeys?.[dependendentPackage.name]) {
+                            dependendentPackage.key = packagesToKeys[dependency.package];
+                        }
+                        packageVersions.push(dependendentPackage);
+                    }
+                }
+            }
+        }
+        return packageVersions;
+    }
+ 
+    /**
+     * Parse keys in string format "packageA:key packageB:key packageC:key"
+     * Returns map of packages to keys
+     * @param keys
+     */
+    private parseKeys(keys: string) {
+        let output: { [p: string]: string } = {};
+ 
+        keys = keys.trim();
+        let listOfKeys = keys.split(' ');
+ 
+        for (let key of listOfKeys) {
+            let packageKeyPair = key.split(':');
+            if (packageKeyPair.length === 2) {
+                output[packageKeyPair[0]] = packageKeyPair[1];
+            } else {
+                // Format is incorrect, throw an error
+                throw new Error(`Error parsing keys, format should be: "packageA:key packageB:key packageC:key"`);
+            }
+        }
+        return output;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/PackageDependencyResolver.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/PackageDependencyResolver.ts.html new file mode 100644 index 000000000..06be92654 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/PackageDependencyResolver.ts.html @@ -0,0 +1,892 @@ + + + + + + Code coverage report for src/core/package/dependencies/PackageDependencyResolver.ts + + + + + + + + + +
+
+

All files / src/core/package/dependencies PackageDependencyResolver.ts

+
+ +
+ 97.4% + Statements + 75/77 +
+ + +
+ 88.88% + Branches + 24/27 +
+ + +
+ 100% + Functions + 11/11 +
+ + +
+ 97.29% + Lines + 72/74 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270  +3x +3x +3x +3x +3x +  +  +  +  +  +3x +10x +  +  +10x +10x +10x +10x +10x +  +  +10x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +24x +  +  +  +  +  +  +19x +28x +  +  +1x +  +  +27x +  +  +  +9x +  +  +  +  +2x +  +16x +  +1x +1x +  +  +  +  +  +1x +  +  +  +  +  +1x +  +  +  +  +  +1x +1x +1x +1x +1x +  +  +15x +  +  +  +  +  +  +  +13x +10x +10x +  +3x +  +  +  +7x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +38x +16x +  +  +  +  +6x +6x +  +6x +  +  +  +  +1x +  +  +  +  +5x +  +  +1x +  +  +  +  +  +4x +  +  +  +  +  +  +5x +  +  +  +  +5x +  +  +  +  +  +  +1x +  +  +  +  +13x +4x +  +  +1x +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +4x +4x +4x +  +  +9x +  +  +3x +3x +  +  +9x +  +  +  +1x +  +  +  +  +3x +  +  +  +55x +55x +  +  +  +  +10x +  +  +  +  +  +  +  +  +6x +6x +5x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +5x +5x +5x +  +  +  +  +  +  +  +  +  +6x +6x +  +  + 
import { Connection } from '@salesforce/core';
+import lodash = require('lodash');
+import Git from '../../git/Git';
+import GitTags from '../../git/GitTags';
+import Package2VersionFetcher, { Package2Version } from '../version/Package2VersionFetcher';
+import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger';
+ 
+ 
+/**
+ * Resolves package dependency versions to their exact versions
+ */
+export default class PackageDependencyResolver {
+    private package2VersionCache: Package2VersionCache = new Package2VersionCache();
+ 
+    constructor(
+        private conn: Connection,
+        private projectConfig,
+        private packagesToBeSkipped?: string[],
+        private packagesToBeResolved?: string[],
+        private resolveExternalDepenciesOnly?: boolean
+    ) {
+        // prevent mutation of original config
+        this.projectConfig = lodash.cloneDeep(this.projectConfig);
+    }
+ 
+    /**
+     * Resolves package dependency versions in project config
+     * Skips dependencies on packages that are queued for build, as they are resolved dynamically(packagesToBeSkipped)
+     * @returns new project config JSON, does not change original JSON
+     */
+    public async resolvePackageDependencyVersions() {
+        for (const packageDirectory of this.projectConfig.packageDirectories) {
+            if (this.packagesToBeResolved?.length > 0 && this.packagesToBeSkipped?.length > 0) {
+                throw Error(`Unsupported path.. Use only one of the options at any given time`);
+            }
+ 
+            if (this.packagesToBeSkipped && !this.packagesToBeSkipped.includes(packageDirectory.package)) {
+                continue;
+            }
+ 
+            if (this.packagesToBeResolved && !this.packagesToBeResolved.includes(packageDirectory.package)) {
+                continue;
+            }
+            if (packageDirectory.dependencies && Array.isArray(packageDirectory.dependencies)) {
+                for (let i = 0; i < packageDirectory.dependencies.length; i++) {
+                    let dependency = packageDirectory.dependencies[i];
+                    if (this.projectConfig.packageAliases[dependency.package] === undefined && !this.isSubscriberPackageVersionId(dependency.package)) {
+                        
+                        throw new Error(`Can't find package id for dependency: ` + dependency.package);
+                    }
+ 
+                    let packageVersionId = this.isSubscriberPackageVersionId(dependency.package)?dependency.package:this.projectConfig.packageAliases[dependency.package]
+ 
+                    if (this.isSubscriberPackageVersionId(packageVersionId)) {
+                        // Already resolved
+                        continue;
+                    }
+ 
+                    if (this.packagesToBeSkipped && this.packagesToBeSkipped.includes(dependency.package) && !dependency.branch) {
+                        // Dependency is part of the same build, will be resolved when new version is created
+                        continue;
+                    }
+                    let package2VersionForDependency: any = '';
+                    if ( dependency.branch && dependency.branch !== '' ) {
+                        SFPLogger.log(`Specified branch: ${dependency.branch} for dependency: ${dependency.package}`, LoggerLevel.INFO);
+                        package2VersionForDependency = await this.getPackage2VersionForDependency(
+                            this.conn,
+                            dependency,
+                            packageVersionId,
+                            dependency.branch
+                        );
+                        SFPLogger.log(`Fetched latest branched package of ${dependency.package},`
+                                        +`version: ${package2VersionForDependency.MajorVersion}.`
+                                                 +`${package2VersionForDependency.MinorVersion}.`
+                                                 +`${package2VersionForDependency.PatchVersion}.`
+                                                 +`${package2VersionForDependency.BuildNumber}`, LoggerLevel.INFO);
+ 
+                        let branchedPackageAlias = `${dependency.package}@`
+                                                    +`${package2VersionForDependency.MajorVersion}.`
+                                                    +`${package2VersionForDependency.MinorVersion}.`
+                                                    +`${package2VersionForDependency.PatchVersion}.`
+                                                    +`${package2VersionForDependency.BuildNumber}-`
+                                                    +`${dependency.branch}`;
+                        dependency.package = branchedPackageAlias;
+                        this.projectConfig.packageAliases[branchedPackageAlias] = package2VersionForDependency.SubscriberPackageVersionId;
+                        delete dependency.versionNumber;
+                        delete dependency.branch;
+                        continue;
+                        
+                    }else {
+                        package2VersionForDependency = await this.getPackage2VersionForDependency(
+                            this.conn,
+                            dependency,
+                            packageVersionId
+                        );
+                    }
+                    
+ 
+                    if (package2VersionForDependency == null) {
+                        packageDirectory.dependencies.splice(i, 1);
+                        i--;
+                    } else
+                        dependency.versionNumber = `${package2VersionForDependency.MajorVersion}.${package2VersionForDependency.MinorVersion}.${package2VersionForDependency.PatchVersion}.${package2VersionForDependency.BuildNumber}`;
+                }
+            }
+        }
+        return this.projectConfig;
+    }
+ 
+    /**
+     * Get last validated Package2 version for package dependency
+     * @param conn
+     * @param dependency
+     * @returns Package2Version
+     */
+    private async getPackage2VersionForDependency(
+        conn: Connection,
+        dependency: { package: string; versionNumber: string },
+        packageVersionId: string,
+        branch?: string,
+    ): Promise<Package2Version> {
+ 
+        //Dont hit api's if its only for external dependencies
+        if (this.projectConfig.packageDirectories.find((dir) => dir.package === dependency.package)) {
+            if (this.resolveExternalDepenciesOnly) return null;
+        }
+ 
+        let package2Version: Package2Version;
+ 
+        let versionNumber: string = dependency.versionNumber;
+        let vers: string[] = versionNumber.split('.');
+        if (vers.length === 4 && vers[3] === 'LATEST') {
+            versionNumber = `${vers[0]}.${vers[1]}.${vers[2]}`;
+        }
+ 
+        let package2Versions: Package2Version[];
+        if (this.package2VersionCache.has(packageVersionId, versionNumber)) {
+            package2Versions = this.package2VersionCache.get(
+                packageVersionId,
+                versionNumber
+            );
+        } else {
+            const package2VersionFetcher = new Package2VersionFetcher(conn);
+            let records;
+            if( branch ){
+                records = await package2VersionFetcher.fetchByPackageBranchAndName(
+                    branch,
+                    dependency.package,
+                    versionNumber
+                );
+            }else{
+                records = await package2VersionFetcher.fetchByPackage2Id(
+                    packageVersionId,
+                    versionNumber,
+                    true
+                );
+            }
+            
+            this.package2VersionCache.set(
+                packageVersionId,
+                versionNumber,
+                records
+            );
+            package2Versions = this.package2VersionCache.get(
+                packageVersionId,
+                versionNumber
+            );
+        }
+ 
+        if (package2Versions.length === 0) {
+            throw new Error(
+                `Failed to find any validated Package2 versions for the dependency ${dependency.package} with version ${dependency.versionNumber}`
+            );
+        }
+ 
+        if (this.projectConfig.packageDirectories.find((dir) => dir.package === dependency.package && !branch)) {
+            package2Version = await this.getPackage2VersionFromCurrentBranch(package2Versions, dependency);
+        } else {
+            // Take last validated package for external packages
+            package2Version = package2Versions[0];
+        }
+        return package2Version;
+    }
+ 
+    /**
+     * Get Package2 version created from the current branch
+     * @param package2Versions
+     * @param dependency
+     * @returns Package2Version
+     */
+    private async getPackage2VersionFromCurrentBranch(
+        package2Versions: Package2Version[],
+        dependency: { package: string; versionNumber: string }
+    ) {
+        let package2VersionOnCurrentBranch: Package2Version;
+ 
+        const git = await Git.initiateRepo();
+        const gitTags = new GitTags(git, dependency.package);
+        const tags = await gitTags.listTagsOnBranch();
+ 
+        for (const package2Version of package2Versions) {
+            const version = `${package2Version.MajorVersion}.${package2Version.MinorVersion}.${package2Version.PatchVersion}.${package2Version.BuildNumber}`;
+            for (const tag of tags) {
+                if (tag.endsWith(version)) {
+                    package2VersionOnCurrentBranch = package2Version;
+                    break;
+                }
+            }
+            if (package2VersionOnCurrentBranch) break;
+        }
+ 
+        if (!package2VersionOnCurrentBranch) {
+            throw new Error(
+                `Failed to find validated Package2 version for dependency ${dependency.package} with version ${dependency.versionNumber} created from the current branch`
+            );
+        }
+ 
+        return package2VersionOnCurrentBranch;
+    }
+ 
+    private isSubscriberPackageVersionId(packageAlias: string): boolean {
+        const subscriberPackageVersionIdPrefix = '04t';
+        return packageAlias.startsWith(subscriberPackageVersionIdPrefix);
+    }
+}
+ 
+class Package2VersionCache {
+    private cache: { [p: string]: Package2Version[] } = {};
+ 
+    /**
+     * Checks whether cache contains key for package ID and version number
+     * @param packageId
+     * @param versionNumberstartw
+     * @returns true or false
+     */
+    has(packageId: string, versionNumber: string): boolean {
+        const key = `${packageId}-${versionNumber}`;
+        if (this.cache[key]) return true;
+        else return false;
+    }
+ 
+    /**
+     * Set the cache value, Package2 versions, for package ID and version number
+     * @param packageId
+     * @param versionNumber
+     * @param package2Versions
+     * @returns cache
+     */
+    set(
+        packageId: string,
+        versionNumber: string,
+        package2Versions: Package2Version[]
+    ): { [p: string]: Package2Version[] } {
+        const key = `${packageId}-${versionNumber}`;
+        this.cache[key] = package2Versions;
+        return this.cache;
+    }
+ 
+    /**
+     *
+     * @param packageId
+     * @param versionNumber
+     * @returns Package2 versions for package ID and version number
+     */
+    get(packageId: string, versionNumber: string): Package2Version[] {
+        const key = `${packageId}-${versionNumber}`;
+        return this.cache[key];
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/TransitiveDependencyResolver.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/TransitiveDependencyResolver.ts.html new file mode 100644 index 000000000..7da072f8e --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/TransitiveDependencyResolver.ts.html @@ -0,0 +1,412 @@ + + + + + + Code coverage report for src/core/package/dependencies/TransitiveDependencyResolver.ts + + + + + + + + + +
+
+

All files / src/core/package/dependencies TransitiveDependencyResolver.ts

+
+ +
+ 88.63% + Statements + 39/44 +
+ + +
+ 0% + Branches + 0/4 +
+ + +
+ 88.88% + Functions + 8/9 +
+ + +
+ 88.37% + Lines + 38/43 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +1102x +2x +2x +2x +2x +2x +  +2x +  +2x +7x +  +  +7x +  +7x +7x +7x +7x +  +  +  +7x +  +7x +  +  +  +  +  +  +  +  +7x +  +  +7x +  +  +  +  +  +7x +  +42x +  +  +  +  +42x +  +  +  +65x +65x +  +  +  +  +  +  +  +  +87x +  +  +42x +221x +152x +42x +  +96x +  +96x +  +7x +  +  +  +  +  +7x +7x +  +  +  +  +  +145x +727x +727x +  +42x +  +7x +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import ProjectConfig from '../../project/ProjectConfig';
+import { COLOR_HEADER, COLOR_KEY_MESSAGE, COLOR_SUCCESS, COLOR_ERROR } from '@flxblio/sfp-logger';
+import SFPLogger, { LoggerLevel, Logger } from '@flxblio/sfp-logger';
+import _, { uniq } from 'lodash';
+import semver = require('semver');
+import convertBuildNumDotDelimToHyphen from '../../utils/VersionNumberConverter';
+import { Connection } from '@salesforce/core';
+import UserDefinedExternalDependencyMap from '../../project/UserDefinedExternalDependency';
+ 
+export default class TransitiveDependencyResolver {
+    constructor(private sfdxProjectConfig: any, private logger?: Logger) {}
+ 
+    public async resolveTransitiveDependencies(): Promise<Map<string, { package: string; versionNumber?: string }[]>> {
+        SFPLogger.log('Validating Project Dependencies...', LoggerLevel.INFO, this.logger);
+ 
+        let clonedProjectConfig = await _.cloneDeep(this.sfdxProjectConfig);
+        clonedProjectConfig = await new UserDefinedExternalDependencyMap().cleanupEntries(clonedProjectConfig);
+        let pkgWithDependencies = ProjectConfig.getAllPackagesAndItsDependencies(clonedProjectConfig);
+        pkgWithDependencies = this.fillDepsWithUserDefinedExternalDependencyMap(
+            pkgWithDependencies,
+            new UserDefinedExternalDependencyMap().fetchDependencyEntries(clonedProjectConfig)
+        );
+        pkgWithDependencies = this.fillDepsTransitively(pkgWithDependencies);
+ 
+        return pkgWithDependencies;
+    }
+ 
+    private fillDepsWithUserDefinedExternalDependencyMap(
+        pkgWithDependencies: Map<string, { package: string; versionNumber?: string }[]>,
+        externalDependencyMap: any
+    ): Map<string, { package: string; versionNumber?: string }[]> {
+        if (externalDependencyMap) {
+            for (let pkg of Object.keys(externalDependencyMap)) {
+                pkgWithDependencies.set(pkg, externalDependencyMap[pkg]);
+            }
+        }
+        return pkgWithDependencies;
+    }
+ 
+    private fillDepsTransitively(
+        dependencyMap: Map<string, { package: string; versionNumber?: string }[]>
+    ): Map<string, { package: string; versionNumber?: string }[]> {
+        let pkgs = Array.from(dependencyMap.keys());
+        for (let pkg of pkgs) {
+            SFPLogger.log(
+                COLOR_HEADER(`fetching dependencies for package:`) + COLOR_KEY_MESSAGE(pkg),
+                LoggerLevel.TRACE,
+                this.logger
+            );
+            let dependenencies: { package: string; versionNumber?: string }[] = [];
+            for (let dependency of dependencyMap.get(pkg)) {
+                if (dependencyMap.get(dependency.package)) {
+                    //push parents first
+                    dependenencies = dependenencies.concat(dependencyMap.get(dependency.package));
+                    SFPLogger.log(
+                        `pushing ${dependencyMap.get(dependency.package).length} dependencies from package ${
+                            dependency.package
+                        }`,
+                        LoggerLevel.TRACE,
+                        this.logger
+                    );
+                }
+                //push itself
+                dependenencies.push(dependency);
+            }
+            //deduplicate dependency list
+            let uniqueDependencies = [
+                ...new Set(dependenencies.map((objects) => JSON.stringify(objects))),
+            ].map((tmpString) => JSON.parse(tmpString));
+            for (let j = 0; j < uniqueDependencies.length; j++) {
+                if (uniqueDependencies[j].versionNumber) {
+                    let version = convertBuildNumDotDelimToHyphen(uniqueDependencies[j].versionNumber);
+ 
+                    for (let i = j + 1; i < uniqueDependencies.length; i++) {
+                        if (uniqueDependencies[j].package == uniqueDependencies[i].package) {
+                            let versionToCompare = convertBuildNumDotDelimToHyphen(uniqueDependencies[i].versionNumber);
+                            // replace existing packageInfo if package version number is newer
+                            if (semver.lt(version, versionToCompare)) {
+                               uniqueDependencies = this.swapAndDropArrayElement(uniqueDependencies,j,i);
+                                
+                            } else {
+                                uniqueDependencies.splice(i, 1);
+                                i--;
+                            }
+                        }
+                    }
+                }
+                //do a dedup again
+                uniqueDependencies = [
+                    ...new Set(uniqueDependencies.map((objects) => JSON.stringify(objects))),
+                ].map((tmpString) => JSON.parse(tmpString));
+            }
+            dependencyMap.set(pkg, uniqueDependencies);
+        }
+        return dependencyMap;
+    }
+ 
+    private swapAndDropArrayElement<T>(arr: T[], i: number, j: number): T[] {
+        if (i < 0 || i >= arr.length || j < 0 || j >= arr.length) {
+          return arr;
+        }
+        
+        let newArr = [...arr];
+        [newArr[i], newArr[j]] = [newArr[j], newArr[i]];
+        return [...newArr.slice(0, j), ...newArr.slice(j + 1)];
+      }
+      
+      
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/index.html new file mode 100644 index 000000000..4a2f2fffa --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/dependencies/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for src/core/package/dependencies + + + + + + + + + +
+
+

All files src/core/package/dependencies

+
+ +
+ 85.25% + Statements + 133/156 +
+ + +
+ 74.35% + Branches + 29/39 +
+ + +
+ 91.66% + Functions + 22/24 +
+ + +
+ 84.56% + Lines + 126/149 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ExternalPackage2DependencyResolver.ts +
+
54.28%19/3562.5%5/875%3/450%16/32
PackageDependencyResolver.ts +
+
97.4%75/7788.88%24/27100%11/1197.29%72/74
TransitiveDependencyResolver.ts +
+
88.63%39/440%0/488.88%8/988.37%38/43
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/deploymentFilters/EntitlementVersionFilter.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/deploymentFilters/EntitlementVersionFilter.ts.html new file mode 100644 index 000000000..908d418b2 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/deploymentFilters/EntitlementVersionFilter.ts.html @@ -0,0 +1,415 @@ + + + + + + Code coverage report for src/core/package/deploymentFilters/EntitlementVersionFilter.ts + + + + + + + + + +
+
+

All files / src/core/package/deploymentFilters EntitlementVersionFilter.ts

+
+ +
+ 84.09% + Statements + 37/44 +
+ + +
+ 42.85% + Branches + 3/7 +
+ + +
+ 66.66% + Functions + 2/3 +
+ + +
+ 90.24% + Lines + 37/41 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +1111x +  +1x +1x +  +1x +1x +1x +1x +  +1x +1x +  +1x +  +  +  +5x +5x +  +  +5x +5x +  +  +5x +  +  +5x +  +  +  +3x +3x +  +1x +1x +  +  +4x +  +4x +4x +  +  +  +  +8x +  +8x +7x +  +  +  +  +  +  +  +  +  +1x +1x +  +  +  +  +1x +1x +1x +  +3x +  +  +  +  +  +  +4x +  +  +  +  +  +  +4x +4x +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { ComponentSet, registry } from '@salesforce/source-deploy-retrieve';
+import SFPOrg from '../../org/SFPOrg';
+import QueryHelper from '../../queryHelper/QueryHelper';
+import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import { DeploymentFilter } from './DeploymentFilter';
+import * as fs from 'fs-extra';
+import SettingsFetcher from '../../metadata/SettingsFetcher';
+import { PackageType } from '../SfpPackage';
+const { XMLBuilder } = require('fast-xml-parser');
+ 
+const EXISTING_SLAPPROCESS_QUERY = `SELECT Name, NameNorm,VersionNumber, VersionMaster FROM SlaProcess ORDER BY VersionNumber DESC`;
+const EXISTING_SLAPPROCESS_QUERY_NO_VERSIONING = `SELECT Name, NameNorm FROM SlaProcess`;
+ 
+export default class EntitlementVersionFilter implements DeploymentFilter {
+  
+    public async apply(org: SFPOrg, componentSet: ComponentSet, logger: Logger): Promise<ComponentSet> {
+        //Only do if entitlment exits in the package
+        let sourceComponents = componentSet.getSourceComponents().toArray();
+        let isEntitlementFound: boolean = false;
+        for (const sourceComponent of sourceComponents) {
+            if (sourceComponent.type.name === registry.types.entitlementprocess.name) {
+                isEntitlementFound = true;
+                break;
+            }
+        }
+        Iif (!isEntitlementFound) return componentSet;
+ 
+        try {
+            let entitlementSettings = await new SettingsFetcher(logger).getSetttingMetadata(org, `Entitlement`);
+ 
+            let query;
+            if (entitlementSettings.enableEntitlementVersioning == true) {
+                SFPLogger.log(`Entitlement Versioning enabled in the org....`, LoggerLevel.INFO, logger);
+                query = EXISTING_SLAPPROCESS_QUERY;
+            } else {
+                SFPLogger.log(`Entitlement Versioning not enabled in the org....`, LoggerLevel.INFO, logger);
+                query = EXISTING_SLAPPROCESS_QUERY_NO_VERSIONING;
+            }
+ 
+            SFPLogger.log(`Filtering Entitlement Process....`, LoggerLevel.INFO, logger);
+            //Fetch Entitlements currently in the org
+            let slaProcessesInOrg = await QueryHelper.query<SlaProcess>(query, org.getConnection(), false);
+            let modifiedComponentSet = new ComponentSet();
+            //Compare version numbers in the org vs version in the component set
+            //Remove if the version numbers match
+            for (const sourceComponent of sourceComponents) {
+                if (sourceComponent.type.name === registry.types.entitlementprocess.name) {
+                    let slaProcessLocal = sourceComponent.parseXmlSync();
+ 
+                    let slaProcessMatchedByName: SlaProcess = slaProcessesInOrg.find(
+                        (element: SlaProcess) => element.Name == slaProcessLocal['EntitlementProcess']['name']
+                    );
+ 
+                    if (
+                        slaProcessMatchedByName &&
+                        entitlementSettings.enableEntitlementVersioning &&
+                        slaProcessLocal['EntitlementProcess']['versionNumber'] > slaProcessMatchedByName.VersionNumber
+                    ) {
+                        //This is a deployment candidate
+                        //Modify versionMaster tag to match in the org
+                        slaProcessLocal['EntitlementProcess']['versionMaster'] = slaProcessMatchedByName.VersionMaster;
+                        let builder = new XMLBuilder({
+                            format: true,
+                            ignoreAttributes: false,
+                            attributeNamePrefix: '@_',
+                        });
+                        let xmlContent = builder.build(slaProcessLocal);
+                        fs.writeFileSync(sourceComponent.xml, xmlContent);
+                        modifiedComponentSet.add(sourceComponent);
+                    } else if (slaProcessMatchedByName) {
+                        SFPLogger.log(
+                            `Skipping EntitlementProcess ${sourceComponent.name} as this version is already deployed`,
+                            LoggerLevel.INFO,
+                            logger
+                        );
+                    } else {
+                        //Doesnt exist, deploy
+                        modifiedComponentSet.add(sourceComponent);
+                    }
+                } else {
+                    modifiedComponentSet.add(sourceComponent);
+                }
+            }
+ 
+            SFPLogger.log(`Completed Filtering of EntitlementProcess\n`, LoggerLevel.INFO, logger);
+            return modifiedComponentSet;
+        } catch (error) {
+            SFPLogger.log(`Unable to filter entitlements, returning the unmodified package`, LoggerLevel.ERROR, logger);
+            return componentSet;
+        }
+    }
+ 
+    public isToApply(projectConfig: any, packageType: string): boolean {
+        Iif (packageType != PackageType.Source) return false;
+ 
+        if (projectConfig?.plugins?.sfp?.disableEntitlementFilter) return false;
+        else return true;
+    }
+ 
+    
+ 
+   
+}
+ 
+interface SlaProcess {
+    Name: string;
+    NameNorm: string;
+    VersionNumber: string;
+    VersionMaster: string;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/deploymentFilters/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/deploymentFilters/index.html new file mode 100644 index 000000000..365701931 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/deploymentFilters/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/core/package/deploymentFilters + + + + + + + + + +
+
+

All files src/core/package/deploymentFilters

+
+ +
+ 84.09% + Statements + 37/44 +
+ + +
+ 42.85% + Branches + 3/7 +
+ + +
+ 66.66% + Functions + 2/3 +
+ + +
+ 90.24% + Lines + 37/41 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
EntitlementVersionFilter.ts +
+
84.09%37/4442.85%3/766.66%2/390.24%37/41
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/diff/PackageComponentDiff.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/diff/PackageComponentDiff.ts.html new file mode 100644 index 000000000..97c3d3034 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/diff/PackageComponentDiff.ts.html @@ -0,0 +1,1357 @@ + + + + + + Code coverage report for src/core/package/diff/PackageComponentDiff.ts + + + + + + + + + +
+
+

All files / src/core/package/diff PackageComponentDiff.ts

+
+ +
+ 13.72% + Statements + 21/153 +
+ + +
+ 10.52% + Branches + 2/19 +
+ + +
+ 8.33% + Functions + 1/12 +
+ + +
+ 13.72% + Lines + 21/153 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +4251x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +  + 
import * as xml2js from 'xml2js';
+import * as path from 'path';
+import * as fs from 'fs-extra';
+import * as rimraf from 'rimraf';
+import * as _ from 'lodash';
+import simplegit from 'simple-git';
+import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import ProjectConfig from '../../project/ProjectConfig';
+import MetadataFiles from '../../metadata/MetadataFiles';
+import { SOURCE_EXTENSION_REGEX, MetadataInfo, METADATA_INFO } from '../../metadata/MetadataInfo';
+import { MetadataResolver } from '@salesforce/source-deploy-retrieve';
+import GitDiffUtils, { DiffFile, DiffFileStatus } from '../../git/GitDiffUtil';
+ 
+const deleteNotSupported = ['RecordType'];
+const git = simplegit();
+let sfdxManifest;
+ 
+export default class PackageComponentDiff {
+    private gitDiffUtils: GitDiffUtils;
+ 
+    destructivePackageObjPre: any[];
+    destructivePackageObjPost: any[];
+    resultOutput: {
+        action: string;
+        metadataType: string;
+        componentName: string;
+        message: string;
+        path: string;
+    }[];
+    public constructor(
+        private logger: Logger,
+        private sfdxPackage: string,
+        private revisionFrom?: string,
+        private revisionTo?: string,
+        private isDestructive?: boolean
+    ) {
+        if (this.revisionTo == null || this.revisionTo.trim() === '') {
+            this.revisionTo = 'HEAD';
+        }
+        if (this.revisionFrom == null) {
+            this.revisionFrom = '';
+        }
+        this.destructivePackageObjPost = [];
+        this.destructivePackageObjPre = [];
+        this.resultOutput = [];
+ 
+        sfdxManifest = ProjectConfig.getSFDXProjectConfig(null);
+        this.gitDiffUtils = new GitDiffUtils();
+    }
+ 
+    public async build(outputFolder: string) {
+        rimraf.sync(outputFolder);
+ 
+        const sepRegex = /\n|\r/;
+        let data = '';
+ 
+        //check if same commit
+        const commitFrom = await git.raw(['rev-list', '-n', '1', this.revisionFrom]);
+        const commitTo = await git.raw(['rev-list', '-n', '1', this.revisionTo]);
+        if (commitFrom === commitTo) {
+            throw new Error(`Unable to compute diff, as both commits are same`);
+        }
+        //Make it relative to make the command works from a project created as a subfolder in a repository
+        data = await git.diff([
+            '--raw',
+            this.revisionFrom,
+            this.revisionTo,
+            '--relative',
+            ProjectConfig.getPackageDescriptorFromConfig(this.sfdxPackage, sfdxManifest).path,
+        ]);
+ 
+        let content = data.split(sepRegex);
+        let diffFile: DiffFile = await this.parseContent(content);
+        await this.gitDiffUtils.fetchFileListRevisionTo(this.revisionTo, this.logger);
+ 
+        let filesToCopy = diffFile.addedEdited;
+        let deletedFiles = diffFile.deleted;
+ 
+        deletedFiles = deletedFiles.filter((deleted) => {
+            let found = false;
+            let deletedMetadata = MetadataFiles.getFullApiNameWithExtension(deleted.path);
+            for (let i = 0; i < filesToCopy.length; i++) {
+                let addedOrEdited = MetadataFiles.getFullApiNameWithExtension(filesToCopy[i].path);
+                if (deletedMetadata === addedOrEdited) {
+                    found = true;
+                    break;
+                }
+            }
+            return !found;
+        });
+ 
+        if (fs.existsSync(outputFolder) == false) {
+            fs.mkdirSync(outputFolder);
+        }
+ 
+        const resolver = new MetadataResolver();
+ 
+        if (filesToCopy && filesToCopy.length > 0) {
+            for (let i = 0; i < filesToCopy.length; i++) {
+ 
+                try {
+                    let filePath = filesToCopy[i].path;
+ 
+                    let sourceComponents = resolver.getComponentsFromPath(filePath);
+                    for (const sourceComponent of sourceComponents) {
+                        if (sourceComponent.type.strategies?.adapter == AdapterId.MatchingContentFile) {
+                            await this.gitDiffUtils.copyFile(sourceComponent.xml, outputFolder, this.logger);
+                            await this.gitDiffUtils.copyFile(sourceComponent.content, outputFolder, this.logger);
+                        } else if (sourceComponent.type.strategies?.adapter == AdapterId.MixedContent) {
+                            await this.gitDiffUtils.copyFile(sourceComponent.xml, outputFolder, this.logger);
+                            if(path.extname(sourceComponent.content))
+                              await this.gitDiffUtils.copyFile(sourceComponent.content, outputFolder, this.logger);
+                            else
+                              await this.gitDiffUtils.copyFolder(sourceComponent.content, outputFolder, this.logger);
+                        } else if (sourceComponent.type.strategies?.adapter == AdapterId.Decomposed) {
+                            await this.gitDiffUtils.copyFile(sourceComponent.xml, outputFolder, this.logger);
+                        } else if (sourceComponent.type.strategies?.adapter == AdapterId.Bundle) {
+                            await this.gitDiffUtils.copyFolder(sourceComponent.content, outputFolder, this.logger);
+                        } else {
+                            await this.gitDiffUtils.copyFile(sourceComponent.xml, outputFolder, this.logger);
+                        }
+                    }
+                } catch (error) {
+                    
+                   Iif(error.message.includes(`Unable to find the required file`))
+                    throw error;
+ 
+                    //Metadata resolver is not respecting forceignores at this stage
+                    // So it fails on diff packages with post deploy, so lets ignore and move on
+                    SFPLogger.log(
+                        `Error while inferencing type of  ${filesToCopy[i].path} to ${outputFolder} : ${error.message}`,
+                        LoggerLevel.TRACE,
+                        this.logger
+                    );
+                }
+            }
+        }
+ 
+        if (this.isDestructive) {
+            SFPLogger.log('Creating Destructive Manifest..', LoggerLevel.TRACE, this.logger);
+            await this.createDestructiveChanges(deletedFiles, outputFolder);
+        }
+ 
+        //Folder is empty after all this operations, return without copying additional files
+        if (fs.readdirSync(outputFolder).length === 0) {
+            rimraf.sync(outputFolder);
+            return null;
+        }
+ 
+        SFPLogger.log(`Generating output summary`, LoggerLevel.TRACE, this.logger);
+ 
+        return this.resultOutput;
+    }
+ 
+    //TODO: Refactor using proper ignore
+    private checkForIngore(pathToIgnore: any[], filePath: string) {
+        pathToIgnore = pathToIgnore || [];
+        if (pathToIgnore.length === 0) {
+            return true;
+        }
+ 
+        let returnVal = true;
+        pathToIgnore.forEach((ignore) => {
+            if (
+                path.resolve(ignore) === path.resolve(filePath) ||
+                path.resolve(filePath).includes(path.resolve(ignore))
+            ) {
+                returnVal = false;
+            }
+        });
+        return returnVal;
+    }
+ 
+    private async createDestructiveChanges(filePaths: DiffFileStatus[], outputFolder: string) {
+        if (_.isNil(this.destructivePackageObjPost)) {
+            this.destructivePackageObjPost = [];
+        } else {
+            this.destructivePackageObjPost = this.destructivePackageObjPost.filter((metaType) => {
+                return !_.isNil(metaType.members) && metaType.members.length > 0;
+            });
+        }
+        this.destructivePackageObjPre = [];
+        //returns root, dir, base and name
+        for (let i = 0; i < filePaths.length; i++) {
+            let filePath = filePaths[i].path;
+            try {
+                let matcher = filePath.match(SOURCE_EXTENSION_REGEX);
+                let extension = '';
+                if (matcher) {
+                    extension = matcher[0];
+                } else {
+                    extension = path.parse(filePath).ext;
+                }
+ 
+                let name = MetadataInfo.getMetadataName(filePath);
+ 
+                if (name) {
+                    if (!MetadataFiles.isCustomMetadata(filePath, name)) {
+                        // avoid to generate destructive for Standard Components
+                        //Support on Custom Fields and Custom Objects for now
+ 
+                        this.resultOutput.push({
+                            action: 'Skip',
+                            componentName: MetadataFiles.getMemberNameFromFilepath(filePath, name),
+                            metadataType: 'StandardField/CustomMetadata',
+                            message: '',
+                            path: '--',
+                        });
+ 
+                        continue;
+                    }
+                    let member = MetadataFiles.getMemberNameFromFilepath(filePath, name);
+                    if (name === METADATA_INFO.CustomField.xmlName) {
+                        let isFormular = await this.gitDiffUtils.isFileIncludesContent(filePaths[i], '<formula>');
+                        if (isFormular) {
+                            this.destructivePackageObjPre = this.buildDestructiveTypeObj(
+                                this.destructivePackageObjPre,
+                                name,
+                                member
+                            );
+ 
+                            SFPLogger.log(
+                                `${filePath} ${MetadataFiles.isCustomMetadata(filePath, name)}`,
+                                LoggerLevel.DEBUG,
+                                this.logger
+                            );
+ 
+                            this.resultOutput.push({
+                                action: 'Delete',
+                                componentName: member,
+                                metadataType: name,
+                                message: '',
+                                path: 'Manual Intervention Required',
+                            });
+                        } else {
+                            this.destructivePackageObjPost = this.buildDestructiveTypeObj(
+                                this.destructivePackageObjPost,
+                                name,
+                                member
+                            );
+                        }
+                        SFPLogger.log(
+                            `${filePath} ${MetadataFiles.isCustomMetadata(filePath, name)}`,
+                            LoggerLevel.DEBUG,
+                            this.logger
+                        );
+ 
+                        this.resultOutput.push({
+                            action: 'Delete',
+                            componentName: member,
+                            metadataType: name,
+                            message: '',
+                            path: 'destructiveChanges.xml',
+                        });
+                    } else {
+                        if (!deleteNotSupported.includes(name)) {
+                            this.destructivePackageObjPost = this.buildDestructiveTypeObj(
+                                this.destructivePackageObjPost,
+                                name,
+                                member
+                            );
+                            this.resultOutput.push({
+                                action: 'Delete',
+                                componentName: member,
+                                metadataType: name,
+                                message: '',
+                                path: 'destructiveChanges.xml',
+                            });
+                        } else {
+                            //add the component in the manual action list
+                            // TODO
+                        }
+                    }
+                }
+            } catch (ex) {
+                this.resultOutput.push({
+                    action: 'ERROR',
+                    componentName: '',
+                    metadataType: '',
+                    message: ex.message,
+                    path: filePath,
+                });
+            }
+        }
+ 
+        this.writeDestructivechanges(this.destructivePackageObjPost, outputFolder, 'destructiveChanges.xml');
+    }
+ 
+    private writeDestructivechanges(destrucObj: Array<any>, outputFolder: string, fileName: string) {
+        //ensure unique component per type
+        for (let i = 0; i < destrucObj.length; i++) {
+            destrucObj[i].members = _.uniq(destrucObj[i].members);
+        }
+        destrucObj = destrucObj.filter((metaType) => {
+            return metaType.members && metaType.members.length > 0;
+        });
+ 
+        if (destrucObj.length > 0) {
+            let dest = {
+                Package: {
+                    $: {
+                        xmlns: 'http://soap.sforce.com/2006/04/metadata',
+                    },
+                    types: destrucObj,
+                },
+            };
+ 
+            let destructivePackageName = fileName;
+            let filepath = path.join(outputFolder, destructivePackageName);
+            let builder = new xml2js.Builder();
+            let xml = builder.buildObject(dest);
+            fs.writeFileSync(filepath, xml);
+        }
+    }
+ 
+    private buildDestructiveTypeObj(destructiveObj, name, member) {
+        let typeIsPresent = false;
+        for (let i = 0; i < destructiveObj.length; i++) {
+            if (destructiveObj[i].name === name) {
+                typeIsPresent = true;
+                destructiveObj[i].members.push(member);
+                break;
+            }
+        }
+        let typeNode: any;
+        if (typeIsPresent === false) {
+            typeNode = {
+                name: name,
+                members: [member],
+            };
+            destructiveObj.push(typeNode);
+        }
+        return destructiveObj;
+    }
+ 
+    private async parseContent(fileContents): Promise<DiffFile> {
+        const statusRegEx = /\sA\t|\sM\t|\sD\t/;
+        const renamedRegEx = /\sR[0-9]{3}\t|\sC[0-9]{3}\t/;
+        const tabRegEx = /\t/;
+        const deletedFileRegEx = new RegExp(/\sD\t/);
+        const lineBreakRegEx = /\r?\n|\r|( $)/;
+ 
+        let metadataFiles = new MetadataFiles();
+ 
+        let diffFile: DiffFile = {
+            deleted: [],
+            addedEdited: [],
+        };
+ 
+        for (let i = 0; i < fileContents.length; i++) {
+            if (statusRegEx.test(fileContents[i])) {
+                let lineParts = fileContents[i].split(statusRegEx);
+ 
+                let finalPath = path.join('.', lineParts[1].replace(lineBreakRegEx, ''));
+                finalPath = finalPath.trim();
+                finalPath = finalPath.replace('\\303\\251', 'é');
+ 
+                if (!(await metadataFiles.isInModuleFolder(finalPath))) {
+                    continue;
+                }
+ 
+                if (!metadataFiles.accepts(finalPath)) {
+                    continue;
+                }
+ 
+                let revisionPart = lineParts[0].split(/\t|\s/);
+ 
+                if (deletedFileRegEx.test(fileContents[i])) {
+                    //Deleted
+                    diffFile.deleted.push({
+                        revisionFrom: revisionPart[2].substring(0, 9),
+                        revisionTo: revisionPart[3].substring(0, 9),
+                        path: finalPath,
+                    });
+                } else {
+                    // Added or edited
+                    diffFile.addedEdited.push({
+                        revisionFrom: revisionPart[2].substring(0, 9),
+                        revisionTo: revisionPart[3].substring(0, 9),
+                        path: finalPath,
+                    });
+                }
+            } else if (renamedRegEx.test(fileContents[i])) {
+                let lineParts = fileContents[i].split(renamedRegEx);
+ 
+                let paths = lineParts[1].trim().split(tabRegEx);
+ 
+                let finalPath = path.join('.', paths[1].trim());
+                finalPath = finalPath.replace('\\303\\251', 'é');
+                let revisionPart = lineParts[0].split(/\t|\s/);
+ 
+                if (!(await metadataFiles.isInModuleFolder(finalPath))) {
+                    continue;
+                }
+ 
+                if (!metadataFiles.accepts(paths[0].trim())) {
+                    continue;
+                }
+ 
+                diffFile.addedEdited.push({
+                    revisionFrom: '0000000',
+                    revisionTo: revisionPart[3],
+                    renamedPath: path.join('.', paths[0].trim()),
+                    path: finalPath,
+                });
+ 
+                //allow deletion of renamed components
+                diffFile.deleted.push({
+                    revisionFrom: revisionPart[2],
+                    revisionTo: '0000000',
+                    path: paths[0].trim(),
+                });
+            }
+        }
+        return diffFile;
+    }
+}
+enum AdapterId {
+    Bundle = 'bundle',
+    Decomposed = 'decomposed',
+    Default = 'default',
+    MatchingContentFile = 'matchingContentFile',
+    MixedContent = 'mixedContent',
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/diff/PackageDiffImpl.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/diff/PackageDiffImpl.ts.html new file mode 100644 index 000000000..e99b30d1f --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/diff/PackageDiffImpl.ts.html @@ -0,0 +1,583 @@ + + + + + + Code coverage report for src/core/package/diff/PackageDiffImpl.ts + + + + + + + + + +
+
+

All files / src/core/package/diff PackageDiffImpl.ts

+
+ +
+ 84.72% + Statements + 61/72 +
+ + +
+ 81.81% + Branches + 9/11 +
+ + +
+ 85.71% + Functions + 6/7 +
+ + +
+ 86.95% + Lines + 60/69 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +1671x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +1x +  +1x +  +  +  +  +1x +  +9x +9x +9x +9x +  +  +  +9x +  +9x +9x +  +8x +  +  +  +  +  +  +  +  +  +  +  +8x +  +  +  +6x +  +  +  +  +6x +  +  +  +  +  +  +  +  +6x +  +6x +  +6x +  +  +  +  +  +  +  +  +7x +7x +  +7x +  +  +1x +1x +  +  +  +5x +  +  +  +  +5x +  +1x +  +  +4x +  +2x +  +  +  +  +2x +  +  +  +  +  +6x +6x +6x +  +6x +  +  +6x +  +6x +  +  +  +8x +8x +  +8x +  +  +  +8x +  +  +8x +  +  +  +5x +5x +  +  +  +  +5x +  +  +  +5x +2x +  +  +2x +1x +1x +1x +3x +  +  +  +  +  +  +  +  +  +  + 
const fs = require('fs');
+const path = require('path');
+import Git from '../../git/Git';
+import IgnoreFiles from '../../ignore/IgnoreFiles';
+import SFPLogger, { COLOR_ERROR, COLOR_KEY_MESSAGE, Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import ProjectConfig from '../../project/ProjectConfig';
+import GitTags from '../../git/GitTags';
+import lodash = require('lodash');
+import { EOL } from 'os';
+import { PackageType } from '../SfpPackage';
+ 
+export class PackageDiffOptions {
+    skipPackageDescriptorChange?: boolean = false;
+    //If not set, utlize latest git tags
+    useLatestGitTags?:boolean=true;
+    packagesMappedToLastKnownCommitId?: { [p: string]: string };
+    pathToReplacementForceIgnore?: string;
+}
+ 
+export default class PackageDiffImpl {
+    public constructor(
+        private logger: Logger,
+        private sfdx_package: string,
+        private project_directory: string|null,
+        private diffOptions?: PackageDiffOptions
+    ) {}
+ 
+    public async exec(): Promise<{ isToBeBuilt: boolean; reason: string; tag?: string }> {
+        let git: Git = await Git.initiateRepo(this.logger,this.project_directory);
+ 
+        let projectConfig = ProjectConfig.getSFDXProjectConfig(this.project_directory);
+        let pkgDescriptor = ProjectConfig.getPackageDescriptorFromConfig(this.sfdx_package, projectConfig);
+ 
+        SFPLogger.log(
+            COLOR_KEY_MESSAGE(
+                `${EOL}Checking last known tags for ${this.sfdx_package} to determine whether package is to be built...`,
+            ),
+            LoggerLevel.TRACE,
+            this.logger
+        );
+ 
+        let tag: string;
+        if (!this.diffOptions?.useLatestGitTags && this.diffOptions?.packagesMappedToLastKnownCommitId != null) {
+            tag = this.getLatestCommitFromMap(this.sfdx_package, this.diffOptions?.packagesMappedToLastKnownCommitId);
+        } else {
+            tag = await this.getLatestTagFromGit(git, this.sfdx_package);
+        }
+ 
+        if (tag) {
+            SFPLogger.log(COLOR_KEY_MESSAGE(`\nUtilizing tag ${tag} for ${this.sfdx_package}`),LoggerLevel.TRACE,this.logger);
+ 
+            // Get the list of modified files between the tag and HEAD refs
+            let modified_files: string[];
+            try {
+                modified_files = await git.diff([`${tag}`, `HEAD`, `--no-renames`, `--name-only`]);
+            } catch (error) {
+                SFPLogger.log(COLOR_ERROR(`Unable to compute diff, The head of the branch is not reachable from the commit id ${tag}`));
+                SFPLogger.log(COLOR_ERROR(`Check your current branch (in case of build) or the scratch org in case of validate command`));
+                SFPLogger.log(COLOR_ERROR(`Actual error received:`));
+                SFPLogger.log(COLOR_ERROR(error));
+                throw new Error(`Failed to compute git diff for package ${this.sfdx_package} against commit id ${tag}`)
+            }
+ 
+            let packageType: string = ProjectConfig.getPackageType(projectConfig, this.sfdx_package);
+ 
+            if (packageType !== PackageType.Data) modified_files = this.applyForceIgnoreToModifiedFiles(modified_files);
+ 
+            SFPLogger.log(
+                `Checking for changes in source directory ${path.normalize(pkgDescriptor.path)}`,
+                LoggerLevel.TRACE,
+                this.logger
+            );
+ 
+            // Check whether the package has been modified
+            for (let filename of modified_files) {
+                
+                let normalizedPkgPath = path.normalize(pkgDescriptor.path);
+                let normalizedFilename = path.normalize(filename);
+            
+                let relativePath = path.relative(normalizedPkgPath, normalizedFilename);
+            
+                if (!relativePath.startsWith('..')) {
+                    SFPLogger.log(`Found change(s) in ${filename}`, LoggerLevel.TRACE, this.logger);
+                    return { isToBeBuilt: true, reason: `Found change(s) in package`, tag: tag };
+                }
+            }
+ 
+            SFPLogger.log(
+                `Checking for changes to package descriptor in sfdx-project.json`,
+                LoggerLevel.TRACE,
+                this.logger
+            );
+            let isPackageDescriptorChanged = await this.isPackageDescriptorChanged(git, tag, pkgDescriptor);
+            if (isPackageDescriptorChanged) {
+                return { isToBeBuilt: true, reason: `Package Descriptor Changed`, tag: tag };
+            }
+ 
+            return { isToBeBuilt: false, reason: `No changes found`, tag: tag };
+        } else {
+            SFPLogger.log(
+                `Tag missing for ${this.sfdx_package}...marking package for build anyways`,
+                LoggerLevel.TRACE,
+                this.logger
+            );
+            return { isToBeBuilt: true, reason: `Previous version not found` };
+        }
+    }
+ 
+    private applyForceIgnoreToModifiedFiles(modified_files: string[]) {
+        let forceignorePath: string;
+        Iif (this.diffOptions?.pathToReplacementForceIgnore) forceignorePath = this.diffOptions?.pathToReplacementForceIgnore;
+        else Iif (this.project_directory != null) forceignorePath = path.join(this.project_directory, '.forceignore');
+        else forceignorePath = '.forceignore';
+ 
+        let ignoreFiles: IgnoreFiles = new IgnoreFiles(fs.readFileSync(forceignorePath).toString());
+ 
+        // Filter the list of modified files with .forceignore
+        modified_files = ignoreFiles.filter(modified_files);
+ 
+        return modified_files;
+    }
+ 
+    private async getLatestTagFromGit(git: Git, sfdx_package: string): Promise<string> {
+        const gitTags: GitTags = new GitTags(git, sfdx_package);
+        let tags: string[] = await gitTags.listTagsOnBranch();
+ 
+        SFPLogger.log('Analysing tags:', LoggerLevel.DEBUG);
+        if (tags.length > 10) {
+            SFPLogger.log(tags.slice(-10).toString().replace(/,/g, '\n'), LoggerLevel.TRACE,this.logger);
+        } else {
+            SFPLogger.log(tags.toString().replace(/,/g, '\n'), LoggerLevel.TRACE,this.logger);
+        }
+ 
+        return tags.pop();
+    }
+ 
+    private async isPackageDescriptorChanged(git: Git, latestTag: string, packageDescriptor: any): Promise<boolean> {
+        let projectConfigJson: string = await git.show([`${latestTag}:sfdx-project.json`]);
+        let projectConfig = JSON.parse(projectConfigJson);
+ 
+        let packageDescriptorFromLatestTag: string;
+        for (let dir of projectConfig['packageDirectories']) {
+            if (this.sfdx_package === dir.package) {
+                packageDescriptorFromLatestTag = dir;
+            }
+        }
+ 
+        if (!lodash.isEqual(packageDescriptor, packageDescriptorFromLatestTag)) {
+            SFPLogger.log(`Found change in ${this.sfdx_package} package descriptor`, LoggerLevel.TRACE, this.logger);
+ 
+            //skip check and ignore
+            if (this.diffOptions?.skipPackageDescriptorChange) {
+                SFPLogger.log(`Ignoring changes in package desriptor as asked to..`, LoggerLevel.TRACE, this.logger);
+                return false;
+            } else return true;
+        } else return false;
+    }
+ 
+    private getLatestCommitFromMap(sfdx_package: string, packagesToCommits: { [p: string]: string }): string {
+        if (packagesToCommits[sfdx_package] != null) {
+            return packagesToCommits[sfdx_package];
+        } else {
+            return null;
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/diff/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/diff/index.html new file mode 100644 index 000000000..945749465 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/diff/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/core/package/diff + + + + + + + + + +
+
+

All files src/core/package/diff

+
+ +
+ 36.44% + Statements + 82/225 +
+ + +
+ 36.66% + Branches + 11/30 +
+ + +
+ 36.84% + Functions + 7/19 +
+ + +
+ 36.48% + Lines + 81/222 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
PackageComponentDiff.ts +
+
13.72%21/15310.52%2/198.33%1/1213.72%21/153
PackageDiffImpl.ts +
+
84.72%61/7281.81%9/1185.71%6/786.95%60/69
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/index.html new file mode 100644 index 000000000..c9f65a9df --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/core/package + + + + + + + + + +
+
+

All files src/core/package

+
+ +
+ 26.92% + Statements + 35/130 +
+ + +
+ 9.52% + Branches + 2/21 +
+ + +
+ 28.57% + Functions + 4/14 +
+ + +
+ 28% + Lines + 35/125 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
SfpPackage.ts +
+
47.82%11/23100%2/250%4/847.82%11/23
SfpPackageBuilder.ts +
+
22.42%24/1070%0/190%0/623.52%24/102
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/CreateDataPackageImpl.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/CreateDataPackageImpl.ts.html new file mode 100644 index 000000000..333cd62d0 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/CreateDataPackageImpl.ts.html @@ -0,0 +1,343 @@ + + + + + + Code coverage report for src/core/package/packageCreators/CreateDataPackageImpl.ts + + + + + + + + + +
+
+

All files / src/core/package/packageCreators CreateDataPackageImpl.ts

+
+ +
+ 21.62% + Statements + 8/37 +
+ + +
+ 0% + Branches + 0/9 +
+ + +
+ 0% + Functions + 0/11 +
+ + +
+ 26.66% + Lines + 8/30 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +871x +1x +1x +1x +1x +  +  +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import SFPLogger, { LoggerLevel, Logger } from '@flxblio/sfp-logger';
+import path from 'path';
+import FileSystem from '../../utils/FileSystem';
+import { CreatePackage } from './CreatePackage';
+import SfpPackage, { PackageType, SfpPackageParams } from '../SfpPackage';
+import { PackageCreationParams } from '../SfpPackageBuilder';
+ 
+const SFDMU_CONFIG = 'export.json';
+const VLOCITY_CONFIG = 'VlocityComponents.yaml';
+ 
+export default class CreateDataPackageImpl extends CreatePackage {
+    public constructor(
+        protected projectDirectory: string,
+        protected sfpPackage: SfpPackage,
+        protected packageCreationParams: PackageCreationParams,
+        protected logger?: Logger,
+        protected params?: SfpPackageParams
+    ) {
+        super(projectDirectory, sfpPackage, packageCreationParams, logger, params);
+    }
+ 
+    getTypeOfPackage() {
+        return PackageType.Data;
+    }
+ 
+    isEmptyPackage(projectDirectory: string, packageDirectory: string): boolean {
+        let files: string[] = FileSystem.readdirRecursive(path.join(projectDirectory, packageDirectory));
+ 
+        let hasExportJson = files.find((file) => path.basename(file) === 'export.json');
+ 
+        let hasCsvFile = files.find((file) => path.extname(file) === '.csv');
+ 
+        let hasYAMLFile = files.find((file) => path.extname(file) === '.yaml'); //check for vlocity config
+ 
+        Iif(hasYAMLFile) return false;
+ 
+        if (!hasExportJson || !hasCsvFile) return true;
+        else return false;
+    }
+ 
+    preCreatePackage(sfpPackage) {
+        this.validateDataPackage(sfpPackage.resolvedPackageDirectory);
+    }
+ 
+    createPackage(sfpPackage: SfpPackage) {
+        //Do Nothing, as no external calls or processing is required
+    }
+ 
+    postCreatePackage(sfpPackage: SfpPackage) {}
+ 
+    printAdditionalPackageSpecificHeaders() {}
+ 
+    // Validate type of data package and existence of the correct configuration files
+    private validateDataPackage(packageDirectory: string) {
+        const files = FileSystem.readdirRecursive(packageDirectory);
+        let isSfdmu: boolean;
+        let isVlocity: boolean;
+ 
+        for (const file of files) {
+            Iif (path.basename(file) === SFDMU_CONFIG) isSfdmu = true;
+            Iif (path.basename(file) === VLOCITY_CONFIG) isVlocity = true;
+        }
+ 
+        if (isSfdmu && isVlocity) {
+            throw new Error(
+                `Data package '${this.sfpPackage.packageName}' contains both SFDMU & Vlocity configuration`
+            );
+        } else if (isSfdmu) {
+            SFPLogger.log(
+                `Found export.json in ${packageDirectory}.. Utilizing it as data package and will be deployed using sfdmu`,
+                LoggerLevel.INFO,
+                this.logger
+            );
+        } else if (isVlocity) {
+            SFPLogger.log(
+                `Found VlocityComponents.yaml in ${packageDirectory}.. Utilizing it as data package and will be deployed using vbt`,
+                LoggerLevel.INFO,
+                this.logger
+            );
+        } else {
+            throw new Error(
+                `Could not find export.json or VlocityComponents.yaml in ${packageDirectory}. sfp only support vlocity or sfdmu based data packages`
+            );
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/CreateDiffPackageImpl.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/CreateDiffPackageImpl.ts.html new file mode 100644 index 000000000..8da0a4057 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/CreateDiffPackageImpl.ts.html @@ -0,0 +1,898 @@ + + + + + + Code coverage report for src/core/package/packageCreators/CreateDiffPackageImpl.ts + + + + + + + + + +
+
+

All files / src/core/package/packageCreators CreateDiffPackageImpl.ts

+
+ +
+ 14.41% + Statements + 16/111 +
+ + +
+ 0% + Branches + 0/9 +
+ + +
+ 0% + Functions + 0/22 +
+ + +
+ 15.53% + Lines + 16/103 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +2721x +  +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import SFPLogger, { LoggerLevel, Logger } from '@flxblio/sfp-logger';
+import { ApexSortedByType } from '../../apex/parser/ApexTypeFetcher';
+import SFPStatsSender from '../../stats/SFPStatsSender';
+import PackageEmptyChecker from '../validators/PackageEmptyChecker';
+import SfpPackage, { DiffPackageMetadata, PackageType, SfpPackageParams } from '../SfpPackage';
+import { PackageCreationParams } from '../SfpPackageBuilder';
+import SFPOrg from '../../org/SFPOrg';
+import CreateSourcePackageImpl from './CreateSourcePackageImpl';
+import PackageToComponent from '../components/PackageToComponent';
+import path from 'path';
+import * as fs from 'fs-extra';
+import ImpactedApexTestClassFetcher from '../../apextest/ImpactedApexTestClassFetcher';
+import SourceToMDAPIConvertor from '../packageFormatConvertors/SourceToMDAPIConvertor';
+import PackageManifest from '../components/PackageManifest';
+import MetadataCount from '../components/MetadataCount';
+import * as rimraf from 'rimraf';
+import Component from '../../dependency/Component';
+import PackageComponentDiff from '../diff/PackageComponentDiff';
+ 
+export default class CreateDiffPackageImp extends CreateSourcePackageImpl {
+    public constructor(
+        protected projectDirectory: string,
+        protected sfpPackage: SfpPackage,
+        protected packageCreationParams: PackageCreationParams,
+        protected logger?: Logger,
+        protected params?: SfpPackageParams
+    ) {
+        super(projectDirectory, sfpPackage, packageCreationParams, logger, params);
+    }
+ 
+    getTypeOfPackage() {
+        return PackageType.Diff;
+    }
+ 
+    printAdditionalPackageSpecificHeaders() {}
+ 
+    isEmptyPackage(projectDirectory: string, packageDirectory: string) {
+        return PackageEmptyChecker.isEmptyFolder(projectDirectory, packageDirectory);
+    }
+ 
+    async preCreatePackage(sfpPackage: SfpPackage) {
+        const devhubOrg = await SFPOrg.create({ aliasOrUsername: this.packageCreationParams.devHub });
+ 
+        //Fetch Baseline commit from DevHub
+        let commitsOfPackagesInstalledInDevHub = await this.getCommitsOfPackagesInstalledInDevHub(devhubOrg);
+ 
+        if (this.packageCreationParams.revisionFrom) {
+            this.sfpPackage.commitSHAFrom = this.packageCreationParams.revisionFrom;
+        } else if (commitsOfPackagesInstalledInDevHub[this.sfpPackage.packageName]) {
+            this.sfpPackage.commitSHAFrom = commitsOfPackagesInstalledInDevHub[this.sfpPackage.packageName];
+        } else {
+            this.sfpPackage.commitSHAFrom = this.sfpPackage.sourceVersion;
+        }
+ 
+        if (this.packageCreationParams.revisionTo) {
+            this.sfpPackage.commitSHATo = this.packageCreationParams.revisionTo;
+        } else {
+            this.sfpPackage.commitSHATo = this.sfpPackage.sourceVersion;
+        }
+    }
+ 
+    private async getCommitsOfPackagesInstalledInDevHub(diffTargetSfpOrg: SFPOrg) {
+        let installedArtifacts = await diffTargetSfpOrg.getInstalledArtifacts();
+        let packagesInstalledInOrgMappedToCommits = await this.mapInstalledArtifactstoPkgAndCommits(installedArtifacts);
+        return packagesInstalledInOrgMappedToCommits;
+    }
+ 
+    public async createPackage(sfpPackage: SfpPackage) {
+        //Unresolved SHAs can be same if the package is not installed in the org or is the same
+        if (this.sfpPackage.commitSHAFrom != this.sfpPackage.commitSHATo) {
+            try {
+                let packageComponentDiffer: PackageComponentDiff = new PackageComponentDiff(
+                    this.logger,
+                    this.sfpPackage.packageName,
+                    this.sfpPackage.commitSHAFrom,
+                    this.sfpPackage.commitSHATo,
+                    true
+                );
+                await packageComponentDiffer.build(path.join(sfpPackage.workingDirectory, 'diff'));
+            } catch (error) {
+                //if both are same after git resolution.. just do nothing, treat is a normal source package
+                if (error.message.includes('Unable to compute diff, as both commits are same')) {
+                    SFPLogger.log(
+                        `Both commits are same, treating it as an empty package`,
+                        LoggerLevel.WARN,
+                        this.logger
+                    );
+                    //Create an empty diff directory to force skip of packages
+                    const diffSrcDir = path.join(sfpPackage.workingDirectory, `diff/${sfpPackage.packageDirectory}`);
+                    fs.mkdirpSync(diffSrcDir);
+                } else throw error;
+            }
+ 
+            await this.introspectDiffPackageCreated(sfpPackage, this.params, this.logger);
+ 
+            await this.replaceSourceWithDiff(
+                sfpPackage.workingDirectory,
+                sfpPackage.packageDirectory,
+                `diff/${sfpPackage.packageDirectory}`
+            );
+ 
+            SFPStatsSender.logGauge('package.metadatacount', sfpPackage.metadataCount, {
+                package: sfpPackage.packageName,
+                type: sfpPackage.packageType,
+            });
+        }
+    }
+ 
+    postCreatePackage(sfpPackage) {}
+ 
+    private async replaceSourceWithDiff(
+        workingDirectory: string,
+        packageDirectory: string,
+        diffPackageDirectory: string
+    ) {
+        const srcDir = path.join(workingDirectory, packageDirectory);
+        const diffSrcDir = path.join(workingDirectory, diffPackageDirectory);
+ 
+        // Check if src directories exist, if so remove them
+        Iif (fs.pathExistsSync(srcDir)) await fs.remove(srcDir);
+ 
+        // Rename diff/src directory to src
+        if (fs.pathExistsSync(diffSrcDir)) await fs.move(diffSrcDir, srcDir);
+        else {
+            // Ensure package directory exists
+            await fs.mkdirpSync(diffSrcDir);
+            await fs.move(diffSrcDir, srcDir);
+        }
+ 
+        //check if destructiveChanges.xml exist in diff directory
+        const destructiveChangesPath = path.join(workingDirectory, 'diff', 'destructiveChanges.xml');
+        if (fs.existsSync(destructiveChangesPath)) {
+            //Move destructiveChanges.xml to diff directory
+            await fs.move(destructiveChangesPath, path.join(workingDirectory, 'destructiveChanges.xml'));
+        }
+        //remove diffSrcDir
+        Iif (fs.pathExistsSync(path.join(workingDirectory, 'diff')))
+            fs.removeSync(path.join(workingDirectory, 'diff'));
+    }
+ 
+    async mapInstalledArtifactstoPkgAndCommits(installedArtifacts: any) {
+        let packagesMappedToLastKnownCommitId: { [p: string]: string } = {};
+        packagesMappedToLastKnownCommitId = await getPackagesToCommits(installedArtifacts);
+ 
+        return packagesMappedToLastKnownCommitId;
+ 
+        async function getPackagesToCommits(installedArtifacts: any): Promise<{ [p: string]: string }> {
+            const packagesToCommits: { [p: string]: string } = {};
+            let jsonOverrides = {};
+ 
+            // Add an option to override diff package from during debugging
+            // Also useful for when the record is yet to be baselined
+            try {
+                const jsonData = await fs.readFile('diffPackageOverrides.json', 'utf8');
+                jsonOverrides = JSON.parse(jsonData);
+            } catch (error) {
+                console.log('No JSON override file found or there is an error reading it');
+            }
+ 
+            // Merge the installedArtifacts data with the JSON overrides
+            if (installedArtifacts) {
+                installedArtifacts.forEach((artifact) => {
+                    packagesToCommits[artifact.Name] = artifact.CommitId__c;
+                });
+            }
+ 
+            // Add additional packages from the JSON overrides that are not in installedArtifacts
+            Object.keys(jsonOverrides).forEach((pkgName) => {
+                if (!packagesToCommits.hasOwnProperty(pkgName)) {
+                    packagesToCommits[pkgName] = jsonOverrides[pkgName];
+                }
+            });
+ 
+            Iif (process.env.VALIDATE_REMOVE_PKG) delete packagesToCommits[process.env.VALIDATE_REMOVE_PKG];
+ 
+            return packagesToCommits;
+        }
+    }
+ 
+    private async introspectDiffPackageCreated(
+        sfpPackage: SfpPackage,
+        packageParams: SfpPackageParams,
+        logger: Logger
+    ): Promise<void> {
+        let workingDirectory = path.join(sfpPackage.workingDirectory, 'diff');
+        if (fs.existsSync(path.join(workingDirectory, sfpPackage.packageDirectory))) {
+            let changedComponents = new PackageToComponent(
+                sfpPackage.packageName,
+                path.join(workingDirectory, sfpPackage.packageDirectory)
+            ).generateComponents();
+ 
+            let impactedApexTestClassFetcher: ImpactedApexTestClassFetcher = new ImpactedApexTestClassFetcher(
+                sfpPackage,
+                changedComponents,
+                logger
+            );
+            let impactedTestClasses = await impactedApexTestClassFetcher.getImpactedTestClasses();
+ 
+            //Convert again for finding the values in the diff package
+            let sourceToMdapiConvertor = new SourceToMDAPIConvertor(
+                workingDirectory,
+                sfpPackage.packageDescriptor.path,
+                sfpPackage.apiVersion,
+                logger
+            );
+ 
+            let mdapiDirPath = (await sourceToMdapiConvertor.convert()).packagePath;
+ 
+            const packageManifest: PackageManifest = await PackageManifest.create(mdapiDirPath);
+ 
+            sfpPackage.payload = packageManifest.manifestJson;
+            sfpPackage.apexTestClassses = impactedTestClasses;
+            sfpPackage.apexClassWithOutTestClasses = getOnlyChangedClassesFromPackage(
+                changedComponents,
+                sfpPackage.apexClassesSortedByTypes
+            );
+            sfpPackage.isApexFound = packageManifest.isApexInPackage();
+            sfpPackage.isProfilesFound = packageManifest.isProfilesInPackage();
+            sfpPackage.isPermissionSetGroupFound = packageManifest.isPermissionSetGroupsFoundInPackage();
+            sfpPackage.isPayLoadContainTypesSupportedByProfiles = packageManifest.isPayLoadContainTypesSupportedByProfiles();
+ 
+            sfpPackage.metadataCount = await MetadataCount.getMetadataCount(
+                workingDirectory,
+                sfpPackage.packageDescriptor.path
+            );
+            rimraf.sync(mdapiDirPath);
+        } else {
+            //Souce Diff Directory is empty
+            sfpPackage.payload = {};
+            sfpPackage.apexTestClassses = [];
+            sfpPackage.apexClassWithOutTestClasses = [];
+            sfpPackage.isApexFound = false;
+            sfpPackage.isProfilesFound = false;
+            sfpPackage.isPermissionSetGroupFound = false;
+            sfpPackage.isPayLoadContainTypesSupportedByProfiles = false;
+            sfpPackage.metadataCount = 0;
+        }
+ 
+        function getOnlyChangedClassesFromPackage(
+            changedComponents: Component[],
+            apexClassesSortedByTypes: ApexSortedByType
+        ): string[] {
+            // Check if the parameters are not empty or undefined
+            if (!changedComponents || !apexClassesSortedByTypes) {
+                return undefined;
+            }
+ 
+            // Check if the 'class' property exists in apexClassesSortedByTypes
+            if (!apexClassesSortedByTypes.class) {
+                return undefined;
+            }
+ 
+            // Get the names of all classes in the ApexSortedByType
+            let apexClassNames = apexClassesSortedByTypes.class.map((cls) => cls.name);
+            let interfaces = apexClassesSortedByTypes.interface.map((cls) => cls.name);
+            const apexTestClassNames = apexClassesSortedByTypes.testClass.map((cls) => cls.name);
+            apexClassNames = apexClassNames.filter((name) => !apexTestClassNames.includes(name));
+            apexClassNames = apexClassNames.filter((name) => !interfaces.includes(name));
+ 
+            // Filter changedComponents based on class names in ApexSortedByType and type === 'ApexClass'
+            const filteredComponents = changedComponents.filter(
+                (component) => apexClassNames.includes(component.fullName) && component.type === 'ApexClass'
+            );
+ 
+            // Extract the fullName property from the filtered components
+            const filteredChangedClasses = filteredComponents.map((component) => component.fullName);
+ 
+            return filteredChangedClasses;
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/CreatePackage.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/CreatePackage.ts.html new file mode 100644 index 000000000..a1194a353 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/CreatePackage.ts.html @@ -0,0 +1,523 @@ + + + + + + Code coverage report for src/core/package/packageCreators/CreatePackage.ts + + + + + + + + + +
+
+

All files / src/core/package/packageCreators CreatePackage.ts

+
+ +
+ 8.69% + Statements + 4/46 +
+ + +
+ 0% + Branches + 0/10 +
+ + +
+ 0% + Functions + 0/7 +
+ + +
+ 8.88% + Lines + 4/45 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +1471x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE, COLOR_WARNING, Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import SFPStatsSender from '../../stats/SFPStatsSender';
+import SfpPackage, { PackageType, SfpPackageParams } from '../SfpPackage';
+import { PackageCreationParams } from '../SfpPackageBuilder';
+ 
+export abstract class CreatePackage {
+    private startTime: number;
+ 
+    constructor(
+        protected projectDirectory: string,
+        protected sfpPackage: SfpPackage,
+        protected packageCreationParams?: PackageCreationParams,
+        protected logger?: Logger,
+        protected params?: SfpPackageParams
+    ) {
+        //Initialize Params
+        Iif (this.params == null) this.params = {};
+    }
+ 
+    public async exec(): Promise<SfpPackage> {
+        //Capture Start TimegetSFDXProjectConfig
+        this.startTime = Date.now();
+ 
+        //Print Header
+        this.printHeader();
+ 
+        //Check if the package is empty
+        await this.checkWhetherProvidedPackageIsEmpty(this.sfpPackage.packageDescriptor.path);
+        //Call lifecycle commands
+        await this.preCreatePackage(this.sfpPackage);
+        await this.createPackage(this.sfpPackage);
+        await this.postCreatePackage(this.sfpPackage);
+ 
+        //Add addtional descriptors available
+        this.writeDeploymentStepsToArtifact(this.sfpPackage);
+ 
+        //Send Metrics to Logging system
+        this.sendMetricsWhenSuccessfullyCreated();
+ 
+        return this.sfpPackage;
+    }
+ 
+    abstract getTypeOfPackage();
+ 
+    abstract preCreatePackage(sfpPackage: SfpPackage);
+    abstract createPackage(sfpPackage: SfpPackage);
+    abstract postCreatePackage(sfpPackage: SfpPackage);
+ 
+    private sendMetricsWhenSuccessfullyCreated() {
+        let elapsedTime = Date.now() - this.startTime;
+ 
+        this.sfpPackage.creation_details = {
+            creation_time: elapsedTime,
+            timestamp: Date.now(),
+        };
+ 
+        Iif (this.getTypeOfPackage() === PackageType.Source || this.getTypeOfPackage() === PackageType.Unlocked)
+            SFPStatsSender.logGauge('package.metadatacount', this.sfpPackage.metadataCount, {
+                package: this.sfpPackage.package_name,
+                type: this.sfpPackage.package_type,
+            });
+ 
+        SFPStatsSender.logCount('package.created', {
+            package: this.sfpPackage.package_name,
+            type: this.sfpPackage.package_type,
+            is_dependency_validated: String(this.sfpPackage.isDependencyValidated),
+        });
+ 
+        SFPStatsSender.logElapsedTime('package.elapsed.time', this.sfpPackage.creation_details.creation_time, {
+            package: this.sfpPackage.package_name,
+            type: this.sfpPackage.package_type,
+            is_dependency_validated: String(this.sfpPackage.isDependencyValidated),
+        });
+        SFPStatsSender.logElapsedTime('package.creation.elapsed_time', this.sfpPackage.creation_details.creation_time, {
+            package: this.sfpPackage.package_name,
+            type: this.sfpPackage.package_type,
+            is_dependency_validated: String(this.sfpPackage.isDependencyValidated),
+        });
+    }
+ 
+    private writeDeploymentStepsToArtifact(packageDescriptor: any) {
+        if (packageDescriptor.assignPermSetsPreDeployment) {
+            if (packageDescriptor.assignPermSetsPreDeployment instanceof Array)
+                this.sfpPackage.assignPermSetsPreDeployment = packageDescriptor.assignPermSetsPreDeployment;
+            else throw new Error("Property 'assignPermSetsPreDeployment' must be of type array");
+        }
+ 
+        if (packageDescriptor.assignPermSetsPostDeployment) {
+            if (packageDescriptor.assignPermSetsPostDeployment instanceof Array)
+                this.sfpPackage.assignPermSetsPostDeployment = packageDescriptor.assignPermSetsPostDeployment;
+            else throw new Error("Property 'assignPermSetsPostDeployment' must be of type array");
+        }
+    }
+ 
+    private async checkWhetherProvidedPackageIsEmpty(packageDirectory: string) {
+        if (await this.isEmptyPackage(this.projectDirectory, packageDirectory)) {
+            if (this.packageCreationParams.breakBuildIfEmpty)
+                throw new Error(`Package directory ${packageDirectory} is empty`);
+            else this.printEmptyArtifactWarning();
+        }
+    }
+ 
+    abstract isEmptyPackage(projectDirectory: string, packageDirectory: string);
+ 
+    protected printEmptyArtifactWarning() {
+        SFPLogger.printHeaderLine(
+           `WARNING! Empty aritfact encountered`,
+            COLOR_WARNING,
+            LoggerLevel.INFO,
+            this.logger
+        );
+        SFPLogger.log(
+            'Either this folder is empty or the application of .forceignore results in an empty folder',
+            LoggerLevel.INFO,
+            this.logger
+        );
+        SFPLogger.log('Proceeding to create an empty artifact', LoggerLevel.INFO, this.logger);
+        SFPLogger.printHeaderLine('',COLOR_WARNING,LoggerLevel.INFO,this.logger);
+    }
+ 
+    private printHeader() {
+        SFPLogger.log(COLOR_HEADER(`command: ${COLOR_KEY_MESSAGE(`create  package`)}`), LoggerLevel.INFO, this.logger);
+        SFPLogger.log(
+            COLOR_HEADER(`package name: ${COLOR_KEY_MESSAGE(`${this.sfpPackage.packageName}`)}`),
+            LoggerLevel.INFO,
+            this.logger
+        );
+        SFPLogger.log(
+            COLOR_HEADER(`package type: ${COLOR_KEY_MESSAGE(`${this.getTypeOfPackage()}`)}`),
+            LoggerLevel.INFO,
+            this.logger
+        );
+ 
+        SFPLogger.log(
+            COLOR_HEADER(`package directory: ${COLOR_KEY_MESSAGE(`${this.sfpPackage.packageDescriptor.path}`)}`),
+            LoggerLevel.INFO,
+            this.logger
+        );
+ 
+        this.printAdditionalPackageSpecificHeaders();
+ 
+        SFPLogger.printHeaderLine('',COLOR_HEADER,LoggerLevel.INFO,this.logger);
+    }
+ 
+    abstract printAdditionalPackageSpecificHeaders();
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/index.html new file mode 100644 index 000000000..f997c332d --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/packageCreators/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for src/core/package/packageCreators + + + + + + + + + +
+
+

All files src/core/package/packageCreators

+
+ +
+ 14.43% + Statements + 28/194 +
+ + +
+ 0% + Branches + 0/28 +
+ + +
+ 0% + Functions + 0/40 +
+ + +
+ 15.73% + Lines + 28/178 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
CreateDataPackageImpl.ts +
+
21.62%8/370%0/90%0/1126.66%8/30
CreateDiffPackageImpl.ts +
+
14.41%16/1110%0/90%0/2215.53%16/103
CreatePackage.ts +
+
8.69%4/460%0/100%0/78.88%4/45
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/AssignPermissionSetFetcher.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/AssignPermissionSetFetcher.ts.html new file mode 100644 index 000000000..e2b29b295 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/AssignPermissionSetFetcher.ts.html @@ -0,0 +1,154 @@ + + + + + + Code coverage report for src/core/package/propertyFetchers/AssignPermissionSetFetcher.ts + + + + + + + + + +
+
+

All files / src/core/package/propertyFetchers AssignPermissionSetFetcher.ts

+
+ +
+ 75% + Statements + 6/8 +
+ + +
+ 50% + Branches + 2/4 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 75% + Lines + 6/8 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24  +  +  +  +2x +  +  +4x +4x +  +  +  +  +  +4x +4x +  +  +  +  +4x +  +  + 
import { Logger } from '@flxblio/sfp-logger';
+import SfpPackage from '../SfpPackage';
+import PropertyFetcher from './PropertyFetcher';
+ 
+export default class AssignPermissionSetFetcher implements PropertyFetcher {
+    public getsfpProperties(packageContents: SfpPackage, packageLogger?: Logger) {
+        if (packageContents.packageDescriptor.assignPermSetsPreDeployment) {
+            if (packageContents.packageDescriptor.assignPermSetsPreDeployment instanceof Array) {
+                packageContents.assignPermSetsPreDeployment =
+                    packageContents.packageDescriptor.assignPermSetsPreDeployment;
+            } else Ethrow new Error("Property 'assignPermSetsPreDeployment' must be of type array");
+        }
+ 
+        if (packageContents.packageDescriptor.assignPermSetsPostDeployment) {
+            if (packageContents.packageDescriptor.assignPermSetsPostDeployment instanceof Array) {
+                packageContents.assignPermSetsPostDeployment =
+                    packageContents.packageDescriptor.assignPermSetsPostDeployment;
+            } else Ethrow new Error("Property 'assignPermSetsPostDeployment' must be of type array");
+        }
+ 
+        return packageContents;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/DestructiveManifestPathFetcher.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/DestructiveManifestPathFetcher.ts.html new file mode 100644 index 000000000..40f04ae24 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/DestructiveManifestPathFetcher.ts.html @@ -0,0 +1,172 @@ + + + + + + Code coverage report for src/core/package/propertyFetchers/DestructiveManifestPathFetcher.ts + + + + + + + + + +
+
+

All files / src/core/package/propertyFetchers DestructiveManifestPathFetcher.ts

+
+ +
+ 77.77% + Statements + 7/9 +
+ + +
+ 100% + Branches + 2/2 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 77.77% + Lines + 7/9 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +302x +  +  +2x +  +  +2x +  +  +  +  +  +  +  +  +4x +4x +  +  +  +  +4x +  +  +  +  +4x +  +  + 
import * as fs from 'fs-extra';
+import SfpPackage from '../SfpPackage';
+import PropertyFetcher from './PropertyFetcher';
+import xml2json from '../../utils/xml2json';
+import { Logger } from '@flxblio/sfp-logger';
+ 
+export default class DestructiveManifestPathFetcher implements PropertyFetcher {
+    public async getsfpProperties(packageContents: SfpPackage, packageLogger?: Logger) {
+        let destructiveChangesPath: string;
+ 
+        if (packageContents.packageDescriptor === null || packageContents.packageDescriptor === undefined) {
+            throw new Error('Project Config (sfdx-project.json) is null');
+        }
+ 
+        if (packageContents.packageDescriptor['destructiveChangePath']) {
+            destructiveChangesPath = packageContents.packageDescriptor['destructiveChangePath'];
+            packageContents.destructiveChangesPath = destructiveChangesPath;
+        }
+ 
+        try {
+            if (destructiveChangesPath != null) {
+                packageContents.destructiveChanges = await xml2json(fs.readFileSync(destructiveChangesPath, 'utf8'));
+            }
+        } catch (error) {
+            throw new Error('Unable to process destructive Manifest specified in the path or in the project manifest');
+        }
+        return packageContents;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.ts.html new file mode 100644 index 000000000..945a5a918 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.ts.html @@ -0,0 +1,115 @@ + + + + + + Code coverage report for src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.ts + + + + + + + + + +
+
+

All files / src/core/package/propertyFetchers ReconcileProfilePropertyFetcher.ts

+
+ +
+ 100% + Statements + 2/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 2/2 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11  +  +  +2x +  +  +2x +  +  +  + 
import SfpPackage from '../SfpPackage';
+import PropertyFetcher from './PropertyFetcher';
+ 
+export default class ReconcilePropertyFetcher implements PropertyFetcher {
+    getsfpProperties(packageContents: SfpPackage, packageLogger?: any) {
+        if (packageContents.packageDescriptor.hasOwnProperty('reconcileProfiles')) {
+            packageContents.reconcileProfiles = packageContents.packageDescriptor.reconcileProfiles;
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/index.html new file mode 100644 index 000000000..1742aac17 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/propertyFetchers/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for src/core/package/propertyFetchers + + + + + + + + + +
+
+

All files src/core/package/propertyFetchers

+
+ +
+ 78.94% + Statements + 15/19 +
+ + +
+ 66.66% + Branches + 4/6 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 78.94% + Lines + 15/19 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
AssignPermissionSetFetcher.ts +
+
75%6/850%2/4100%1/175%6/8
DestructiveManifestPathFetcher.ts +
+
77.77%7/9100%2/2100%1/177.77%7/9
ReconcileProfilePropertyFetcher.ts +
+
100%2/2100%0/0100%1/1100%2/2
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/validators/PackageEmptyChecker.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/validators/PackageEmptyChecker.ts.html new file mode 100644 index 000000000..681b92b0c --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/validators/PackageEmptyChecker.ts.html @@ -0,0 +1,340 @@ + + + + + + Code coverage report for src/core/package/validators/PackageEmptyChecker.ts + + + + + + + + + +
+
+

All files / src/core/package/validators PackageEmptyChecker.ts

+
+ +
+ 12.5% + Statements + 5/40 +
+ + +
+ 0% + Branches + 0/12 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 13.51% + Lines + 5/37 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +861x +1x +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import path from 'path';
+import { readFileSync, existsSync } from 'fs';
+import FileSystem from '../../utils/FileSystem';
+import ignore from 'ignore';
+ 
+export default class PackageEmptyChecker {
+    public static isToBreakBuildForEmptyDirectory(
+        projectDir: string,
+        sourceDirectory: string,
+        isToBreakBuildIfEmpty: boolean
+    ): {
+        message: string;
+        result: string;
+    } {
+        let directoryToCheck;
+        let status: { message: string; result: string } = {
+            message: '',
+            result: '',
+        };
+ 
+        if (projectDir != null) {
+            directoryToCheck = path.join(projectDir, sourceDirectory);
+        } else directoryToCheck = sourceDirectory;
+ 
+        try {
+            if (!existsSync(directoryToCheck)) {
+                //Folder do not exists, break build
+                if (isToBreakBuildIfEmpty) {
+                    status.message = `Folder not Found , Stopping build as isToBreakBuildIfEmpty is ${isToBreakBuildIfEmpty}`;
+                    status.result = 'break';
+                } else {
+                    status.message = `Folder not Found , Skipping task as isToBreakBuildIfEmpty is ${isToBreakBuildIfEmpty}`;
+                    status.result = 'skip';
+                }
+                return status;
+            } else if (PackageEmptyChecker.isEmptyFolder(projectDir, sourceDirectory)) {
+                if (isToBreakBuildIfEmpty) {
+                    status.message = `Folder is Empty , Stopping build as isToBreakBuildIfEmpty is ${isToBreakBuildIfEmpty}`;
+                    status.result = 'break';
+                } else {
+                    status.message = `Folder is Empty, Skipping task as isToBreakBuildIfEmpty is ${isToBreakBuildIfEmpty}`;
+                    status.result = 'skip';
+                }
+                return status;
+            } else {
+                status.result = 'continue';
+                return status;
+            }
+        } catch (err) {
+            if (err.code === 'ENOENT') {
+                throw new Error(`No such file or directory ${err.path}`); // Re-throw error if .forceignore does not exist
+            } else if (!isToBreakBuildIfEmpty) {
+                status.message = `Something wrong with the path provided ${directoryToCheck}, but skipping, The exception is ${err}`;
+                status.result = 'skip';
+                return status;
+            } else throw err;
+        }
+    }
+ 
+    public static isEmptyFolder(projectDirectory: string, sourceDirectory: string): boolean {
+        let dirToCheck;
+ 
+        if (projectDirectory != null) {
+            dirToCheck = path.join(projectDirectory, sourceDirectory);
+        } else {
+            dirToCheck = sourceDirectory;
+        }
+ 
+        let files: string[] = FileSystem.readdirRecursive(dirToCheck, false, false);
+        // Include source directory in filepaths, as it can be a pattern in forceignore
+        files = files.map((file) => path.join(sourceDirectory, file));
+ 
+        let forceignorePath;
+        if (projectDirectory != null) forceignorePath = path.join(projectDirectory, '.forceignore');
+        else forceignorePath = path.join(process.cwd(), '.forceignore');
+ 
+        // Ignore files that are listed in .forceignore
+        files = ignore()
+            .add(readFileSync(forceignorePath).toString()) // Add ignore patterns from '.forceignore'.
+            .filter(files);
+ 
+        if (files == null || files.length === 0) return true;
+        else return false;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/validators/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/validators/index.html new file mode 100644 index 000000000..6418d85ad --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/validators/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/core/package/validators + + + + + + + + + +
+
+

All files src/core/package/validators

+
+ +
+ 12.5% + Statements + 5/40 +
+ + +
+ 0% + Branches + 0/12 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 13.51% + Lines + 5/37 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
PackageEmptyChecker.ts +
+
12.5%5/400%0/120%0/313.51%5/37
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/version/Package2VersionFetcher.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/version/Package2VersionFetcher.ts.html new file mode 100644 index 000000000..4c3703af1 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/version/Package2VersionFetcher.ts.html @@ -0,0 +1,412 @@ + + + + + + Code coverage report for src/core/package/version/Package2VersionFetcher.ts + + + + + + + + + +
+
+

All files / src/core/package/version Package2VersionFetcher.ts

+
+ +
+ 86.95% + Statements + 40/46 +
+ + +
+ 90% + Branches + 9/10 +
+ + +
+ 80% + Functions + 4/5 +
+ + +
+ 86.84% + Lines + 33/38 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110  +4x +4x +  +  +  +  +4x +8x +  +  +8x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +4x +  +4x +  +  +  +4x +  +4x +4x +4x +4x +  +  +4x +  +4x +4x +  +  +4x +  +  +4x +3x +6x +6x +6x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +  +2x +  +  +1x +1x +1x +1x +  +2x +  +2x +2x +  +2x +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Connection } from '@salesforce/core';
+import QueryHelper from '../../queryHelper/QueryHelper';
+import semver from 'semver';
+ 
+/**
+ * Fetcher for second-generation package version in Dev Hub
+ */
+export default class Package2VersionFetcher {
+    private readonly query: string =
+        'Select SubscriberPackageVersionId, Package2Id, Package2.Name, IsPasswordProtected, IsReleased, MajorVersion, MinorVersion, PatchVersion, BuildNumber, CodeCoverage, HasPassedCodeCoverageCheck, Branch from Package2Version ';
+ 
+    constructor(private conn: Connection) {}
+ 
+    /**
+     * Fetch Package2 versions by Package2 Id
+     * Sorts by semantic version, in descending order
+     * @param package2Id
+     * @param versionNumber
+     * @param isValidatedPackages
+     * @returns
+     */
+    async fetchByPackage2Id(
+        package2Id: string,
+        versionNumber?: string,
+        isValidatedPackages?: boolean
+    ): Promise<Package2Version[]> {
+        let query = this.query;
+ 
+        let whereClause: string = `where Package2Id='${package2Id}'  `;
+ 
+        if (versionNumber) {
+            // TODO: validate version number
+            const versions = versionNumber.split('.');
+ 
+            if (versions[0]) whereClause += `and MajorVersion=${versions[0]} `;
+            if (versions[1]) whereClause += `and MinorVersion=${versions[1]} `;
+            if (versions[2]) whereClause += `and PatchVersion=${versions[2]} `;
+            Iif (versions[3]) whereClause += `and BuildNumber=${versions[3]} `;
+        }
+ 
+        if (isValidatedPackages) whereClause += `and ValidationSkipped = false `;
+ 
+        whereClause += `and IsDeprecated = false `;
+        query += whereClause;
+ 
+ 
+        const records = await QueryHelper.query<Package2Version>(query, this.conn, true);
+ 
+       
+        if (records.length > 1) {
+            return records.sort((a, b) => {
+                const v1 = `${a.MajorVersion}.${a.MinorVersion}.${a.PatchVersion}-${a.BuildNumber}`;
+                const v2 = `${b.MajorVersion}.${b.MinorVersion}.${b.PatchVersion}-${b.BuildNumber}`;
+                return semver.rcompare(v1, v2);
+            });
+        } else return records;
+    }
+ 
+    async fetchBySubscriberPackageVersionId(subscriberPackageVersionId: string): Promise<Package2Version> {
+        let query = this.query;
+ 
+        let whereClause: string = `where SubscriberPackageVersionId='${subscriberPackageVersionId}'`;
+        query += whereClause;
+ 
+        const records = await QueryHelper.query<Package2Version>(query, this.conn, true);
+        return records[0];
+    }
+ 
+    async fetchByPackageBranchAndName(
+        packageBranch: string, 
+        packageName: string, 
+        versionNumber?: string,
+        ): Promise<Package2Version[]> {
+            
+        let query = this.query;
+ 
+        let whereClause: string = `where Branch='${packageBranch}' and Package2.Name ='${packageName}' `;
+        if (versionNumber) {
+            // TODO: validate version number
+            const versions = versionNumber.split('.');
+            if (versions[0]) whereClause += `and MajorVersion=${versions[0]} `;
+            if (versions[1]) whereClause += `and MinorVersion=${versions[1]} `;
+            if (versions[2]) whereClause += `and PatchVersion=${versions[2]} `;
+        }
+        query += whereClause;
+ 
+        let orderByClause: string = `order by CreatedDate desc`;
+        query += orderByClause;
+ 
+        const records = await QueryHelper.query<Package2Version>(query, this.conn, true);
+        return records;
+ 
+    }        
+}
+ 
+export interface Package2Version {
+    SubscriberPackageVersionId: string;
+    Package2Id: string;
+    Package2: { Name: string };
+    IsPasswordProtected: boolean;
+    IsReleased: boolean;
+    MajorVersion: number;
+    MinorVersion: number;
+    PatchVersion: number;
+    BuildNumber: number;
+    CodeCoverage: { apexCodeCoveragePercentage: number };
+    HasPassedCodeCoverageCheck: boolean;
+    Branch: string;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/version/PackageVersionUpdater.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/version/PackageVersionUpdater.ts.html new file mode 100644 index 000000000..6e1bc289c --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/version/PackageVersionUpdater.ts.html @@ -0,0 +1,139 @@ + + + + + + Code coverage report for src/core/package/version/PackageVersionUpdater.ts + + + + + + + + + +
+
+

All files / src/core/package/version PackageVersionUpdater.ts

+
+ +
+ 12.5% + Statements + 1/8 +
+ + +
+ 0% + Branches + 0/2 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 14.28% + Lines + 1/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import SfpPackage from '../SfpPackage';
+ 
+export default class PackageVersionUpdater {
+    public constructor() {}
+ 
+    public substituteBuildNumber(sfpPackage: SfpPackage, buildNumber: string):string {
+        if (!sfpPackage.versionNumber) {
+            throw new Error('The package doesnt have a version attribute, Please check your definition');
+        } else {
+            let segments = sfpPackage.versionNumber.split('.');
+            let numberToBeAppended = parseInt(buildNumber);
+ 
+            if (isNaN(numberToBeAppended)) throw new Error('BuildNumber should be a number');
+            else segments[3] = buildNumber;
+            return `${segments[0]}.${segments[1]}.${segments[2]}.${segments[3]}`;
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/version/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/version/index.html new file mode 100644 index 000000000..212c33df9 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/package/version/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/core/package/version + + + + + + + + + +
+
+

All files src/core/package/version

+
+ +
+ 75.92% + Statements + 41/54 +
+ + +
+ 75% + Branches + 9/12 +
+ + +
+ 57.14% + Functions + 4/7 +
+ + +
+ 75.55% + Lines + 34/45 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
Package2VersionFetcher.ts +
+
86.95%40/4690%9/1080%4/586.84%33/38
PackageVersionUpdater.ts +
+
12.5%1/80%0/20%0/214.28%1/7
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/AssignPermissionSetsImpl.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/AssignPermissionSetsImpl.ts.html new file mode 100644 index 000000000..548091816 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/AssignPermissionSetsImpl.ts.html @@ -0,0 +1,364 @@ + + + + + + Code coverage report for src/core/permsets/AssignPermissionSetsImpl.ts + + + + + + + + + +
+
+

All files / src/core/permsets AssignPermissionSetsImpl.ts

+
+ +
+ 90.9% + Statements + 30/33 +
+ + +
+ 100% + Branches + 2/2 +
+ + +
+ 100% + Functions + 5/5 +
+ + +
+ 90.9% + Lines + 30/33 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94  +1x +1x +1x +1x +1x +  +1x +  +3x +3x +3x +3x +  +  +  +  +  +  +  +  +  +  +  +  +3x +3x +  +  +  +  +3x +  +  +  +3x +  +  +6x +12x +  +  +  +  +  +  +  +  +  +6x +  +  +  +  +  +  +  +  +6x +6x +3x +3x +  +  +  +  +  +  +2x +2x +  +  +  +2x +2x +  +  +3x +  +  +  +4x +  +  +  +  +4x +6x +  +  +4x +  +  + 
import { Connection } from '@salesforce/core';
+import child_process = require('child_process');
+import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import PermissionSetFetcher from './PermissionSetFetcher';
+import { ZERO_BORDER_TABLE } from '../display/TableConstants';
+const Table = require('cli-table');
+ 
+export default class AssignPermissionSetsImpl {
+    constructor(
+        private conn: Connection,
+        private permSets: string[],
+        private project_directory: string,
+        private packageLogger: Logger
+    ) {}
+ 
+    public async exec(): Promise<{
+        successfullAssignments: {
+            username: string;
+            permset: string;
+        }[];
+        failedAssignments: {
+            username: string;
+            permset: string;
+        }[];
+    }> {
+        let permsetListImpl: PermissionSetFetcher = new PermissionSetFetcher(this.conn.getUsername(), this.conn);
+        let assignedPermSets = await permsetListImpl.fetchAllPermsetAssignment();
+ 
+        let failedAssignments: {
+            username: string;
+            permset: string;
+        }[] = [];
+        let successfullAssignments: {
+            username: string;
+            permset: string;
+        }[] = [];
+ 
+        for (let permSet of this.permSets) {
+            let permSetAssignmentMatch = assignedPermSets.find((record) => {
+                return record.PermissionSet.Name === permSet;
+            });
+ 
+            if (permSetAssignmentMatch !== undefined) {
+                // Treat permsets that have already been assigned as successes
+                successfullAssignments.push({ username: this.conn.getUsername(), permset: permSet });
+                continue;
+            }
+ 
+            try {
+                let permsetAssignmentJson: string = child_process.execSync(
+                    `sf org assign permset -n ${permSet} -o ${this.conn.getUsername()} --json`,
+                    {
+                        cwd: this.project_directory,
+                        encoding: 'utf8',
+                        stdio: ['pipe', 'pipe', 'inherit'],
+                    }
+                );
+ 
+                let permsetAssignment = JSON.parse(permsetAssignmentJson);
+                if (permsetAssignment.status === 0)
+                    successfullAssignments.push({ username: this.conn.getUsername(), permset: permSet });
+                else failedAssignments.push({ username: this.conn.getUsername(), permset: permSet });
+            } catch (err) {
+                failedAssignments.push({ username: this.conn.getUsername(), permset: permSet });
+            }
+        }
+ 
+        if (successfullAssignments.length > 0) {
+            SFPLogger.log('Successful PermSet Assignments:', LoggerLevel.INFO, this.packageLogger);
+            this.printPermsetAssignments(successfullAssignments);
+        }
+ 
+        if (failedAssignments.length > 0) {
+            SFPLogger.log('Failed PermSet Assignments', LoggerLevel.INFO, this.packageLogger);
+            this.printPermsetAssignments(failedAssignments);
+        }
+ 
+        return { successfullAssignments, failedAssignments };
+    }
+ 
+    private printPermsetAssignments(assignments: { username: string; permset: string }[]) {
+        let table = new Table({
+            head: ['Username', 'Permission Set Assignment'],
+            chars: ZERO_BORDER_TABLE
+        });
+ 
+        assignments.forEach((assignment) => {
+            table.push([assignment.username, assignment.permset]);
+        });
+ 
+        SFPLogger.log(table.toString(), LoggerLevel.INFO, this.packageLogger);
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/PermissionSetFetcher.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/PermissionSetFetcher.ts.html new file mode 100644 index 000000000..b70f990bf --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/PermissionSetFetcher.ts.html @@ -0,0 +1,130 @@ + + + + + + Code coverage report for src/core/permsets/PermissionSetFetcher.ts + + + + + + + + + +
+
+

All files / src/core/permsets PermissionSetFetcher.ts

+
+ +
+ 100% + Statements + 6/6 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 5/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16  +1x +  +  +  +  +1x +3x +  +  +3x +  +3x +  +  + 
import { Connection } from '@salesforce/core';
+import QueryHelper from '../queryHelper/QueryHelper';
+ 
+/*
+ * Retrieve Permsets for a user from a target org
+ */
+export default class PermissionSetFetcher {
+    constructor(private username: string, private conn: Connection) {}
+ 
+    public async fetchAllPermsetAssignment() {
+        const query = `SELECT Id, PermissionSet.Name, Assignee.Username FROM PermissionSetAssignment WHERE Assignee.Username = '${this.username}'`;
+ 
+        return QueryHelper.query<any>(query, this.conn, false);
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/PermissionSetGroupUpdateAwaiter.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/PermissionSetGroupUpdateAwaiter.ts.html new file mode 100644 index 000000000..017d0ff90 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/PermissionSetGroupUpdateAwaiter.ts.html @@ -0,0 +1,223 @@ + + + + + + Code coverage report for src/core/permsets/PermissionSetGroupUpdateAwaiter.ts + + + + + + + + + +
+
+

All files / src/core/permsets PermissionSetGroupUpdateAwaiter.ts

+
+ +
+ 70.58% + Statements + 12/17 +
+ + +
+ 100% + Branches + 1/1 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 66.66% + Lines + 10/15 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47  +1x +1x +1x +  +1x +  +1x +1x +  +  +1x +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +  +  +  +  + 
import { Connection } from '@salesforce/core';
+import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import QueryHelper from '../queryHelper/QueryHelper';
+import { delay } from '../utils/Delay';
+ 
+const psGroupQuery = `SELECT Id,MasterLabel,Status FROM PermissionSetGroup WHERE Status = 'Updating'`;
+ 
+export default class PermissionSetGroupUpdateAwaiter {
+    constructor(private connection: Connection, private logger: Logger, private intervalBetweenRepeats = 30000) {}
+ 
+    async waitTillAllPermissionSetGroupIsUpdated() {
+        SFPLogger.log(
+            `Checking status of permission sets group..`,
+            LoggerLevel.INFO,
+            this.logger
+        );
+        while (true) {
+            try {
+                let records = await QueryHelper.query(psGroupQuery, this.connection, false);
+                if (records.length > 0) {
+                    SFPLogger.log(
+                        `Pausing deployment as ${records.length} PermissionSetGroups are being updated`,
+                        LoggerLevel.INFO,
+                        this.logger
+                    );
+                    SFPLogger.log(
+                        `Retrying for status in next ${this.intervalBetweenRepeats / 1000} seconds`,
+                        LoggerLevel.INFO,
+                        this.logger
+                    );
+                    await delay(this.intervalBetweenRepeats);
+                } else {
+                    SFPLogger.log(
+                        `Proceeding with deployment, as no PermissionSetGroups are being updated`,
+                        LoggerLevel.INFO,
+                        this.logger
+                    );
+                    break;
+                }
+            } catch (error) {
+                SFPLogger.log(`Unable to fetch permission group status ${error}`, LoggerLevel.TRACE, this.logger);
+                throw error;
+            }
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/index.html new file mode 100644 index 000000000..e48b0995f --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/permsets/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for src/core/permsets + + + + + + + + + +
+
+

All files src/core/permsets

+
+ +
+ 85.71% + Statements + 48/56 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 100% + Functions + 9/9 +
+ + +
+ 84.9% + Lines + 45/53 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
AssignPermissionSetsImpl.ts +
+
90.9%30/33100%2/2100%5/590.9%30/33
PermissionSetFetcher.ts +
+
100%6/6100%0/0100%2/2100%5/5
PermissionSetGroupUpdateAwaiter.ts +
+
70.58%12/17100%1/1100%2/266.66%10/15
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/project/ProjectConfig.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/project/ProjectConfig.ts.html new file mode 100644 index 000000000..1f9e646ef --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/project/ProjectConfig.ts.html @@ -0,0 +1,928 @@ + + + + + + Code coverage report for src/core/project/ProjectConfig.ts + + + + + + + + + +
+
+

All files / src/core/project ProjectConfig.ts

+
+ +
+ 71.59% + Statements + 63/88 +
+ + +
+ 72.72% + Branches + 16/22 +
+ + +
+ 76.92% + Functions + 20/26 +
+ + +
+ 72.5% + Lines + 58/80 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +2825x +  +5x +5x +5x +  +  +  +  +5x +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +1x +1x +1x +  +5x +  +1x +  +  +  +  +  +2x +2x +2x +2x +3x +  +  +  +15x +  +  +  +  +1x +  +2x +  +  +  +  +  +  +  +1x +1x +  +5x +  +1x +  +  +  +  +  +7x +7x +  +  +35x +  +  +7x +  +  +  +  +  +  +  +  +  +  +  +  +  +13x +13x +  +60x +  +13x +  +  +  +  +  +  +  +  +  +  +  +  +4x +  +  +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +13x +  +  +7x +  +6x +5x +  +5x +  +  +  +  +  +  +  +  +  +1x +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +24x +  +23x +  +  +  +  +24x +  +23x +  +  +  +  +  +  +  +  +  +  +1x +  +  +1x +  +1x +1x +  +  +  +1x +1x +  +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +1x +  +  +4x +  +  +  +  +  +  +  +  +  +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
const fs = require('fs-extra');
+import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger';
+import _ from 'lodash';
+import { PackageType } from '../package/SfpPackage';
+let path = require('path');
+ 
+/**
+ * Helper functions for retrieving info from project config
+ */
+export default class ProjectConfig {
+    /**
+     * Returns 0H Id of package from project config
+     * @param projectConfig
+     * @param sfdxPackage
+     */
+    public static getPackageId(projectConfig: any, sfdxPackage: string) {
+        if (projectConfig['packageAliases']?.[sfdxPackage]) {
+            return projectConfig['packageAliases'][sfdxPackage];
+        } else {
+            throw Error('No Package Id found in sfdx-project.json. Please ensure package alias have the package added');
+        }
+    }
+ 
+    /**
+     * Returns package names, as an array of strings
+     * @param projectDirectory
+     */
+    public static getAllPackages(projectDirectory: string): string[] {
+        let projectConfig = ProjectConfig.getSFDXProjectConfig(projectDirectory);
+        let sfdxpackages = [];
+        projectConfig['packageDirectories'].forEach((pkg) => {
+            //Only push packages that have package and versionNumber, ignore everything else
+            if (pkg.package && pkg.versionNumber) sfdxpackages.push(pkg.package);
+        });
+        return sfdxpackages;
+    }
+ 
+    public static getAllExternalPackages(
+        projectConfig: any
+    ): { alias: string; Package2IdOrSubscriberPackageVersionId: string }[] {
+        let externalPackages: { alias: string; Package2IdOrSubscriberPackageVersionId: string }[] = [];
+        let packagesInCurrentDirectory = ProjectConfig.getAllPackageDirectoriesFromConfig(projectConfig);
+        const packageAliases = projectConfig.packageAliases || {};
+        Object.entries(packageAliases).forEach(([key, value]) => {
+            if (
+                !_.find(
+                    packagesInCurrentDirectory,
+                    (elem) => {
+                        return elem.package == key;
+                    },
+                    0
+                )
+            )
+                externalPackages.push({ alias: key, Package2IdOrSubscriberPackageVersionId: value as string });
+        });
+        return externalPackages;
+    }
+ 
+    /**
+     * Returns package names from projectConfig, as an array of strings
+     * @param projectDirectory
+     */
+    public static getAllPackagesFromProjectConfig(projectConfig: any): string[] {
+        let sfdxpackages = [];
+        projectConfig.packageDirectories.forEach((pkg) => {
+            //Only push packages that have package and versionNumber, ignore everything else
+            if (pkg.package && pkg.versionNumber) sfdxpackages.push(pkg.package);
+        });
+        return sfdxpackages;
+    }
+ 
+    public static getAllPackagesAndItsDependencies(
+        projectConfig: any
+    ): Map<string, { package: string; versionNumber?: string }[]> {
+        let pkgWithDependencies = new Map<string, { package: string; versionNumber?: string }[]>();
+        let packages = ProjectConfig.getAllPackageDirectoriesFromConfig(projectConfig);
+        for (let pkg of packages) {
+            if (pkg.dependencies) {
+                pkgWithDependencies.set(pkg.package, pkg.dependencies);
+            }
+        }
+        return pkgWithDependencies;
+    }
+ 
+    public static getAllPackageDirectoriesFromDirectory(projectDirectory?: string): any[] {
+        let projectConfig = ProjectConfig.getSFDXProjectConfig(projectDirectory);
+        let sfdxpackages = [];
+        projectConfig.packageDirectories?.forEach((pkg) => {
+            //Only push packages that have package and versionNumber, ignore everything else
+            Iif (pkg.package && pkg.versionNumber) sfdxpackages.push(pkg);
+        });
+        return sfdxpackages;
+    }
+ 
+    public static getAllPackageDirectoriesFromConfig(projectConfig: any): any[] {
+        let sfdxpackages = [];
+        projectConfig.packageDirectories?.forEach((pkg) => {
+            //Only push packages that have package and versionNumber, ignore everything else
+            if (pkg.package && pkg.versionNumber) sfdxpackages.push(pkg);
+        });
+        return sfdxpackages;
+    }
+ 
+    /**
+     * Returns package manifest as JSON object
+     * @param projectDirectory
+     */
+    public static getSFDXProjectConfig(projectDirectory: string): any {
+        let projectConfigJSON: string;
+ 
+        if (projectDirectory) {
+            projectConfigJSON = path.join(projectDirectory, 'sfdx-project.json');
+        } else {
+            projectConfigJSON = 'sfdx-project.json';
+        }
+ 
+        try {
+            return JSON.parse(fs.readFileSync(projectConfigJSON, 'utf8'));
+        } catch (error) {
+            throw new Error(`sfdx-project.json doesn't exist or not readable at ${projectConfigJSON}`);
+        }
+    }
+ 
+    /**
+     * Returns type of package
+     * @param projectConfig
+     * @param sfdxPackage
+     */
+    public static getPackageType(
+        projectConfig: any,
+        sfdxPackage: string
+    ): PackageType.Unlocked | PackageType.Data | PackageType.Source | PackageType.Diff {
+        let packageDescriptor = ProjectConfig.getPackageDescriptorFromConfig(sfdxPackage, projectConfig);
+ 
+        if (projectConfig['packageAliases']?.[sfdxPackage]) {
+            return PackageType.Unlocked;
+        } else {
+            if (packageDescriptor.type?.toLowerCase() === PackageType.Data) return PackageType.Data;
+            else Iif(packageDescriptor.type?.toLowerCase() === PackageType.Diff) return PackageType.Diff 
+            else
+             return PackageType.Source;
+        }
+    }
+ 
+    /**
+     * Returns package descriptor from package manifest at project directory
+     * @param projectDirectory
+     * @param sfdxPackage
+     */
+    public static getSFDXPackageDescriptor(projectDirectory: string, sfdxPackage: string): any {
+        let projectConfig = ProjectConfig.getSFDXProjectConfig(projectDirectory);
+ 
+        let sfdxPackageDescriptor = ProjectConfig.getPackageDescriptorFromConfig(sfdxPackage, projectConfig);
+ 
+        return sfdxPackageDescriptor;
+    }
+ 
+    /**
+     * Returns package descriptor from project config JSON object
+     * @param sfdxPackage
+     * @param projectConfig
+     */
+    public static getPackageDescriptorFromConfig(sfdxPackage: string, projectConfig: any) {
+        let sfdxPackageDescriptor: any;
+ 
+        if (sfdxPackage) {
+            projectConfig['packageDirectories'].forEach((pkg) => {
+                if (sfdxPackage == pkg['package']) {
+                    sfdxPackageDescriptor = pkg;
+                }
+            });
+        }
+ 
+        if (sfdxPackageDescriptor == null) throw new Error(`Package ${sfdxPackage} does not exist,Please check inputs`);
+ 
+        return sfdxPackageDescriptor;
+    }
+ 
+    /**
+     * Returns descriptor of default package
+     * @param projectDirectory
+     */
+    public static getDefaultSFDXPackageDescriptor(projectDirectory: string): any {
+        let packageDirectory: string;
+        let sfdxPackageDescriptor: any;
+ 
+        let projectConfig = this.getSFDXProjectConfig(projectDirectory);
+ 
+        //Return the default package directory
+        projectConfig['packageDirectories'].forEach((pkg) => {
+            if (pkg['default'] == true) {
+                packageDirectory = pkg['path'];
+                sfdxPackageDescriptor = pkg;
+            }
+        });
+ 
+        Iif (packageDirectory == null) throw new Error('Package or package directory not exist');
+        else return sfdxPackageDescriptor;
+    }
+ 
+    /**
+     * Returns pruned package manifest, containing sfdxPackage only
+     * @param projectDirectory
+     * @param sfdxPackage
+     */
+    public static cleanupMPDFromProjectDirectory(projectDirectory: string, sfdxPackage: string): any {
+        const projectConfig = this.getSFDXProjectConfig(projectDirectory);
+ 
+        return ProjectConfig.cleanupMPDFromProjectConfig(projectConfig, sfdxPackage);
+    }
+ 
+    /**
+     * Returns pruned package manifest, containing sfdxPackage only
+     * @param projectConfig
+     * @param sfdxPackage
+     */
+    public static cleanupMPDFromProjectConfig(projectConfig: any, sfdxPackage: string): any {
+        if (sfdxPackage) {
+            let i = projectConfig['packageDirectories'].length;
+            while (i--) {
+                if (sfdxPackage != projectConfig['packageDirectories'][i]['package']) {
+                    projectConfig['packageDirectories'].splice(i, 1);
+                }
+            }
+        } else {
+            let i = projectConfig['packageDirectories'].length;
+            while (i--) {
+                if (!fs.existsSync(projectConfig['packageDirectories'][i]['path'])) {
+                    projectConfig['packageDirectories'].splice(i, 1);
+                }
+            }
+        }
+        projectConfig['packageDirectories'][0]['default'] = true; //add default = true
+        return projectConfig;
+    }
+ 
+    /**
+     * Returns pruned package manifest, containing sfdxPackages only
+     * @param projectConfig
+     * @param sfdxPackages
+     */
+    public static cleanupPackagesFromProjectConfig(projectConfig: any, sfdxPackages: string[]): any {
+        let revisedPackageDirectory = [];
+        let originalPackageDirectory = projectConfig['packageDirectories'];
+        for (let pkg of originalPackageDirectory) {
+            for (const sfdxPackage of sfdxPackages) {
+                if (pkg.name == sfdxPackage) {
+                    pkg.default = false;
+                    revisedPackageDirectory.push(pkg);
+                }
+            }
+        }
+        projectConfig['packageDirectories'][0]['default'] = true; //add default = true
+        projectConfig.packageDirectories = revisedPackageDirectory;
+        return projectConfig;
+    }
+ 
+    /**
+     * Returns pruned package manifest, containing sfdxPackages only
+     * @param projectConfig
+     * @param sfdxPackages
+     */
+    public static cleanupPackagesFromProjectDirectory(projectDirectory: string, sfdxPackages: string[]): any {
+        const projectConfig = this.getSFDXProjectConfig(projectDirectory);
+        return ProjectConfig.cleanupPackagesFromProjectConfig(projectConfig, sfdxPackages);
+    }
+ 
+   
+ 
+    public static async updateProjectConfigWithDependencies(
+        projectConfig: any,
+        dependencyMap: Map<string, { package: string; versionNumber?: string }[]>
+    ) {
+        let updatedprojectConfig = await _.cloneDeep(projectConfig);
+        updatedprojectConfig.packageDirectories.map((pkg) => {
+            return Object.assign(pkg, { dependencies: dependencyMap.get(pkg.package) });
+        });
+ 
+        return updatedprojectConfig;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/project/UserDefinedExternalDependency.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/project/UserDefinedExternalDependency.ts.html new file mode 100644 index 000000000..dabdc67c8 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/project/UserDefinedExternalDependency.ts.html @@ -0,0 +1,268 @@ + + + + + + Code coverage report for src/core/project/UserDefinedExternalDependency.ts + + + + + + + + + +
+
+

All files / src/core/project UserDefinedExternalDependency.ts

+
+ +
+ 83.33% + Statements + 20/24 +
+ + +
+ 75% + Branches + 3/4 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 83.33% + Lines + 20/24 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +622x +2x +2x +2x +  +  +  +  +  +2x +  +  +  +9x +9x +9x +9x +  +  +  +  +  +  +2x +2x +2x +  +2x +  +2x +  +  +2x +  +  +  +  +  +  +2x +2x +  +  +  +7x +  +7x +  +  +  +  +  +  +  +  +  +  +  +7x +  +  + 
import SFPLogger from '@flxblio/sfp-logger';
+import { Connection, LoggerLevel } from '@salesforce/core';
+import _ from 'lodash';
+import ExternalPackage2DependencyResolver from '../package/dependencies/ExternalPackage2DependencyResolver';
+ 
+/**
+ * Functions to deal with externalDependencyMap supplied by the user
+ * to aid in resolving transitive dependencies
+ */
+export default class UserDefinedExternalDependencyMap {
+   
+ 
+    public  fetchDependencyEntries(projectConfig: any) {
+        if (projectConfig.plugins?.sfp?.externalDependencyMap) {
+            let externalDependencyMap = projectConfig.plugins.sfp.externalDependencyMap;
+            SFPLogger.log(JSON.stringify(externalDependencyMap), LoggerLevel.DEBUG);
+            return externalDependencyMap;
+        }
+        else
+         Ereturn {};
+    }
+ 
+    public async addDependencyEntries(projectConfig: any, connToDevHub: Connection) {
+        let externalDependencies = [];
+        let updatedProjectConfig = await _.cloneDeep(projectConfig);
+        let externalPackageResolver = new ExternalPackage2DependencyResolver(connToDevHub, projectConfig, null);
+ 
+        let externalDependencyMap = this.fetchDependencyEntries(projectConfig);
+ 
+        let externalPackage2s = await externalPackageResolver.resolveExternalPackage2DependenciesToVersions();
+ 
+        for (let externalPackage2 of externalPackage2s) {
+            externalDependencies.push(externalPackage2.name);
+        }
+        for (let dependency of externalDependencies) {
+            if (!Object.keys(externalDependencyMap).includes(dependency)) {
+                externalDependencyMap[dependency] = [{ package: '', versionNumber: '' }];
+            }
+        }
+        updatedProjectConfig.plugins.sfp.externalDependencyMap = externalDependencyMap;
+        return updatedProjectConfig;
+    }
+ 
+    public async cleanupEntries(projectConfig: any) {
+        let updatedProjectConfig = await _.cloneDeep(projectConfig);
+        if (updatedProjectConfig?.plugins?.sfp?.externalDependencyMap) {
+            const externalDependencyMap = updatedProjectConfig.plugins.sfp.externalDependencyMap;
+            for (let externalPackage of Object.keys(externalDependencyMap)) {
+                if (externalDependencyMap[externalPackage][0].package == '') {
+                    delete externalDependencyMap[externalPackage];
+                } else if (
+                    externalDependencyMap[externalPackage][0].package != '' &&
+                    externalDependencyMap[externalPackage][0].versionNumber == ''
+                ) {
+                    delete externalDependencyMap[externalPackage][0].versionNumber;
+                }
+            }
+        }
+        return updatedProjectConfig;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/project/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/project/index.html new file mode 100644 index 000000000..36a0329c5 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/project/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/core/project + + + + + + + + + +
+
+

All files src/core/project

+
+ +
+ 74.1% + Statements + 83/112 +
+ + +
+ 73.07% + Branches + 19/26 +
+ + +
+ 79.31% + Functions + 23/29 +
+ + +
+ 75% + Lines + 78/104 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ProjectConfig.ts +
+
71.59%63/8872.72%16/2276.92%20/2672.5%58/80
UserDefinedExternalDependency.ts +
+
83.33%20/2475%3/4100%3/383.33%20/24
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/queryHelper/ChunkCollection.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/queryHelper/ChunkCollection.ts.html new file mode 100644 index 000000000..92a33a04a --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/queryHelper/ChunkCollection.ts.html @@ -0,0 +1,196 @@ + + + + + + Code coverage report for src/core/queryHelper/ChunkCollection.ts + + + + + + + + + +
+
+

All files / src/core/queryHelper ChunkCollection.ts

+
+ +
+ 100% + Statements + 16/16 +
+ + +
+ 100% + Branches + 2/2 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 16/16 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38  +  +  +  +  +  +  +  +2x +3x +3x +  +3x +3x +  +  +1x +  +  +7x +  +6x +6x +  +1x +  +  +1x +1x +1x +1x +  +  +  +2x +  +2x + 
 
+ 
+/**
+ * Split values in SOQL WHERE clause into chunks to avoid exceeding max. URI length (16,000 chars) or max. WHERE clause length (4000 chars)
+ * @param collection values in SOQL WHERE clause
+ * @param chunkSize default is 4000
+ * @param offset offset to account for keywords, fields, operators and literals in the query. Default is 1000
+ */
+export default function chunkCollection(collection: string[], chunkSize: number = 4000, offset: number = 1000): string[][] {
+  const result: string[][] = [];
+  chunkSize = chunkSize - offset;
+ 
+  let chunk: string[] = [];
+  let numberOfCharsInChunk: number = 0;
+  for (const elem of collection) {
+    if (elem.length + 2 > chunkSize) {
+      throw new Error(`Single value cannot exceed chunk size limit of ${chunkSize}`);
+    }
+ 
+    const commasAndQuotes = 2*(chunk.length+1) + chunk.length;
+    if (numberOfCharsInChunk + elem.length +  commasAndQuotes <= chunkSize) {
+      chunk.push(elem);
+      numberOfCharsInChunk += elem.length;
+    } else {
+      result.push(chunk);
+ 
+      // Create new chunk
+      chunk = [];
+      numberOfCharsInChunk = 0;
+      chunk.push(elem);
+      numberOfCharsInChunk += elem.length;
+    }
+  }
+ 
+  result.push(chunk);
+ 
+  return result;
+}
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/queryHelper/QueryHelper.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/queryHelper/QueryHelper.ts.html new file mode 100644 index 000000000..ba125afc0 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/queryHelper/QueryHelper.ts.html @@ -0,0 +1,139 @@ + + + + + + Code coverage report for src/core/queryHelper/QueryHelper.ts + + + + + + + + + +
+
+

All files / src/core/queryHelper QueryHelper.ts

+
+ +
+ 100% + Statements + 7/7 +
+ + +
+ 100% + Branches + 2/2 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 6/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19  +  +11x +  +11x +  +21x +  +  +27x +20x +  +19x +  +  +  +  +  + 
import { Connection } from '@salesforce/core';
+ 
+const retry = require('async-retry');
+ 
+export default class QueryHelper {
+    static async query<T>(query: string, conn: Connection, isTooling: boolean): Promise<T[]> {
+        return retry(
+            async (bail) => {
+                let records;
+                if (isTooling) records = (await conn.tooling.query(query)).records;
+                else records = (await conn.query(query)).records;
+ 
+                return records;
+            },
+            { retries: 3, minTimeout: 2000 }
+        );
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/queryHelper/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/queryHelper/index.html new file mode 100644 index 000000000..8d953ebe0 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/queryHelper/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/core/queryHelper + + + + + + + + + +
+
+

All files src/core/queryHelper

+
+ +
+ 100% + Statements + 23/23 +
+ + +
+ 100% + Branches + 4/4 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 100% + Lines + 22/22 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ChunkCollection.ts +
+
100%16/16100%2/2100%1/1100%16/16
QueryHelper.ts +
+
100%7/7100%2/2100%2/2100%6/6
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/NativeMetricSender.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/NativeMetricSender.ts.html new file mode 100644 index 000000000..87c6f55b8 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/NativeMetricSender.ts.html @@ -0,0 +1,151 @@ + + + + + + Code coverage report for src/core/stats/NativeMetricSender.ts + + + + + + + + + +
+
+

All files / src/core/stats NativeMetricSender.ts

+
+ +
+ 16.66% + Statements + 1/6 +
+ + +
+ 0% + Branches + 0/2 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 16.66% + Lines + 1/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Logger } from '@flxblio/sfp-logger';
+ 
+export abstract class NativeMetricSender {
+    constructor(protected logger: Logger) {}
+ 
+    abstract initialize(apiHost: string, apiKey: string): void;
+ 
+    abstract sendGaugeMetric(metric: string, value: number, tags: string[] | { [key: string]: string }): void;
+ 
+    abstract sendCountMetric(metric: string, tags: string[] | { [key: string]: string }): void;
+ 
+    protected transformTagsToStringArray(tags: { [key: string]: string } | string[]): string[] {
+        if (tags != null && !Array.isArray(tags)) {
+            let transformedTagArray: string[] = [];
+            for (const [key, value] of Object.entries(tags)) {
+                transformedTagArray.push(`${key}:${value}`);
+            }
+            return transformedTagArray;
+        }
+        return tags as string[];
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/SFPStatsSender.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/SFPStatsSender.ts.html new file mode 100644 index 000000000..5f8563238 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/SFPStatsSender.ts.html @@ -0,0 +1,427 @@ + + + + + + Code coverage report for src/core/stats/SFPStatsSender.ts + + + + + + + + + +
+
+

All files / src/core/stats SFPStatsSender.ts

+
+ +
+ 18.42% + Statements + 7/38 +
+ + +
+ 0% + Branches + 0/11 +
+ + +
+ 0% + Functions + 0/7 +
+ + +
+ 20% + Lines + 7/35 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +1151x +1x +1x +  +1x +  +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import StatsDClient, { ClientOptions, StatsD } from 'hot-shots';
+import * as fs from 'fs-extra';
+import { EOL } from 'os';
+import { NativeMetricSender } from './NativeMetricSender';
+import { DataDogMetricsSender } from './nativeMetricSenderImpl/DataDogMetricSender';
+import { Logger } from '@flxblio/sfp-logger';
+import { NewRelicMetricSender } from './nativeMetricSenderImpl/NewRelicMetricSender';
+import { SplunkMetricSender } from './nativeMetricSenderImpl/SplunkMetricSender';
+ 
+export default class SFPStatsSender {
+    private static client: StatsD;
+    private static metricsLogger;
+    private static nativeMetricsSender: NativeMetricSender;
+ 
+    static initialize(port: string, host: string, protocol: string) {
+        let options: ClientOptions = {
+            host: host,
+            port: port == null ? 8125 : Number(port),
+            protocol: protocol == 'tcp' ? 'tcp' : 'udp',
+            prefix: 'sfp.',
+        };
+        SFPStatsSender.client = new StatsDClient(options);
+    }
+ 
+    static initializeNativeMetrics(type: string, apiHost: string, apiKey: string, logger?: Logger) {
+        switch (type) {
+            case 'DataDog':
+                this.nativeMetricsSender = new DataDogMetricsSender(logger);
+                this.nativeMetricsSender.initialize(apiHost, apiKey);
+                break;
+ 
+            case 'NewRelic':
+                this.nativeMetricsSender = new NewRelicMetricSender(logger);
+                this.nativeMetricsSender.initialize(apiHost, apiKey);
+                break;
+                
+            case 'Splunk':
+                this.nativeMetricsSender = new SplunkMetricSender(logger);
+                this.nativeMetricsSender.initialize(apiHost, apiKey);
+                break;
+ 
+            default:
+                throw new Error('Invalid Metric Type');
+        }
+    }
+ 
+    static initializeLogBasedMetrics() {
+        try {
+            fs.mkdirpSync('.sfp/logs');
+            SFPStatsSender.metricsLogger = `.sfp/logs/metrics.log`;
+        } catch (error) {
+            console.log('Unable to initiate Log based metrics', error);
+        }
+    }
+ 
+    static logElapsedTime(metric: string, elapsedMilliSeconds: number, tags?: { [key: string]: string } | string[]) {
+        Iif (SFPStatsSender.client != null) SFPStatsSender.client.timing(metric, elapsedMilliSeconds, tags);
+ 
+        //Native Datadog integration
+        if (SFPStatsSender.nativeMetricsSender != null) {
+            SFPStatsSender.nativeMetricsSender.sendGaugeMetric(metric, elapsedMilliSeconds, tags);
+        }
+ 
+        let metrics = {
+            metric: `sfp.${metric}`,
+            type: `timers`,
+            value: elapsedMilliSeconds,
+            timestamp: Date.now(),
+            tags: tags,
+        };
+        SFPStatsSender.logMetrics(metrics, SFPStatsSender.metricsLogger);
+    }
+ 
+    static logGauge(metric: string, value: number, tags?: { [key: string]: string } | string[]) {
+        Iif (SFPStatsSender.client != null) SFPStatsSender.client.gauge(metric, value, tags);
+ 
+        //Native Metrics integration
+        if (SFPStatsSender.nativeMetricsSender != null) {
+            SFPStatsSender.nativeMetricsSender.sendGaugeMetric(metric, value, tags);
+        }
+ 
+        let metrics = {
+            metric: `sfp.${metric}`,
+            type: `guage`,
+            value: value,
+            timestamp: Date.now(),
+            tags: tags,
+        };
+        SFPStatsSender.logMetrics(metrics, SFPStatsSender.metricsLogger);
+    }
+ 
+    static logCount(metric: string, tags?: { [key: string]: string } | string[]) {
+        Iif (SFPStatsSender.client != null) SFPStatsSender.client.increment(metric, tags);
+ 
+        //Native Metrics integration
+        if (SFPStatsSender.nativeMetricsSender != null) {
+            SFPStatsSender.nativeMetricsSender.sendCountMetric(metric, tags);
+        }
+ 
+        let metrics = {
+            metric: `sfp.${metric}`,
+            type: `count`,
+            timestamp: Date.now(),
+            tags: tags,
+        };
+        SFPStatsSender.logMetrics(metrics, SFPStatsSender.metricsLogger);
+    }
+ 
+    static logMetrics(key: any, logger?: any) {
+        if (logger) {
+            fs.appendFileSync(logger, `${JSON.stringify(key)}${EOL}`, 'utf8');
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/index.html new file mode 100644 index 000000000..afbdc9391 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/core/stats + + + + + + + + + +
+
+

All files src/core/stats

+
+ +
+ 18.18% + Statements + 8/44 +
+ + +
+ 0% + Branches + 0/13 +
+ + +
+ 0% + Functions + 0/9 +
+ + +
+ 19.51% + Lines + 8/41 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
NativeMetricSender.ts +
+
16.66%1/60%0/20%0/216.66%1/6
SFPStatsSender.ts +
+
18.42%7/380%0/110%0/720%7/35
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/DataDogMetricSender.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/DataDogMetricSender.ts.html new file mode 100644 index 000000000..a847ecda3 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/DataDogMetricSender.ts.html @@ -0,0 +1,241 @@ + + + + + + Code coverage report for src/core/stats/nativeMetricSenderImpl/DataDogMetricSender.ts + + + + + + + + + +
+
+

All files / src/core/stats/nativeMetricSenderImpl DataDogMetricSender.ts

+
+ +
+ 26.66% + Statements + 4/15 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 26.66% + Lines + 4/15 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +531x +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { BufferedMetricsLogger } from 'datadog-metrics';
+import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import { NativeMetricSender } from '../NativeMetricSender';
+ 
+export class DataDogMetricsSender extends NativeMetricSender {
+    constructor(logger: Logger) {
+        super(logger);
+    }
+ 
+    private nativeDataDogMetricsLogger: BufferedMetricsLogger;
+ 
+    public initialize(apiHost: string, apiKey: string) {
+        try {
+            this.nativeDataDogMetricsLogger = new BufferedMetricsLogger({
+                apiHost: apiHost,
+                apiKey: apiKey,
+                prefix: 'sfp.',
+                flushIntervalSeconds: 0,
+            });
+        } catch (error) {
+            SFPLogger.log('Unable to intialize native datadog logger' + error, LoggerLevel.TRACE, this.logger);
+        }
+    }
+ 
+    public sendGaugeMetric(metric: string, value: number, tags: string[] | { [key: string]: string }) {
+        try {
+            let transformedTags = this.transformTagsToStringArray(tags);
+            this.nativeDataDogMetricsLogger.gauge(metric, value, transformedTags);
+            this.nativeDataDogMetricsLogger.flush();
+        } catch (error) {
+            SFPLogger.log(
+                `Unable to transmit metrics for metric ${metric} due to` + error,
+                LoggerLevel.TRACE,
+                this.logger
+            );
+        }
+    }
+ 
+    public sendCountMetric(metric: string, tags: string[] | { [key: string]: string }) {
+        try {
+            let transformedTags = this.transformTagsToStringArray(tags);
+            this.nativeDataDogMetricsLogger.increment(metric, 1, transformedTags);
+            this.nativeDataDogMetricsLogger.flush();
+        } catch (error) {
+            SFPLogger.log(
+                `Unable to transmit metrics for metric ${metric} due to` + error,
+                LoggerLevel.TRACE,
+                this.logger
+            );
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/NewRelicMetricSender.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/NewRelicMetricSender.ts.html new file mode 100644 index 000000000..1387b7910 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/NewRelicMetricSender.ts.html @@ -0,0 +1,283 @@ + + + + + + Code coverage report for src/core/stats/nativeMetricSenderImpl/NewRelicMetricSender.ts + + + + + + + + + +
+
+

All files / src/core/stats/nativeMetricSenderImpl NewRelicMetricSender.ts

+
+ +
+ 15.38% + Statements + 4/26 +
+ + +
+ 0% + Branches + 0/2 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 15.38% + Lines + 4/26 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +671x +  +1x +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { NativeMetricSender } from '../NativeMetricSender';
+import { telemetry } from '@newrelic/telemetry-sdk';
+import {
+    CountMetric,
+    GaugeMetric,
+    MetricBatch,
+    MetricClient,
+} from '@newrelic/telemetry-sdk/dist/src/telemetry/metrics';
+import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+ 
+export class NewRelicMetricSender extends NativeMetricSender {
+    constructor(logger: Logger) {
+        super(logger);
+    }
+ 
+    private nrMetricClient: telemetry.metrics.MetricClient;
+ 
+    //Ignore API Host, as newrelic sdk doesnt need it
+    public initialize(apiHost: string, apiKey: string) {
+        try {
+            this.nrMetricClient = new MetricClient({ apiKey: apiKey });
+        } catch (error) {
+            SFPLogger.log(`Unable to intialize native newrelic metric logger ${error}`, LoggerLevel.WARN, this.logger);
+        }
+    }
+ 
+    public sendGaugeMetric(metric: string, value: number, tags: string[] | { [key: string]: string }) {
+        metric = `sfp.${metric}`;
+        const guageMetric = new GaugeMetric(metric, value);
+        guageMetric.attributes = tags as { [key: string]: string };
+        const batch = new MetricBatch({}, Date.now(), 1);
+        batch.addMetric(guageMetric);
+        this.nrMetricClient.send(batch, (error, response, body) => {
+            if (response) {
+                SFPLogger.log(`Transmitted metric ${metric} ${response.statusCode}`, LoggerLevel.TRACE, this.logger);
+            }
+            Iif (error)
+                SFPLogger.log(
+                    `Unable to transmit metrics for metric ${metric} due to` + error,
+                    LoggerLevel.WARN,
+                    this.logger
+                );
+        });
+    }
+ 
+    public sendCountMetric(metric: string, tags: string[] | { [key: string]: string }) {
+        metric = `sfp.${metric}`;
+        const countMetric = new CountMetric(metric);
+        countMetric.record(1);
+        countMetric.attributes = tags as { [key: string]: string };
+        const batch = new MetricBatch({}, Date.now(), 1);
+        batch.addMetric(countMetric);
+ 
+        this.nrMetricClient.send(batch, (error, response, body) => {
+            if (response) {
+                SFPLogger.log(`Transmitted metric ${metric} ${response.statusCode}`, LoggerLevel.TRACE, this.logger);
+            }
+            Iif (error)
+                SFPLogger.log(
+                    `Unable to transmit metrics for metric ${metric} due to` + error,
+                    LoggerLevel.WARN,
+                    this.logger
+                );
+        });
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/SplunkMetricSender.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/SplunkMetricSender.ts.html new file mode 100644 index 000000000..c6f36719b --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/SplunkMetricSender.ts.html @@ -0,0 +1,232 @@ + + + + + + Code coverage report for src/core/stats/nativeMetricSenderImpl/SplunkMetricSender.ts + + + + + + + + + +
+
+

All files / src/core/stats/nativeMetricSenderImpl SplunkMetricSender.ts

+
+ +
+ 25% + Statements + 4/16 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/8 +
+ + +
+ 25% + Lines + 4/16 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +501x +1x +1x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import { NativeMetricSender } from '../NativeMetricSender';
+import axios ,{AxiosInstance} from 'axios';
+ 
+ 
+ 
+export class SplunkMetricSender extends NativeMetricSender {
+    constructor(logger: Logger) {
+        super(logger);
+    }
+ 
+    private instance: AxiosInstance;
+ 
+    public initialize(apiHost: string, apiKey: string) {
+          this.instance = axios.create({
+            baseURL: apiHost,
+            headers: {'Authorization': apiKey, 'Content-Type': 'application/json'}
+          });
+    }
+ 
+    public sendGaugeMetric(metric: string, value: number, tags: string[] | { [key: string]: string }) {
+        metric = `sfp.${metric}`;
+        const payload = {source: "sfp",sourcetype: "metrics",event: {metric: metric, type: 'guage', value: value,tags: tags as { [key: string]: string },timestamp: Date.now()}};
+        this.instance.post('', JSON.stringify(payload))
+        .then((response) => {SFPLogger.log(`Transmitted metric ${metric} ${response.status}`, LoggerLevel.TRACE, this.logger)})
+        .catch((error) => {
+            SFPLogger.log(
+                `Unable to transmit metrics for metric ${metric} due to` + error,
+                LoggerLevel.WARN,
+                this.logger
+            );
+        });
+    }
+ 
+    public sendCountMetric(metric: string, tags: string[] | { [key: string]: string }) {
+        metric = `sfp.${metric}`;
+        const payload = {source: "sfp",sourcetype: "metrics",event: {metric: metric, type: 'count', tags: tags as { [key: string]: string },timestamp: Date.now()}};
+        this.instance.post('', JSON.stringify(payload))
+        .then((response) => {SFPLogger.log(`Transmitted metric ${metric} ${response.status}`, LoggerLevel.TRACE, this.logger)})
+        .catch((error) => {
+            SFPLogger.log(
+                `Unable to transmit metrics for metric ${metric} due to` + error,
+                LoggerLevel.WARN,
+                this.logger
+            );
+        });
+    }
+}
+ 
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/index.html new file mode 100644 index 000000000..810a21c05 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/stats/nativeMetricSenderImpl/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for src/core/stats/nativeMetricSenderImpl + + + + + + + + + +
+
+

All files src/core/stats/nativeMetricSenderImpl

+
+ +
+ 21.05% + Statements + 12/57 +
+ + +
+ 0% + Branches + 0/2 +
+ + +
+ 0% + Functions + 0/18 +
+ + +
+ 21.05% + Lines + 12/57 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
DataDogMetricSender.ts +
+
26.66%4/15100%0/00%0/426.66%4/15
NewRelicMetricSender.ts +
+
15.38%4/260%0/20%0/615.38%4/26
SplunkMetricSender.ts +
+
25%4/16100%0/00%0/825%4/16
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/AliasList.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/AliasList.ts.html new file mode 100644 index 000000000..0f431c7ea --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/AliasList.ts.html @@ -0,0 +1,133 @@ + + + + + + Code coverage report for src/core/utils/AliasList.ts + + + + + + + + + +
+
+

All files / src/core/utils AliasList.ts

+
+ +
+ 33.33% + Statements + 3/9 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 33.33% + Lines + 3/9 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +174x +  +  +4x +  +  +  +  +  +4x +  +  +  +  +  +  + 
import { StateAggregator } from '@salesforce/core';
+ 
+ 
+export async function convertAliasToUsername(alias: string) {
+    const stateAggregator = await StateAggregator.getInstance();
+    await stateAggregator.orgs.readAll();
+    return await stateAggregator.aliases.resolveUsername(alias)
+}
+ 
+export async function convertUsernameToAlias(username: string) {
+   
+    const stateAggregator = await StateAggregator.getInstance();
+    await stateAggregator.orgs.readAll();
+    return await stateAggregator.aliases.resolveAlias(username)
+  
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/ChunkArray.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/ChunkArray.ts.html new file mode 100644 index 000000000..280a18edc --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/ChunkArray.ts.html @@ -0,0 +1,118 @@ + + + + + + Code coverage report for src/core/utils/ChunkArray.ts + + + + + + + + + +
+
+

All files / src/core/utils ChunkArray.ts

+
+ +
+ 100% + Statements + 6/6 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 6/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +121x +2x +2x +2x +  +  +9x +  +  +2x +  + 
export function chunkArray(perChunk: number, inputArray: any[]): Array<any> {
+    let chunks = [],
+        i = 0,
+        n = inputArray.length;
+ 
+    while (i < n) {
+        chunks.push(inputArray.slice(i, (i += perChunk)));
+    }
+ 
+    return chunks;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/Delay.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/Delay.ts.html new file mode 100644 index 000000000..6cd22982a --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/Delay.ts.html @@ -0,0 +1,94 @@ + + + + + + Code coverage report for src/core/utils/Delay.ts + + + + + + + + + +
+
+

All files / src/core/utils Delay.ts

+
+ +
+ 33.33% + Statements + 1/3 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 50% + Lines + 1/2 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +41x +  +  + 
export async function delay(ms: number = 0) {
+    return new Promise((resolve) => setTimeout(resolve, ms));
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/FileSystem.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/FileSystem.ts.html new file mode 100644 index 000000000..1b96d2715 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/FileSystem.ts.html @@ -0,0 +1,241 @@ + + + + + + Code coverage report for src/core/utils/FileSystem.ts + + + + + + + + + +
+
+

All files / src/core/utils FileSystem.ts

+
+ +
+ 94.11% + Statements + 16/17 +
+ + +
+ 0% + Branches + 0/3 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 100% + Lines + 16/16 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +532x +2x +  +2x +  +  +  +  +  +  +  +  +  +  +  +4x +  +4x +  +4x +24x +  +24x +  +  +22x +  +22x +  +  +  +  +  +5x +  +5x +  +  +20x +  +  +12x +  +12x +  +  +  +  +  +4x +  +  + 
import fs = require('fs-extra');
+import path = require('path');
+ 
+export default class FileSystem {
+    /**
+     * List nested files within a directory
+     * @param directory
+     * @param includeDirectories
+     * @returns
+     */
+    static readdirRecursive(
+        searchDirectory: string,
+        includeDirectories: boolean = false,
+        isAbsolute: boolean = false
+    ): string[] {
+        const result: string[] = [];
+ 
+        Iif (!fs.lstatSync(searchDirectory).isDirectory()) throw new Error(`${searchDirectory} is not a directory`);
+ 
+        (function readdirRecursiveHandler(directory: string): void {
+            const files: string[] = fs.readdirSync(directory);
+ 
+            files.forEach((file) => {
+                let filepath: string;
+                if (isAbsolute) {
+                    filepath = path.resolve(directory, file);
+                } else {
+                    filepath = path.join(directory, file);
+                }
+ 
+                if (fs.lstatSync(filepath).isDirectory()) {
+                    if (includeDirectories) {
+                        if (isAbsolute) {
+                            result.push(path.resolve(filepath));
+                        } else {
+                            result.push(path.relative(searchDirectory, filepath));
+                        }
+                    }
+                    readdirRecursiveHandler(filepath);
+                } else {
+                    if (isAbsolute) {
+                        result.push(path.resolve(filepath));
+                    } else {
+                        result.push(path.relative(searchDirectory, filepath));
+                    }
+                }
+            });
+        })(searchDirectory);
+ 
+        return result;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/Fileutils.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/Fileutils.ts.html new file mode 100644 index 000000000..f31d63f49 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/Fileutils.ts.html @@ -0,0 +1,697 @@ + + + + + + Code coverage report for src/core/utils/Fileutils.ts + + + + + + + + + +
+
+

All files / src/core/utils Fileutils.ts

+
+ +
+ 9.09% + Statements + 7/77 +
+ + +
+ 0% + Branches + 0/12 +
+ + +
+ 0% + Functions + 0/15 +
+ + +
+ 9.09% + Lines + 7/77 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +2051x +1x +1x +1x +  +1x +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
const fs = require('fs');
+const path = require('path');
+const _ = require('lodash');
+const os = require('os');
+ 
+const SEP = /\/|\\/;
+ 
+export const PLUGIN_CACHE_FOLDER = 'sfpowerkit';
+ 
+export default class FileUtils {
+    /**
+     * Delete file or directories recursively from the project
+     * @param deletedComponents Files or directories to delete
+     */
+    public static deleteComponents(deletedComponents: string[]) {
+        deletedComponents.forEach((component) => {
+            if (fs.existsSync(component)) {
+                if (fs.lstatSync(component).isDirectory()) {
+                    FileUtils.deleteFolderRecursive(component);
+                } else {
+                    fs.unlinkSync(component);
+                }
+            }
+        });
+    }
+    /**
+     * Load all files from the given folder with the given extension
+     * @param folder the folder from which files wille be loaded
+     * @param extension File extension to load.
+     */
+    public static getAllFilesSync(folder: string, extension: string = '.xml'): string[] {
+        let result: string[] = [];
+        let pathExists = fs.existsSync(folder);
+        let folderName = path.basename(folder);
+        if (!pathExists) {
+            console.log('Folder does not exist:', folderName);
+            return result;
+        }
+        let content: string[] = fs.readdirSync(folder);
+        content.forEach((file) => {
+            let curFile = path.join(folder, file);
+            let stats = fs.statSync(curFile);
+            if (stats.isFile()) {
+                if (extension.indexOf(path.extname(curFile)) != -1 || extension === '') {
+                    result.push(curFile);
+                }
+            } else if (stats.isDirectory()) {
+                let files: string[] = this.getAllFilesSync(curFile, extension);
+                result = _.concat(result, files);
+            }
+        });
+        return result;
+    }
+ 
+    public static getGlobalCacheDir() {
+        let homedir = os.homedir();
+        let configDir = homedir + path.sep + PLUGIN_CACHE_FOLDER;
+        if (!fs.existsSync(configDir)) {
+            console.log('Config folder does not exists, Creating Folder');
+            fs.mkdirSync(configDir);
+        }
+ 
+        return configDir;
+    }
+ 
+    /**
+     * Get the cache path for the given cache file name
+     * @param fileName
+     */
+    public static getGlobalCachePath(fileName: string) {
+        let homedir = os.homedir();
+        let configDir = homedir + path.sep + PLUGIN_CACHE_FOLDER;
+        if (!fs.existsSync(configDir)) {
+            console.log('Config folder does not exists, Creating Folder');
+            fs.mkdirSync(configDir);
+        }
+        return configDir + path.sep + fileName;
+    }
+ 
+    /**
+     * Create a folder path recursively
+     * @param targetDir
+     * @param param1
+     */
+    public static mkDirByPathSync(targetDir: string, { isRelativeToScript = false } = {}) {
+        const sep = path.sep;
+        const initDir = path.isAbsolute(targetDir) ? sep : '';
+        const baseDir = isRelativeToScript ? __dirname : '.';
+ 
+        targetDir.split(sep).reduce((parentDir, childDir) => {
+            const curDir = path.resolve(baseDir, parentDir, childDir);
+            try {
+                fs.mkdirSync(curDir);
+            } catch (err) {
+                if (err.code !== 'EEXIST' && err.code !== 'EPERM' && err.code !== 'EISDIR') {
+                    throw err;
+                }
+            }
+            return curDir;
+        }, initDir);
+    }
+    /**
+     * Get the file name withoud extension
+     * @param filePath file path
+     * @param extension extension
+     */
+    public static getFileNameWithoutExtension(filePath: string, extension?: string): string {
+        let fileParts = filePath.split(SEP);
+        let fileName = fileParts[fileParts.length - 1];
+        if (extension) {
+            fileName = fileName.substr(0, fileName.lastIndexOf(extension));
+        } else {
+            fileName = fileName.substr(0, fileName.indexOf('.'));
+        }
+        return fileName;
+    }
+ 
+    /**
+     * Copu folder recursively
+     * @param src source folder to copy
+     * @param dest destination folder
+     */
+    public static copyRecursiveSync(src, dest) {
+        let exists = fs.existsSync(src);
+        if (exists) {
+            let stats = fs.statSync(src);
+            let isDirectory = stats.isDirectory();
+            if (isDirectory) {
+                exists = fs.existsSync(dest);
+                if (!exists) {
+                    fs.mkdirSync(dest);
+                }
+                fs.readdirSync(src).forEach(function (childItemName) {
+                    FileUtils.copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName));
+                });
+            } else {
+                fs.copyFileSync(src, dest);
+            }
+        }
+    }
+    /**
+     * Get path to a given folder base on the parent folder
+     * @param src  Parent folder
+     * @param foldername folder to build the path to
+     */
+    public static getFolderPath(src, foldername) {
+        let exists = fs.existsSync(src);
+        let toReturn = '';
+        if (exists) {
+            let stats = fs.statSync(src);
+            let isDirectory = stats.isDirectory();
+            if (isDirectory) {
+                let childs = fs.readdirSync(src);
+                for (let i = 0; i < childs.length; i++) {
+                    let childItemName = childs[i];
+                    if (childItemName === foldername) {
+                        toReturn = path.join(src, childItemName);
+                    } else {
+                        let childStat = fs.statSync(path.join(src, childItemName));
+                        if (childStat.isDirectory()) {
+                            toReturn = FileUtils.getFolderPath(path.join(src, childItemName), foldername);
+                        }
+                    }
+                    if (toReturn !== '') {
+                        break;
+                    }
+                }
+            }
+        }
+        return toReturn;
+    }
+ 
+    /**
+     * Delete a folder and its content recursively
+     * @param folder folder to delete
+     */
+    public static deleteFolderRecursive(folder) {
+        if (fs.existsSync(folder)) {
+            fs.readdirSync(folder).forEach(function (file, index) {
+                let curPath = path.join(folder, file);
+                if (fs.lstatSync(curPath).isDirectory()) {
+                    // recurse
+                    //console.log("Delete recursively");
+                    FileUtils.deleteFolderRecursive(curPath);
+                } else {
+                    // delete file
+                    //console.log("Delete file "+ curPath);
+                    fs.unlinkSync(curPath);
+                }
+            });
+            //console.log("delete folder "+ folder);
+            fs.rmdirSync(folder);
+        }
+    }
+    public static makefolderid(length): string {
+        var result = '';
+        var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+        var charactersLength = characters.length;
+        for (var i = 0; i < length; i++) {
+            result += characters.charAt(Math.floor(Math.random() * charactersLength));
+        }
+        return result;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/ObjectCRUDHelper.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/ObjectCRUDHelper.ts.html new file mode 100644 index 000000000..2589703d5 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/ObjectCRUDHelper.ts.html @@ -0,0 +1,199 @@ + + + + + + Code coverage report for src/core/utils/ObjectCRUDHelper.ts + + + + + + + + + +
+
+

All files / src/core/utils ObjectCRUDHelper.ts

+
+ +
+ 42.1% + Statements + 8/19 +
+ + +
+ 25% + Branches + 2/8 +
+ + +
+ 50% + Functions + 2/4 +
+ + +
+ 43.75% + Lines + 7/16 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39  +  +4x +  +4x +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +3x +  +3x +3x +1x +  +  +  +  +  + 
import { Connection } from '@salesforce/core';
+import { Record, SaveResult } from 'jsforce';
+import { isArray } from 'lodash';
+ 
+const retry = require('async-retry');
+ 
+export default class ObjectCRUDHelper {
+    static async updateRecord(conn: Connection, sObject: string, record: Record): Promise<string> {
+        return retry(
+            async (bail) => {
+                let result = await conn.update(sObject, record);
+                if (isArray(result)) {
+                    let isAllRecordsSucceeded = true;
+                    for (const individualResult of result as SaveResult[]) {
+                        if (!individualResult.success) {
+                            isAllRecordsSucceeded = false;
+                        }
+                    }
+                    if (isAllRecordsSucceeded) return 'All records updated';
+                    else throw new Error('Some records have been failed to update');
+                } else if ((result as SaveResult).success) return (result as SaveResult).id;
+                else bail();
+            },
+            { retries: 3, minTimeout: 2000 }
+        );
+    }
+ 
+    static async createRecord(conn: Connection, sObject: string, record: Record): Promise<string> {
+        return retry(
+            async (bail) => {
+                let result = await conn.create(sObject, record);
+                if (result.success) return result.id;
+                else bail();
+            },
+            { retries: 3, minTimeout: 2000 }
+        );
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/VersionNumberConverter.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/VersionNumberConverter.ts.html new file mode 100644 index 000000000..710eb3af9 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/VersionNumberConverter.ts.html @@ -0,0 +1,184 @@ + + + + + + Code coverage report for src/core/utils/VersionNumberConverter.ts + + + + + + + + + +
+
+

All files / src/core/utils VersionNumberConverter.ts

+
+ +
+ 90% + Statements + 9/10 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 90% + Lines + 9/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34  +  +  +  +  +2x +103x +  +103x +  +103x +  +  +103x +  +  +  +  +  +  +  +  +103x +103x +  +309x +  +  +103x +  +  +  +  + 
/**
+ * Converts build-number dot delimeter to hyphen
+ * If dot delimeter does not exist, returns input
+ * @param version
+ */
+export default function convertBuildNumDotDelimToHyphen(version: string) {
+    let convertedVersion = version;
+ 
+    let indexOfBuildNumDelimiter = getIndexOfBuildNumDelimeter(version);
+    if (version[indexOfBuildNumDelimiter] === '.') {
+        convertedVersion =
+            version.substring(0, indexOfBuildNumDelimiter) + '-' + version.substring(indexOfBuildNumDelimiter + 1);
+    }
+    return convertedVersion;
+}
+ 
+/**
+ * Get the index of the build-number delimeter
+ * Returns null if unable to find index of delimeter
+ * @param version
+ */
+function getIndexOfBuildNumDelimeter(version: string) {
+    let numOfDelimetersTraversed: number = 0;
+    for (let i = 0; i < version.length; i++) {
+        if (!Number.isInteger(parseInt(version[i], 10))) {
+            numOfDelimetersTraversed++;
+        }
+        if (numOfDelimetersTraversed === 3) {
+            return i;
+        }
+    }
+    return null;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/extractDomainFromUrl.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/extractDomainFromUrl.ts.html new file mode 100644 index 000000000..2d484f6b0 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/extractDomainFromUrl.ts.html @@ -0,0 +1,115 @@ + + + + + + Code coverage report for src/core/utils/extractDomainFromUrl.ts + + + + + + + + + +
+
+

All files / src/core/utils extractDomainFromUrl.ts

+
+ +
+ 100% + Statements + 5/5 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 4/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11  +  +  +  +  +1x +7x +4x +4x +  + 
/**
+ * Extracts domain name from full URL string
+ * @param url
+ * @returns
+ */
+export default function extractDomainFromUrl(url: string): string {
+    if (!url) return url;
+    const matches = url.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
+    return matches && matches[1];
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/index.html new file mode 100644 index 000000000..5f2b9c407 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/index.html @@ -0,0 +1,236 @@ + + + + + + Code coverage report for src/core/utils + + + + + + + + + +
+
+

All files src/core/utils

+
+ +
+ 39.86% + Statements + 61/153 +
+ + +
+ 20.68% + Branches + 6/29 +
+ + +
+ 36.36% + Functions + 12/33 +
+ + +
+ 40.41% + Lines + 59/146 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
AliasList.ts +
+
33.33%3/9100%0/00%0/233.33%3/9
ChunkArray.ts +
+
100%6/6100%0/0100%1/1100%6/6
Delay.ts +
+
33.33%1/30%0/10%0/250%1/2
FileSystem.ts +
+
94.11%16/170%0/3100%3/3100%16/16
Fileutils.ts +
+
9.09%7/770%0/120%0/159.09%7/77
ObjectCRUDHelper.ts +
+
42.1%8/1925%2/850%2/443.75%7/16
VersionNumberConverter.ts +
+
90%9/10100%0/0100%2/290%9/10
extractDomainFromUrl.ts +
+
100%5/5100%3/3100%1/1100%4/4
xml2json.ts +
+
85.71%6/750%1/2100%3/3100%6/6
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/xml2json.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/xml2json.ts.html new file mode 100644 index 000000000..fed7111ec --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/core/utils/xml2json.ts.html @@ -0,0 +1,115 @@ + + + + + + Code coverage report for src/core/utils/xml2json.ts + + + + + + + + + +
+
+

All files / src/core/utils xml2json.ts

+
+ +
+ 85.71% + Statements + 6/7 +
+ + +
+ 50% + Branches + 1/2 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 100% + Lines + 6/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +114x +  +4x +12x +12x +12x +12x +  +  +  + 
const xmlParser = require('xml2js').Parser({ explicitArray: false });
+ 
+export default function xml2json(xml) {
+    return new Promise<any>((resolve, reject) => {
+        xmlParser.parseString(xml, function (err, json) {
+            Iif (err) reject(err);
+            else resolve(json);
+        });
+    });
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/CommitUpdater.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/CommitUpdater.ts.html new file mode 100644 index 000000000..856f28547 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/CommitUpdater.ts.html @@ -0,0 +1,256 @@ + + + + + + Code coverage report for src/impl/changelog/CommitUpdater.ts + + + + + + + + + +
+
+

All files / src/impl/changelog CommitUpdater.ts

+
+ +
+ 88.23% + Statements + 15/17 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 88.23% + Lines + 15/17 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58  +  +  +  +1x +  +4x +4x +4x +4x +  +  +  +  +  +  +  +  +  +10x +  +  +  +  +10x +  +  +  +6x +76x +  +  +2x +  +  +  +  +2x +2x +2x +  +  +  +  +4x +  +  +  +  +  +  +  +4x +  +  +  +  + 
import { Release } from './ReleaseChangelog';
+import { Changelog as PackageChangelog } from '../../core/changelog/interfaces/GenericChangelogInterfaces';
+import ReadPackageChangelog from './ReadPackageChangelog';
+ 
+export default class CommitUpdater {
+    constructor(
+        private latestRelease: Release,
+        private artifactsToLatestCommitId: { [p: string]: string },
+        private packagesToChangelogFilePaths: { [p: string]: string },
+        private readPackageChangelog: ReadPackageChangelog
+    ) {}
+ 
+    /**
+     * Generate commits in latest release, for each artifact
+     * Also sets new latestCommitId for artifacts
+     * @returns
+     */
+    update(): void {
+        for (let artifact of this.latestRelease['artifacts']) {
+            let packageChangelog: PackageChangelog = this.readPackageChangelog(
+                this.packagesToChangelogFilePaths[artifact.name]
+            );
+ 
+            // Set new latestCommitId
+            artifact['latestCommitId'] = packageChangelog['commits'][0]['commitId'];
+ 
+            let indexOfLatestCommitId;
+            if (this.artifactsToLatestCommitId?.[artifact.name]) {
+                indexOfLatestCommitId = packageChangelog['commits'].findIndex(
+                    (commit) => commit['commitId'] === this.artifactsToLatestCommitId[artifact.name]
+                );
+                if (indexOfLatestCommitId === -1) {
+                    console.log(
+                        `Cannot find commit Id ${this.artifactsToLatestCommitId[artifact.name]} in ${
+                            artifact.name
+                        } changelog`
+                    );
+                    console.log('Assuming that there are no changes...');
+                    artifact['commits'] = [];
+                    continue;
+                }
+            }
+ 
+            if (indexOfLatestCommitId > 0) {
+                artifact['commits'] = packageChangelog['commits'].slice(0, indexOfLatestCommitId);
+            } else if (indexOfLatestCommitId === 0) {
+                // Artifact verison has not changed
+                artifact['commits'] = [];
+                // Skip to next artifact
+                continue;
+            } else if (indexOfLatestCommitId === undefined) {
+                // Artifact was not in previous release
+                artifact['commits'] = packageChangelog['commits'];
+            }
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/OrgsUpdater.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/OrgsUpdater.ts.html new file mode 100644 index 000000000..0608096da --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/OrgsUpdater.ts.html @@ -0,0 +1,466 @@ + + + + + + Code coverage report for src/impl/changelog/OrgsUpdater.ts + + + + + + + + + +
+
+

All files / src/impl/changelog OrgsUpdater.ts

+
+ +
+ 82.35% + Statements + 28/34 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 6/6 +
+ + +
+ 81.25% + Lines + 26/32 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128  +1x +  +1x +  +  +  +  +4x +4x +4x +4x +  +4x +  +  +2x +  +  +  +  +  +  +2x +  +  +1x +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +  +  +  +  +  +  +  +  +3x +  +  +2x +2x +  +  +  +  +  +  +  +  +1x +  +  +  +1x +1x +  +  +1x +  +  +  +  +  +1x +1x +  +  +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +6x +6x +  +  +  +  +  +  + 
import { ReleaseChangelog, Release, ReleaseId } from './ReleaseChangelog';
+import lodash = require('lodash');
+ 
+export default class OrgsUpdater {
+    private latestReleaseId: ReleaseId;
+    private idOfReleaseWithMatchingHashId: ReleaseId;
+ 
+    constructor(
+        private releaseChangelog: ReleaseChangelog,
+        private latestRelease: Release,
+        private org: string,
+        private releaseWithMatchingHashId: Release
+    ) {
+        this.latestReleaseId = this.convertReleaseToId(this.latestRelease);
+ 
+        if (this.releaseWithMatchingHashId) {
+            this.idOfReleaseWithMatchingHashId = this.convertReleaseToId(this.releaseWithMatchingHashId);
+        }
+    }
+ 
+    update(): void {
+        if (!this.idOfReleaseWithMatchingHashId) {
+            if (this.releaseChangelog.orgs) {
+                let org = this.releaseChangelog.orgs.find((org) => org.name === this.org);
+ 
+                if (org) {
+                    org.releases.push(this.latestReleaseId);
+                    org.latestRelease = org.releases[org.releases.length - 1];
+                    org.retryCount = 0;
+                } else {
+                    this.releaseChangelog.orgs.push({
+                        name: this.org,
+                        releases: [this.latestReleaseId],
+                        latestRelease: this.latestReleaseId,
+                        retryCount: 0,
+                    });
+                }
+            } else {
+                // for backwards-compatibility with pre-existing changelogs
+                this.releaseChangelog.orgs = [
+                    {
+                        name: this.org,
+                        releases: [this.latestReleaseId],
+                        latestRelease: this.latestReleaseId,
+                        retryCount: 0,
+                    },
+                ];
+            }
+            console.log(
+                `Updating ${this.org} org with`,
+                this.latestRelease.names[this.latestRelease.names.length - 1] +
+                    '-' +
+                    this.latestRelease.buildNumber +
+                    `(0)`
+            );
+        } else {
+            // Update orgs
+            let org = this.releaseChangelog.orgs.find((org) => org.name === this.org);
+ 
+            if (org) {
+                let indexOfReleaseToOrg = org.releases.findIndex(
+                    (orgRelease) => orgRelease.hashId === this.idOfReleaseWithMatchingHashId.hashId
+                );
+                if (org.latestRelease.hashId !== this.idOfReleaseWithMatchingHashId.hashId) {
+                    if (indexOfReleaseToOrg >= 0) {
+                        // Update release names in releases to org
+                        org.releases[indexOfReleaseToOrg] = this.idOfReleaseWithMatchingHashId;
+                        org.releases[indexOfReleaseToOrg].date = new Date().toUTCString();
+                    } else {
+                        // Add releaseId in releases to org
+                        org.releases.push(this.idOfReleaseWithMatchingHashId);
+                    }
+ 
+                    // Update latest release
+                    org.latestRelease = this.idOfReleaseWithMatchingHashId;
+                    org.retryCount = 0;
+                } else {
+                    if (lodash.isEqual(org.releases[indexOfReleaseToOrg], this.idOfReleaseWithMatchingHashId)) {
+                        org.retryCount++;
+                    } else {
+                        org.retryCount = 0;
+                    }
+ 
+                    // Update releases names in releases to org & latestRelease
+                    org.releases[indexOfReleaseToOrg] = this.idOfReleaseWithMatchingHashId;
+                    org.latestRelease = this.idOfReleaseWithMatchingHashId;
+                }
+ 
+                console.log(
+                    `Updating ${this.org} org with`,
+                    org.latestRelease.names[org.latestRelease.names.length - 1] +
+                        '-' +
+                        org.latestRelease.buildNumber +
+                        `(${org.retryCount})`
+                );
+            } else {
+                // new org
+                this.releaseChangelog.orgs.push({
+                    name: this.org,
+                    releases: [this.idOfReleaseWithMatchingHashId],
+                    latestRelease: this.idOfReleaseWithMatchingHashId,
+                    retryCount: 0,
+                });
+                console.log(
+                    `Updating ${this.org} org with`,
+                    `${this.idOfReleaseWithMatchingHashId.names[this.idOfReleaseWithMatchingHashId.names.length - 1]}-${
+                        this.idOfReleaseWithMatchingHashId.buildNumber
+                    }(0)`
+                );
+            }
+        }
+    }
+ 
+    /**
+     * Convert Release to Release Id
+     * @param release
+     * @returns
+     */
+    private convertReleaseToId(release: Release): ReleaseId {
+        let releaseNames = [...release.names]; // Shallow copy
+        return {
+            names: releaseNames,
+            buildNumber: release.buildNumber,
+            hashId: release.hashId,
+        };
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/WorkItemUpdater.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/WorkItemUpdater.ts.html new file mode 100644 index 000000000..5a2260a30 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/WorkItemUpdater.ts.html @@ -0,0 +1,208 @@ + + + + + + Code coverage report for src/impl/changelog/WorkItemUpdater.ts + + + + + + + + + +
+
+

All files / src/impl/changelog WorkItemUpdater.ts

+
+ +
+ 100% + Statements + 13/13 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 11/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +421x +  +  +  +1x +1x +  +  +  +  +  +  +  +2x +2x +  +  +  +24x +24x +  +  +  +3x +3x +  +1x +  +  +  +  +  +  +  +  +  +  +3x +  +  +  + 
import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger';
+import { Release } from './ReleaseChangelog';
+ 
+ 
+export default class WorkItemUpdater {
+    constructor(private latestRelease: Release, private workItemFilters: string[],private logger?:Logger) {}
+ 
+    /**
+     * Generate work items in latest release
+     */
+    update(): void {
+        for (const workItemFilter of this.workItemFilters) {
+     
+        let workItemFilterRegex: RegExp = RegExp(workItemFilter, 'gi');
+        SFPLogger.log(`Matching...${workItemFilterRegex}`,LoggerLevel.INFO,this.logger);
+ 
+        for (let artifact of this.latestRelease['artifacts']) {
+            for (let commit of artifact['commits']) {
+                let commitMessage: String = commit['message'] + '\n' + commit['body'];
+                let workItems: RegExpMatchArray = commitMessage.match(workItemFilterRegex);
+                if (workItems) {
+                    for (let item of workItems) {
+                        if (this.latestRelease['workItems'][item] == null) {
+                            this.latestRelease['workItems'][item] = new Set<string>();
+                            this.latestRelease['workItems'][item].add(commit['commitId'].slice(0, 8));
+                        } else {
+                            this.latestRelease['workItems'][item].add(commit['commitId'].slice(0, 8));
+                        }
+                    }
+                }
+            }
+        }
+       }
+ 
+        // Convert each work item Set to Array
+        // Enables JSON stringification of work item
+        for (let key in this.latestRelease['workItems']) {
+            this.latestRelease.workItems[key] = Array.from(this.latestRelease.workItems[key]);
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/index.html new file mode 100644 index 000000000..1d197cc97 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/changelog/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for src/impl/changelog + + + + + + + + + +
+
+

All files src/impl/changelog

+
+ +
+ 87.5% + Statements + 56/64 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 11/11 +
+ + +
+ 86.66% + Lines + 52/60 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
CommitUpdater.ts +
+
88.23%15/17100%0/0100%3/388.23%15/17
OrgsUpdater.ts +
+
82.35%28/34100%0/0100%6/681.25%26/32
WorkItemUpdater.ts +
+
100%13/13100%0/0100%2/2100%11/11
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/dependency/ShrinkImpl.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/dependency/ShrinkImpl.ts.html new file mode 100644 index 000000000..07347a6ee --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/dependency/ShrinkImpl.ts.html @@ -0,0 +1,328 @@ + + + + + + Code coverage report for src/impl/dependency/ShrinkImpl.ts + + + + + + + + + +
+
+

All files / src/impl/dependency ShrinkImpl.ts

+
+ +
+ 100% + Statements + 27/27 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 5/5 +
+ + +
+ 100% + Lines + 26/26 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +821x +1x +1x +1x +  +1x +1x +  +1x +  +  +  +  +2x +  +2x +  +2x +  +2x +  +  +  +2x +2x +  +2x +  +2x +  +  +  +2x +  +  +12x +  +  +  +  +12x +12x +  +  +22x +  +  +  +  +  +  +  +36x +  +24x +  +  +  +  +18x +  +  +  +  +  +  +  +12x +  +  +  +  +  +12x +  +10x +  +  +  +  +  + 
import TransitiveDependencyResolver from '../../core/package/dependencies/TransitiveDependencyResolver';
+import { COLOR_HEADER, COLOR_KEY_MESSAGE, COLOR_SUCCESS, COLOR_ERROR } from '@flxblio/sfp-logger';
+import SFPLogger, { LoggerLevel, Logger } from '@flxblio/sfp-logger';
+import _ from 'lodash';
+import { Connection } from '@salesforce/core';
+const Table = require('cli-table');
+import UserDefinedExternalDependency from '../../core/project/UserDefinedExternalDependency';
+ 
+export default class ShrinkImpl {
+    private dependencyMap;
+    private updatedprojectConfig: any;
+ 
+ 
+    constructor(private connToDevHub:Connection, private logger?: Logger) {}
+    public async shrinkDependencies(sfdxProjectConfig: any): Promise<any> {
+        SFPLogger.log('Shrinking Project Dependencies...', LoggerLevel.INFO, this.logger);
+ 
+        this.updatedprojectConfig = _.cloneDeep(sfdxProjectConfig);
+ 
+        const transitiveDependencyResolver = new TransitiveDependencyResolver(
+            sfdxProjectConfig
+        );
+ 
+        this.dependencyMap = await transitiveDependencyResolver.resolveTransitiveDependencies();
+        await this.resolveAndShrinkDependencies(this.dependencyMap);
+ 
+        this.updatedprojectConfig = new UserDefinedExternalDependency().addDependencyEntries(  this.updatedprojectConfig, this.connToDevHub);
+ 
+        return this.updatedprojectConfig;
+    }
+ 
+    private async resolveAndShrinkDependencies(dependencyMap: any) {
+        let pkgs = [...dependencyMap.keys()];
+ 
+        for (let pkg of pkgs) {
+            SFPLogger.log(
+                COLOR_HEADER(`cleaning up dependencies for package:`) + COLOR_KEY_MESSAGE(pkg),
+                LoggerLevel.TRACE,
+                this.logger
+            );
+            let dependenencies = dependencyMap.get(pkg);
+            let updatedDependencies = _.cloneDeep(dependenencies);
+            for (let dependency of dependencyMap.get(pkg)) {
+                if (dependencyMap.get(dependency.package)) {
+                    SFPLogger.log(
+                        `Shrinking ${dependencyMap.get(dependency.package).length} dependencies from package ${
+                            dependency.package
+                        }`,
+                        LoggerLevel.TRACE,
+                        this.logger
+                    );
+                    for (let temp of dependencyMap.get(dependency.package)) {
+                        for (let i = 0; i < updatedDependencies.length; i++) {
+                            if (updatedDependencies[i].package == temp.package) {
+                                updatedDependencies.splice(i, 1);
+                            }
+                        }
+                    }
+                } else {
+                    SFPLogger.log(
+                        `no dependency found for ${dependency.package} in the map`,
+                        LoggerLevel.TRACE,
+                        this.logger
+                    );
+                }
+            }
+            //Update project config
+            await this.updateProjectConfig(pkg, updatedDependencies);
+        }
+ 
+    }
+ 
+    private async updateProjectConfig(packageName: string, fixedDependencies: any) {
+        this.updatedprojectConfig.packageDirectories.map((pkg) => {
+            if (pkg.package == packageName) {
+                return Object.assign(pkg, { dependencies: fixedDependencies });
+            }
+        });
+    }
+}
+ 
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/dependency/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/dependency/index.html new file mode 100644 index 000000000..810c9907b --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/dependency/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/impl/dependency + + + + + + + + + +
+
+

All files src/impl/dependency

+
+ +
+ 100% + Statements + 27/27 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 5/5 +
+ + +
+ 100% + Lines + 26/26 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ShrinkImpl.ts +
+
100%27/27100%0/0100%5/5100%26/26
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/parallelBuilder/BuildCollections.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/parallelBuilder/BuildCollections.ts.html new file mode 100644 index 000000000..2d2dee9d6 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/parallelBuilder/BuildCollections.ts.html @@ -0,0 +1,280 @@ + + + + + + Code coverage report for src/impl/parallelBuilder/BuildCollections.ts + + + + + + + + + +
+
+

All files / src/impl/parallelBuilder BuildCollections.ts

+
+ +
+ 89.47% + Statements + 17/19 +
+ + +
+ 71.42% + Branches + 5/7 +
+ + +
+ 71.42% + Functions + 5/7 +
+ + +
+ 88.23% + Lines + 15/17 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +661x +1x +  +  +  +  +1x +  +  +  +  +4x +  +  +  +2x +  +  +  +  +  +  +  +4x +  +4x +  +  +7x +6x +  +6x +  +  +  +9x +2x +  +1x +  +  +  +  +5x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import UndirectedGraph from './UndirectedGraph';
+import ProjectConfig from '../../core/project/ProjectConfig';
+ 
+/**
+ * Class for the manipulation of package build collections
+ */
+export default class BuildCollections {
+    // Disconnected undirected graph is used to represent collections and their packages
+    private _graph: UndirectedGraph;
+ 
+    constructor(projectDirectory: string) {
+        this.createGraphOfBuildCollections(projectDirectory);
+    }
+ 
+    get graph(): UndirectedGraph {
+        return this._graph;
+    }
+ 
+    /**
+     * Constructs graph representation of collections
+     * @param projectDirectory
+     */
+    private createGraphOfBuildCollections(projectDirectory: string) {
+        this._graph = new UndirectedGraph();
+ 
+        let projectConfig = ProjectConfig.getSFDXProjectConfig(projectDirectory);
+        for (let pkg of projectConfig.packageDirectories) {
+            if (pkg.buildCollection) {
+                if (pkg.buildCollection instanceof Array) {
+                    if (!this._graph.adjacencyList[pkg.package]) this._graph.addVertex(pkg.package);
+ 
+                    pkg.buildCollection.forEach((packageInCollection) => {
+                        // Create vertex for package in collection if it doesn't exist yet
+                        if (!this._graph.adjacencyList[packageInCollection]) {
+                            // Verify that the package in collection is a valid package, before adding to adj. list
+                            if (projectConfig.packageDirectories.find((elem) => elem.package === packageInCollection)) {
+                                this._graph.addVertex(packageInCollection);
+                            } else
+                                throw new Error(
+                                    `Package '${packageInCollection}' in collection ${pkg.buildCollection} is not a valid package`
+                                );
+                        }
+ 
+                        this._graph.addEdge(pkg.package, packageInCollection);
+                    });
+                } else
+                    throw new Error(
+                        `Property 'buildCollection' must be of type Array. Received ${pkg.buildCollection}`
+                    );
+            }
+        }
+    }
+ 
+    /**
+     * Returns list of packages contained in the same collection as the package
+     * @param pkg
+     */
+    listPackagesInCollection(pkg: string): string[] {
+        return this._graph.dfs(pkg);
+    }
+ 
+    isPackageInACollection(pkg: string): boolean {
+        return this._graph.adjacencyList[pkg] ? true : false;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/parallelBuilder/UndirectedGraph.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/parallelBuilder/UndirectedGraph.ts.html new file mode 100644 index 000000000..4b19b9f40 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/parallelBuilder/UndirectedGraph.ts.html @@ -0,0 +1,232 @@ + + + + + + Code coverage report for src/impl/parallelBuilder/UndirectedGraph.ts + + + + + + + + + +
+
+

All files / src/impl/parallelBuilder UndirectedGraph.ts

+
+ +
+ 96.55% + Statements + 28/29 +
+ + +
+ 88.88% + Branches + 8/9 +
+ + +
+ 100% + Functions + 7/7 +
+ + +
+ 100% + Lines + 21/21 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +502x +  +  +  +12x +  +  +  +17x +  +  +  +22x +1x +  +  +  +17x +16x +14x +  +13x +13x +  +  +  +  +  +  +  +4x +4x +4x +  +4x +19x +19x +18x +18x +18x +  +15x +  +  +  +  +3x +  +  + 
export default class UndirectedGraph {
+    private _adjacencyList: { [p: string]: string[] };
+ 
+    constructor() {
+        this._adjacencyList = {};
+    }
+ 
+    get adjacencyList() {
+        return this._adjacencyList;
+    }
+ 
+    addVertex(name: string) {
+        if (!this._adjacencyList[name]) this._adjacencyList[name] = [];
+        else throw new Error(`Vertex with name '${name}' already exists`);
+    }
+ 
+    addEdge(vertexA: string, vertexB: string): void {
+        if (vertexA === vertexB) throw new Error('Cannot add an edge to a single vertex');
+        if (!this._adjacencyList[vertexA]) throw new Error(`Vertex with name ${vertexA} does not exist`);
+        if (!this._adjacencyList[vertexB]) throw new Error(`Vertex with name ${vertexB} does not exist`);
+ 
+        if (!this._adjacencyList[vertexA].includes(vertexB)) this._adjacencyList[vertexA].push(vertexB);
+        if (!this._adjacencyList[vertexB].includes(vertexA)) this._adjacencyList[vertexB].push(vertexA);
+    }
+ 
+    /**
+     * Returns vertices in graph, using depth-first search from the starting vertex
+     * @param start
+     */
+    dfs(start: string): string[] {
+        const vertices: string[] = [];
+        const visited: { [p: string]: boolean } = {};
+        const adjacencyList = this._adjacencyList;
+ 
+        (function dfsHandler(vertex) {
+            Iif (!vertex) return null;
+            if (!adjacencyList[vertex]) throw new Error(`Vertex '${vertex}' does not exist`);
+            visited[vertex] = true;
+            vertices.push(vertex);
+            adjacencyList[vertex].forEach((neighbor) => {
+                if (!visited[neighbor]) {
+                    return dfsHandler(neighbor);
+                }
+            });
+        })(start);
+ 
+        return vertices;
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/parallelBuilder/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/parallelBuilder/index.html new file mode 100644 index 000000000..e5f2f31fe --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/parallelBuilder/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/impl/parallelBuilder + + + + + + + + + +
+
+

All files src/impl/parallelBuilder

+
+ +
+ 93.75% + Statements + 45/48 +
+ + +
+ 81.25% + Branches + 13/16 +
+ + +
+ 85.71% + Functions + 12/14 +
+ + +
+ 94.73% + Lines + 36/38 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
BuildCollections.ts +
+
89.47%17/1971.42%5/771.42%5/788.23%15/17
UndirectedGraph.ts +
+
96.55%28/2988.88%8/9100%7/7100%21/21
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/release/ReleaseDefinition.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/release/ReleaseDefinition.ts.html new file mode 100644 index 000000000..a9eaa72c8 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/release/ReleaseDefinition.ts.html @@ -0,0 +1,322 @@ + + + + + + Code coverage report for src/impl/release/ReleaseDefinition.ts + + + + + + + + + +
+
+

All files / src/impl/release ReleaseDefinition.ts

+
+ +
+ 70% + Statements + 21/30 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 66.66% + Functions + 4/6 +
+ + +
+ 70% + Lines + 21/30 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80  +1x +1x +1x +1x +1x +  +1x +1x +  +1x +  +  +  +  +5x +5x +  +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +5x +  +  +  +  +  +5x +  +  +  +  +  +  +  +  +  +  +5x +  +  +  +  +5x +5x +  +  +  +4x +  +  +4x +6x +  +  +  +  +  +  +4x +  +  +  + 
import ReleaseDefinitionSchema from './ReleaseDefinitionSchema';
+import Ajv from 'ajv';
+const yaml = require('js-yaml');
+import lodash = require('lodash');
+import get18DigitSalesforceId from '../../utils/Get18DigitSalesforceId';
+import Git from '../../core/git/Git';
+import { ConsoleLogger } from '@flxblio/sfp-logger';
+const fs = require('fs-extra');
+const path = require('path');
+ 
+export default class ReleaseDefinition {
+    get releaseDefinition() {
+        // Return clone of releaseDefinition for immutability
+        return lodash.cloneDeep(this._releaseDefinitionSchema);
+    }
+    private constructor(private _releaseDefinitionSchema: ReleaseDefinitionSchema) {
+        this.validateReleaseDefinition(this._releaseDefinitionSchema);
+ 
+        // Workaround for jsonschema not supporting validation based on dependency value
+        if (this._releaseDefinitionSchema.baselineOrg && !this._releaseDefinitionSchema.skipIfAlreadyInstalled)
+            throw new Error("Release option 'skipIfAlreadyInstalled' must be true for 'baselineOrg'");
+ 
+        if (this._releaseDefinitionSchema.packageDependencies) {
+            this.convertPackageDependenciesIdTo18Digits(this._releaseDefinitionSchema.packageDependencies);
+        }
+    }
+ 
+    public static async loadReleaseDefinition(pathToReleaseDefinition: string) {
+        //Check whether path contains gitRef
+        let releaseDefinitionSchema: ReleaseDefinitionSchema;
+        try {
+            if (pathToReleaseDefinition.includes(':')) {
+                let git = await Git.initiateRepo();
+                await git.fetch();
+                let releaseFile = await git.show([pathToReleaseDefinition]);
+                releaseDefinitionSchema = yaml.load(releaseFile);
+            } else {
+                releaseDefinitionSchema = yaml.load(fs.readFileSync(pathToReleaseDefinition, 'UTF8'));
+            }
+        } catch (error) {
+            throw new Error(`Unable to read the release definition file due to ${JSON.stringify(error)}`);
+        }
+ 
+        let releaseDefinition = new ReleaseDefinition(releaseDefinitionSchema);
+        return releaseDefinition;
+    }
+ 
+    private convertPackageDependenciesIdTo18Digits(packageDependencies: { [p: string]: string }) {
+        for (let pkg in packageDependencies) {
+            packageDependencies[pkg] = get18DigitSalesforceId(packageDependencies[pkg]);
+        }
+    }
+ 
+    private validateReleaseDefinition(releaseDefinition: ReleaseDefinitionSchema): void {
+        let schema = fs.readJSONSync(
+            path.join(__dirname, '..', '..', '..', 'resources', 'schemas', 'releasedefinition.schema.json'),
+            { encoding: 'UTF-8' }
+        );
+ 
+        let validator = new Ajv({ allErrors: true }).compile(schema);
+        let validationResult = validator(releaseDefinition);
+ 
+        if (!validationResult) {
+            let errorMsg: string =
+                `Release definition does not meet schema requirements, ` +
+                `found ${validator.errors.length} validation errors:\n`;
+ 
+            validator.errors.forEach((error, errorNum) => {
+                errorMsg += `\n${errorNum + 1}: ${error.instancePath}: ${error.message} ${JSON.stringify(
+                    error.params,
+                    null,
+                    4
+                )}`;
+            });
+ 
+            throw new Error(errorMsg);
+        }
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/release/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/release/index.html new file mode 100644 index 000000000..f3bef4d7c --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/impl/release/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/impl/release + + + + + + + + + +
+
+

All files src/impl/release

+
+ +
+ 70% + Statements + 21/30 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 66.66% + Functions + 4/6 +
+ + +
+ 70% + Lines + 21/30 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ReleaseDefinition.ts +
+
70%21/30100%3/366.66%4/670%21/30
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/index.html new file mode 100644 index 000000000..06f4f0138 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src + + + + + + + + + +
+
+

All files src

+
+ +
+ 100% + Statements + 26/26 +
+ + +
+ 100% + Branches + 3/3 +
+ + +
+ 100% + Functions + 7/7 +
+ + +
+ 100% + Lines + 26/26 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ProjectValidation.ts +
+
100%26/26100%3/3100%7/7100%26/26
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/utils/Get18DigitSalesforceId.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/utils/Get18DigitSalesforceId.ts.html new file mode 100644 index 000000000..65aadb36d --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/utils/Get18DigitSalesforceId.ts.html @@ -0,0 +1,142 @@ + + + + + + Code coverage report for src/utils/Get18DigitSalesforceId.ts + + + + + + + + + +
+
+

All files / src/utils Get18DigitSalesforceId.ts

+
+ +
+ 7.69% + Statements + 1/13 +
+ + +
+ 0% + Branches + 0/7 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 8.33% + Lines + 1/12 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +201x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
export default function get18DigitSalesforceId(recordId: string) {
+    if (recordId && recordId.length === 18) {
+        return recordId;
+    } else if (recordId && recordId.length === 15) {
+        let addon = '';
+        for (let block = 0; block < 3; block++) {
+            let loop = 0;
+            for (let position = 0; position < 5; position++) {
+                let current = recordId.charAt(block * 5 + position);
+                Iif (current >= 'A' && current <= 'Z') loop += 1 << position;
+            }
+            addon += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345'.charAt(loop);
+        }
+        let convertedId = recordId + addon;
+        return convertedId;
+    } else {
+        throw new Error(`Invalid Salesforce Id ${recordId}`);
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/src/utils/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/src/utils/index.html new file mode 100644 index 000000000..c1385a4ad --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/src/utils/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/utils + + + + + + + + + +
+
+

All files src/utils

+
+ +
+ 7.69% + Statements + 1/13 +
+ + +
+ 0% + Branches + 0/7 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 8.33% + Lines + 1/12 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
Get18DigitSalesforceId.ts +
+
7.69%1/130%0/70%0/18.33%1/12
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/utils/Get18DigitSalesforceId.ts.html b/packages/sfpowerscripts-cli/coverage/lcov-report/utils/Get18DigitSalesforceId.ts.html new file mode 100644 index 000000000..510da83a6 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/utils/Get18DigitSalesforceId.ts.html @@ -0,0 +1,142 @@ + + + + + + Code coverage report for utils/Get18DigitSalesforceId.ts + + + + + + + + + +
+
+

All files / utils Get18DigitSalesforceId.ts

+
+ +
+ 7.69% + Statements + 1/13 +
+ + +
+ 0% + Branches + 0/7 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 8.33% + Lines + 1/12 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +201x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
export default function get18DigitSalesforceId(recordId: string) {
+    if (recordId && recordId.length === 18) {
+        return recordId;
+    } else if (recordId && recordId.length === 15) {
+        let addon = '';
+        for (let block = 0; block < 3; block++) {
+            let loop = 0;
+            for (let position = 0; position < 5; position++) {
+                let current = recordId.charAt(block * 5 + position);
+                Iif (current >= 'A' && current <= 'Z') loop += 1 << position;
+            }
+            addon += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345'.charAt(loop);
+        }
+        let convertedId = recordId + addon;
+        return convertedId;
+    } else {
+        throw new Error(`Invalid Salesforce Id ${recordId}`);
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov-report/utils/index.html b/packages/sfpowerscripts-cli/coverage/lcov-report/utils/index.html new file mode 100644 index 000000000..88cd8cf7c --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov-report/utils/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for utils + + + + + + + + + +
+
+

All files utils

+
+ +
+ 7.69% + Statements + 1/13 +
+ + +
+ 0% + Branches + 0/7 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 8.33% + Lines + 1/12 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
Get18DigitSalesforceId.ts +
+
7.69%1/130%0/70%0/18.33%1/12
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/coverage/lcov.info b/packages/sfpowerscripts-cli/coverage/lcov.info new file mode 100644 index 000000000..89a657895 --- /dev/null +++ b/packages/sfpowerscripts-cli/coverage/lcov.info @@ -0,0 +1,4285 @@ +TN: +SF:src/ProjectValidation.ts +FN:13,(anonymous_7) +FN:19,(anonymous_8) +FN:26,(anonymous_9) +FN:40,(anonymous_10) +FN:41,(anonymous_11) +FN:62,(anonymous_12) +FN:63,(anonymous_13) +FNF:7 +FNH:7 +FNDA:8,(anonymous_7) +FNDA:4,(anonymous_8) +FNDA:7,(anonymous_9) +FNDA:2,(anonymous_10) +FNDA:4,(anonymous_11) +FNDA:2,(anonymous_12) +FNDA:4,(anonymous_13) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:8,1 +DA:14,8 +DA:15,8 +DA:16,8 +DA:20,4 +DA:21,4 +DA:22,4 +DA:24,1 +DA:26,1 +DA:27,7 +DA:35,1 +DA:36,1 +DA:41,2 +DA:42,4 +DA:44,1 +DA:51,1 +DA:63,2 +DA:64,4 +DA:66,4 +DA:71,1 +LF:26 +LH:26 +BRDA:68,0,0,4 +BRDA:68,0,1,2 +BRDA:68,0,2,1 +BRF:3 +BRH:3 +end_of_record +TN: +SF:src/core/apex/ApexClassFetcher.ts +FN:6,(anonymous_1) +FN:14,(anonymous_2) +FN:19,(anonymous_3) +FNF:3 +FNH:1 +FNDA:1,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +DA:2,1 +DA:3,1 +DA:5,1 +DA:6,1 +DA:15,0 +DA:17,0 +DA:19,0 +DA:20,0 +DA:22,0 +DA:23,0 +DA:26,0 +LF:11 +LH:4 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/apex/ApexTriggerFetcher.ts +FN:6,(anonymous_1) +FN:14,(anonymous_2) +FN:19,(anonymous_3) +FNF:3 +FNH:1 +FNDA:1,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +DA:2,1 +DA:3,1 +DA:5,1 +DA:6,1 +DA:15,0 +DA:17,0 +DA:19,0 +DA:20,0 +DA:22,0 +DA:23,0 +DA:26,0 +LF:11 +LH:4 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/apex/coverage/ApexCodeCoverageAggregateFetcher.ts +FN:6,(anonymous_1) +FN:13,(anonymous_2) +FN:28,(anonymous_3) +FNF:3 +FNH:1 +FNDA:1,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +DA:2,1 +DA:3,1 +DA:5,1 +DA:6,1 +DA:24,0 +DA:26,0 +DA:28,0 +DA:29,0 +DA:31,0 +DA:37,0 +DA:40,0 +LF:11 +LH:4 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/apex/coverage/IndividualClassCoverage.ts +FN:4,(anonymous_6) +FN:6,(anonymous_7) +FN:13,(anonymous_8) +FN:19,(anonymous_9) +FN:27,(anonymous_10) +FN:46,(anonymous_11) +FNF:6 +FNH:5 +FNDA:9,(anonymous_6) +FNDA:2,(anonymous_7) +FNDA:8,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:4,(anonymous_10) +FNDA:13,(anonymous_11) +DA:1,2 +DA:3,2 +DA:4,9 +DA:10,2 +DA:13,2 +DA:14,8 +DA:18,2 +DA:19,0 +DA:20,0 +DA:24,2 +DA:37,0 +DA:38,0 +DA:41,4 +DA:46,4 +DA:47,13 +DA:50,4 +DA:51,2 +DA:58,2 +LF:18 +LH:14 +BRDA:18,0,0,0 +BRDA:18,1,0,2 +BRDA:18,1,1,0 +BRDA:50,2,0,2 +BRDA:50,2,1,2 +BRF:5 +BRH:3 +end_of_record +TN: +SF:src/core/apextest/ApexTestSuite.ts +FN:8,(anonymous_1) +FN:10,(anonymous_2) +FNF:2 +FNH:2 +FNDA:3,(anonymous_1) +FNDA:3,(anonymous_2) +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:7,1 +DA:8,3 +DA:11,3 +DA:16,3 +DA:18,3 +DA:20,2 +DA:23,1 +DA:25,1 +DA:26,1 +DA:27,1 +LF:14 +LH:14 +BRDA:18,0,0,1 +BRF:1 +BRH:1 +end_of_record +TN: +SF:src/core/apextest/ImpactedApexTestClassFetcher.ts +FN:9,(anonymous_7) +FN:16,(anonymous_8) +FN:24,(anonymous_9) +FNF:3 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +DA:1,1 +DA:2,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:8,1 +DA:10,0 +DA:11,0 +DA:12,0 +DA:13,0 +DA:18,0 +DA:19,0 +DA:23,0 +DA:24,0 +DA:27,0 +DA:28,0 +DA:32,0 +DA:33,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:43,0 +DA:51,0 +DA:56,0 +DA:57,0 +DA:62,0 +DA:66,0 +DA:71,0 +DA:73,0 +DA:74,0 +DA:81,0 +DA:86,0 +DA:88,0 +LF:33 +LH:6 +BRDA:50,0,0,0 +BRDA:50,0,1,0 +BRDA:62,1,0,0 +BRDA:66,2,0,0 +BRF:4 +BRH:0 +end_of_record +TN: +SF:src/core/artifacts/ArtifactFetcher.ts +FN:16,(anonymous_6) +FN:46,(anonymous_7) +FN:66,(anonymous_8) +FN:101,(anonymous_9) +FN:135,(anonymous_10) +FN:160,(anonymous_11) +FN:162,(anonymous_12) +FN:168,(anonymous_13) +FN:180,(anonymous_14) +FN:187,(anonymous_15) +FN:188,(anonymous_16) +FN:198,(anonymous_17) +FN:209,(anonymous_18) +FNF:13 +FNH:5 +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:3,(anonymous_10) +FNDA:1,(anonymous_11) +FNDA:4,(anonymous_12) +FNDA:4,(anonymous_13) +FNDA:4,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:9,1 +DA:17,0 +DA:20,0 +DA:23,0 +DA:25,0 +DA:30,0 +DA:32,0 +DA:34,0 +DA:36,0 +DA:39,0 +DA:47,0 +DA:49,0 +DA:51,0 +DA:57,0 +DA:59,0 +DA:67,0 +DA:69,0 +DA:70,0 +DA:73,0 +DA:75,0 +DA:77,0 +DA:80,0 +DA:82,0 +DA:84,0 +DA:86,0 +DA:92,0 +DA:94,0 +DA:102,0 +DA:103,0 +DA:105,0 +DA:111,0 +DA:113,0 +DA:115,0 +DA:117,0 +DA:123,0 +DA:125,0 +DA:138,2 +DA:140,1 +DA:143,3 +DA:148,3 +DA:149,1 +DA:150,1 +DA:151,1 +DA:152,1 +DA:153,2 +DA:162,1 +DA:163,4 +DA:164,4 +DA:167,1 +DA:168,1 +DA:169,4 +DA:170,4 +DA:172,4 +DA:173,0 +DA:177,1 +DA:178,1 +DA:180,4 +DA:188,0 +DA:189,0 +DA:200,0 +DA:202,0 +DA:205,0 +DA:210,0 +DA:211,0 +DA:212,0 +DA:213,0 +DA:214,0 +DA:216,0 +LF:75 +LH:28 +BRDA:148,0,0,1 +BRDA:148,0,1,2 +BRDA:148,1,0,3 +BRDA:148,1,1,2 +BRDA:164,2,0,4 +BRDA:164,2,1,1 +BRDA:172,3,0,4 +BRDA:172,3,1,0 +BRDA:189,4,0,0 +BRDA:199,5,0,0 +BRDA:199,5,1,0 +BRDA:201,6,0,0 +BRDA:201,6,1,0 +BRF:13 +BRH:7 +end_of_record +TN: +SF:src/core/display/TableConstants.ts +FNF:0 +FNH:0 +DA:2,1 +DA:22,1 +LF:2 +LH:2 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/git/Git.ts +FN:14,(anonymous_7) +FN:24,(anonymous_8) +FN:28,(anonymous_9) +FN:32,(anonymous_10) +FN:36,(anonymous_11) +FN:45,(anonymous_12) +FN:54,(anonymous_13) +FN:60,(anonymous_14) +FN:75,(anonymous_15) +FN:90,(anonymous_16) +FN:99,(anonymous_17) +FN:103,(anonymous_18) +FN:121,(anonymous_19) +FN:124,(anonymous_20) +FN:127,(anonymous_21) +FN:159,(anonymous_22) +FN:169,(anonymous_23) +FN:173,(anonymous_24) +FN:177,(anonymous_25) +FN:189,(anonymous_26) +FN:204,(anonymous_27) +FN:208,(anonymous_28) +FN:212,(anonymous_29) +FN:218,(anonymous_30) +FN:224,(anonymous_31) +FN:233,(anonymous_32) +FNF:26 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +FNDA:0,(anonymous_22) +FNDA:0,(anonymous_23) +FNDA:0,(anonymous_24) +FNDA:0,(anonymous_25) +FNDA:0,(anonymous_26) +FNDA:0,(anonymous_27) +FNDA:0,(anonymous_28) +FNDA:0,(anonymous_29) +FNDA:0,(anonymous_30) +FNDA:0,(anonymous_31) +FNDA:0,(anonymous_32) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:8,1 +DA:12,0 +DA:14,0 +DA:16,0 +DA:17,0 +DA:19,0 +DA:20,0 +DA:25,0 +DA:29,0 +DA:33,0 +DA:37,0 +DA:39,0 +DA:40,0 +DA:42,0 +DA:46,0 +DA:48,0 +DA:49,0 +DA:51,0 +DA:55,0 +DA:57,0 +DA:62,0 +DA:63,0 +DA:65,0 +DA:67,0 +DA:68,0 +DA:70,0 +DA:72,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:82,0 +DA:86,0 +DA:91,0 +DA:94,0 +DA:100,0 +DA:105,0 +DA:107,0 +DA:109,0 +DA:110,0 +DA:113,0 +DA:117,0 +DA:122,0 +DA:124,0 +DA:128,0 +DA:130,0 +DA:131,0 +DA:134,0 +DA:137,0 +DA:138,0 +DA:139,0 +DA:141,0 +DA:142,0 +DA:143,0 +DA:145,0 +DA:148,0 +DA:151,0 +DA:156,0 +DA:160,0 +DA:161,0 +DA:163,0 +DA:165,0 +DA:166,0 +DA:170,0 +DA:174,0 +DA:181,0 +DA:185,0 +DA:190,0 +DA:191,0 +DA:193,0 +DA:194,0 +DA:198,0 +DA:200,0 +DA:205,0 +DA:209,0 +DA:213,0 +DA:214,0 +DA:215,0 +DA:219,0 +DA:220,0 +DA:221,0 +DA:226,0 +DA:227,0 +DA:229,0 +DA:230,0 +DA:235,0 +DA:238,0 +DA:240,0 +DA:243,0 +LF:94 +LH:6 +BRDA:62,0,0,0 +BRDA:62,0,1,0 +BRDA:70,1,0,0 +BRDA:75,2,0,0 +BRDA:100,3,0,0 +BRDA:124,4,0,0 +BRDA:124,4,1,0 +BRDA:152,5,0,0 +BRDA:152,5,1,0 +BRDA:174,6,0,0 +BRDA:190,7,0,0 +BRDA:213,8,0,0 +BRDA:213,8,1,0 +BRDA:219,9,0,0 +BRDA:219,9,1,0 +BRF:15 +BRH:0 +end_of_record +TN: +SF:src/core/git/GitDiffUtil.ts +FN:30,(anonymous_7) +FN:36,(anonymous_8) +FN:56,(anonymous_9) +FN:67,(anonymous_10) +FN:107,(anonymous_11) +FN:114,(anonymous_12) +FN:122,(anonymous_13) +FN:145,(anonymous_14) +FN:165,(anonymous_15) +FNF:9 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +DA:1,1 +DA:2,1 +DA:3,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:22,1 +DA:24,1 +DA:31,0 +DA:32,0 +DA:33,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:51,0 +DA:53,0 +DA:57,0 +DA:59,0 +DA:60,0 +DA:66,0 +DA:67,0 +DA:69,0 +DA:73,0 +DA:74,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:81,0 +DA:87,0 +DA:89,0 +DA:92,0 +DA:95,0 +DA:96,0 +DA:97,0 +DA:99,0 +DA:102,0 +DA:103,0 +DA:108,0 +DA:110,0 +DA:111,0 +DA:114,0 +DA:115,0 +DA:117,0 +DA:123,0 +DA:130,0 +DA:133,0 +DA:137,0 +DA:141,0 +DA:145,0 +DA:146,0 +DA:147,0 +DA:148,0 +DA:152,0 +DA:154,0 +DA:155,0 +DA:159,0 +DA:165,0 +DA:166,0 +DA:170,0 +DA:173,0 +LF:70 +LH:9 +BRDA:43,0,0,0 +BRDA:73,1,0,0 +BRDA:129,2,0,0 +BRDA:129,2,1,0 +BRDA:132,3,0,0 +BRDA:132,3,1,0 +BRDA:136,4,0,0 +BRDA:136,4,1,0 +BRDA:136,4,2,0 +BRDA:140,5,0,0 +BRDA:140,5,1,0 +BRDA:140,5,2,0 +BRDA:144,6,0,0 +BRDA:144,6,1,0 +BRF:14 +BRH:0 +end_of_record +TN: +SF:src/core/git/GitIdentity.ts +FN:4,(anonymous_0) +FN:6,(anonymous_1) +FN:11,(anonymous_2) +FN:23,(anonymous_3) +FNF:4 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +DA:3,1 +DA:4,0 +DA:7,0 +DA:8,0 +DA:15,0 +DA:17,0 +DA:20,0 +DA:27,0 +DA:29,0 +DA:32,0 +LF:10 +LH:1 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/git/GitTags.ts +FN:5,(anonymous_0) +FN:13,(anonymous_1) +FN:25,(anonymous_2) +FN:45,(anonymous_3) +FN:49,(anonymous_4) +FN:53,(anonymous_5) +FN:58,(anonymous_6) +FN:74,(anonymous_7) +FN:82,(anonymous_8) +FN:92,(anonymous_9) +FN:104,(anonymous_10) +FN:121,(anonymous_11) +FN:126,(anonymous_12) +FN:131,(anonymous_13) +FN:134,(anonymous_14) +FN:138,(anonymous_15) +FN:141,(anonymous_16) +FN:147,(anonymous_17) +FNF:18 +FNH:6 +FNDA:3,(anonymous_0) +FNDA:3,(anonymous_1) +FNDA:2,(anonymous_2) +FNDA:24,(anonymous_3) +FNDA:8,(anonymous_4) +FNDA:8,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +DA:2,1 +DA:4,1 +DA:5,3 +DA:14,3 +DA:21,3 +DA:22,1 +DA:27,2 +DA:32,2 +DA:38,2 +DA:40,2 +DA:41,2 +DA:45,24 +DA:48,2 +DA:49,8 +DA:53,8 +DA:55,2 +DA:61,0 +DA:62,0 +DA:63,0 +DA:64,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:71,0 +DA:75,0 +DA:78,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:88,0 +DA:93,0 +DA:96,0 +DA:99,0 +DA:100,0 +DA:105,0 +DA:109,0 +DA:111,0 +DA:115,0 +DA:118,0 +DA:120,0 +DA:122,0 +DA:123,0 +DA:124,0 +DA:127,0 +DA:128,0 +DA:131,0 +DA:135,0 +DA:138,0 +DA:139,0 +DA:141,0 +DA:146,0 +DA:147,0 +DA:148,0 +LF:53 +LH:16 +BRDA:21,0,0,2 +BRDA:21,0,1,1 +BRDA:63,1,0,0 +BRDA:63,1,1,0 +BRDA:67,2,0,0 +BRDA:67,2,1,0 +BRDA:77,3,0,0 +BRDA:77,3,1,0 +BRDA:83,4,0,0 +BRDA:83,4,1,0 +BRDA:128,5,0,0 +BRDA:128,5,1,0 +BRF:12 +BRH:2 +end_of_record +TN: +SF:src/core/metadata/MetadataFiles.ts +FN:16,(anonymous_7) +FN:23,(anonymous_8) +FN:37,(anonymous_9) +FN:52,(anonymous_10) +FN:63,(anonymous_11) +FN:116,(anonymous_12) +FN:120,(anonymous_13) +FN:176,(anonymous_14) +FN:185,(anonymous_15) +FN:189,(anonymous_16) +FN:190,(anonymous_17) +FN:194,(anonymous_18) +FN:208,(anonymous_19) +FNF:13 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:11,1 +DA:13,1 +DA:14,1 +DA:18,0 +DA:20,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:29,0 +DA:30,0 +DA:31,0 +DA:33,0 +DA:35,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:47,0 +DA:49,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:56,0 +DA:59,0 +DA:61,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:81,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:88,0 +DA:91,0 +DA:92,0 +DA:93,0 +DA:94,0 +DA:95,0 +DA:97,0 +DA:100,0 +DA:101,0 +DA:102,0 +DA:103,0 +DA:104,0 +DA:106,0 +DA:108,0 +DA:109,0 +DA:113,0 +DA:117,0 +DA:118,0 +DA:120,0 +DA:121,0 +DA:123,0 +DA:124,0 +DA:126,0 +DA:131,0 +DA:135,0 +DA:136,0 +DA:139,0 +DA:141,0 +DA:147,0 +DA:148,0 +DA:149,0 +DA:152,0 +DA:154,0 +DA:155,0 +DA:160,0 +DA:163,0 +DA:164,0 +DA:167,0 +DA:169,0 +DA:170,0 +DA:176,0 +DA:178,0 +DA:179,0 +DA:186,0 +DA:190,0 +DA:192,0 +DA:194,0 +DA:195,0 +DA:196,0 +DA:198,0 +DA:209,0 +DA:210,0 +DA:211,0 +DA:212,0 +DA:213,0 +DA:215,0 +DA:218,0 +DA:221,0 +DA:223,0 +DA:227,0 +DA:229,0 +DA:230,0 +DA:234,0 +DA:237,0 +DA:240,0 +DA:243,0 +DA:246,0 +DA:247,0 +DA:248,0 +DA:250,0 +DA:255,0 +DA:257,0 +DA:259,0 +DA:260,0 +DA:262,0 +DA:263,0 +DA:265,0 +DA:266,0 +DA:267,0 +DA:268,0 +DA:269,0 +DA:276,0 +DA:277,0 +DA:278,0 +DA:279,0 +DA:281,0 +DA:287,0 +DA:288,0 +DA:289,0 +DA:290,0 +DA:291,0 +DA:292,0 +DA:293,0 +DA:295,0 +DA:296,0 +DA:297,0 +DA:298,0 +DA:299,0 +DA:300,0 +DA:301,0 +DA:304,0 +DA:307,0 +DA:311,0 +DA:314,0 +DA:319,0 +DA:320,0 +DA:321,0 +DA:322,0 +DA:323,0 +DA:325,0 +DA:326,0 +DA:327,0 +DA:328,0 +DA:332,0 +DA:335,0 +DA:339,0 +LF:170 +LH:11 +BRDA:57,0,0,0 +BRDA:57,0,1,0 +BRDA:59,1,0,0 +BRDA:59,1,1,0 +BRDA:87,2,0,0 +BRDA:87,2,1,0 +BRDA:116,3,0,0 +BRDA:119,4,0,0 +BRDA:119,4,1,0 +BRDA:128,5,0,0 +BRDA:128,5,1,0 +BRDA:138,6,0,0 +BRDA:138,6,1,0 +BRDA:138,6,2,0 +BRDA:161,7,0,0 +BRDA:161,7,1,0 +BRDA:166,8,0,0 +BRDA:166,8,1,0 +BRDA:166,8,2,0 +BRDA:191,9,0,0 +BRDA:191,9,1,0 +BRDA:275,10,0,0 +BRDA:275,10,1,0 +BRDA:324,11,0,0 +BRDA:324,11,1,0 +BRF:25 +BRH:0 +end_of_record +TN: +SF:src/core/metadata/MetadataInfo.ts +FN:102,(anonymous_6) +FN:107,(anonymous_7) +FN:129,(anonymous_8) +FN:149,(anonymous_9) +FNF:4 +FNH:3 +FNDA:1,(anonymous_6) +FNDA:149,(anonymous_7) +FNDA:25,(anonymous_8) +FNDA:0,(anonymous_9) +DA:2,1 +DA:3,1 +DA:4,1 +DA:6,1 +DA:7,1 +DA:101,1 +DA:103,1 +DA:104,1 +DA:105,1 +DA:106,1 +DA:107,1 +DA:108,149 +DA:111,1 +DA:112,1 +DA:114,1 +DA:115,1 +DA:118,149 +DA:120,4 +DA:122,0 +DA:124,4 +DA:129,9 +DA:130,25 +DA:132,9 +DA:133,9 +DA:134,9 +DA:135,9 +DA:136,9 +DA:137,9 +DA:138,9 +DA:139,9 +DA:140,9 +DA:144,149 +DA:146,1 +DA:150,0 +DA:151,0 +DA:153,0 +DA:155,0 +DA:158,0 +DA:160,0 +DA:161,0 +DA:162,0 +DA:163,0 +DA:164,0 +DA:166,0 +DA:171,0 +DA:176,0 +DA:181,0 +DA:186,0 +DA:188,0 +DA:189,0 +DA:190,0 +DA:196,0 +DA:197,0 +DA:201,0 +DA:205,1 +DA:206,1 +DA:214,1 +LF:57 +LH:35 +BRDA:149,0,0,0 +BRDA:165,1,0,0 +BRDA:165,1,1,0 +BRDA:165,1,2,0 +BRDA:168,2,0,0 +BRDA:168,2,1,0 +BRDA:168,2,2,0 +BRDA:173,3,0,0 +BRDA:173,3,1,0 +BRDA:173,3,2,0 +BRDA:178,4,0,0 +BRDA:178,4,1,0 +BRDA:178,4,2,0 +BRDA:183,5,0,0 +BRDA:183,5,1,0 +BRDA:183,5,2,0 +BRDA:192,6,0,0 +BRDA:192,6,1,0 +BRDA:192,6,2,0 +BRDA:192,6,3,0 +BRF:20 +BRH:0 +end_of_record +TN: +SF:src/core/metadata/SettingsFetcher.ts +FN:8,(anonymous_7) +FN:12,(anonymous_8) +FNF:2 +FNH:1 +FNDA:5,(anonymous_7) +FNDA:0,(anonymous_8) +DA:1,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:7,1 +DA:9,5 +DA:13,0 +DA:14,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +LF:12 +LH:6 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/org/SFPOrg.ts +FN:14,(anonymous_7) +FN:39,(anonymous_8) +FN:76,(anonymous_9) +FN:128,(anonymous_10) +FN:142,(anonymous_11) +FN:147,(anonymous_12) +FN:169,(anonymous_13) +FN:171,(anonymous_14) +FN:176,(anonymous_15) +FN:179,(anonymous_16) +FN:188,(anonymous_17) +FN:195,(anonymous_18) +FN:200,(anonymous_19) +FN:207,(anonymous_20) +FN:219,(anonymous_21) +FN:220,(anonymous_22) +FNF:16 +FNH:5 +FNDA:6,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:3,(anonymous_9) +FNDA:3,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:1,(anonymous_15) +FNDA:2,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +FNDA:0,(anonymous_22) +DA:1,4 +DA:2,4 +DA:5,4 +DA:6,4 +DA:7,4 +DA:8,4 +DA:10,4 +DA:15,6 +DA:17,6 +DA:22,5 +DA:24,1 +DA:32,1 +DA:43,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:53,0 +DA:55,0 +DA:56,0 +DA:61,0 +DA:69,0 +DA:77,3 +DA:79,3 +DA:89,3 +DA:92,3 +DA:103,0 +DA:116,2 +DA:125,2 +DA:129,3 +DA:131,3 +DA:134,0 +DA:137,3 +DA:143,0 +DA:145,0 +DA:147,0 +DA:148,0 +DA:150,0 +DA:160,0 +DA:163,0 +DA:170,0 +DA:171,0 +DA:177,1 +DA:178,1 +DA:179,1 +DA:180,2 +DA:184,1 +DA:185,0 +DA:189,0 +DA:196,0 +DA:197,0 +DA:198,0 +DA:200,0 +DA:201,0 +DA:207,0 +DA:209,0 +DA:210,0 +DA:211,0 +DA:213,0 +DA:214,0 +DA:216,0 +DA:219,0 +DA:220,0 +DA:222,0 +DA:227,0 +DA:228,0 +DA:229,0 +DA:231,0 +DA:232,0 +DA:233,0 +DA:236,0 +DA:241,4 +LF:72 +LH:27 +BRDA:14,0,0,6 +BRDA:83,1,0,0 +BRDA:83,1,1,3 +BRDA:119,2,0,2 +BRDA:119,2,1,0 +BRDA:177,3,0,1 +BRDA:177,3,1,0 +BRDA:181,4,0,0 +BRDA:181,4,1,2 +BRDA:181,5,0,1 +BRDA:181,5,1,1 +BRDA:210,6,0,0 +BRDA:210,6,1,0 +BRDA:227,7,0,0 +BRDA:227,7,1,0 +BRDA:228,8,0,0 +BRDA:228,8,1,0 +BRF:17 +BRH:7 +end_of_record +TN: +SF:src/core/org/packageQuery/InstalledPackagesQueryExecutor.ts +FN:5,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:2,4 +DA:4,4 +DA:7,0 +DA:12,0 +LF:4 +LH:2 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/package/SfpPackage.ts +FN:64,(anonymous_1) +FN:68,(anonymous_2) +FN:72,(anonymous_3) +FN:77,(anonymous_4) +FN:81,(anonymous_5) +FN:89,(anonymous_6) +FN:93,(anonymous_7) +FN:107,(anonymous_8) +FNF:8 +FNH:4 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:8,(anonymous_4) +FNDA:7,(anonymous_5) +FNDA:12,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:13,(anonymous_8) +DA:1,13 +DA:54,13 +DA:61,12 +DA:65,0 +DA:69,0 +DA:74,0 +DA:78,8 +DA:82,7 +DA:90,12 +DA:94,0 +DA:95,0 +DA:96,0 +DA:97,0 +DA:98,0 +DA:99,0 +DA:100,0 +DA:101,0 +DA:102,0 +DA:107,13 +DA:108,13 +DA:109,13 +DA:110,13 +DA:111,13 +LF:23 +LH:11 +BRDA:107,0,0,13 +BRDA:107,0,1,13 +BRF:2 +BRH:2 +end_of_record +TN: +SF:src/core/package/SfpPackageBuilder.ts +FN:28,(anonymous_7) +FN:193,(anonymous_8) +FN:214,(anonymous_9) +FN:235,(anonymous_10) +FN:257,(anonymous_11) +FN:264,(anonymous_12) +FNF:6 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:11,1 +DA:13,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:17,1 +DA:18,1 +DA:19,1 +DA:21,1 +DA:22,1 +DA:23,1 +DA:24,1 +DA:25,1 +DA:27,1 +DA:37,0 +DA:40,0 +DA:43,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:58,0 +DA:59,0 +DA:61,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:71,0 +DA:75,0 +DA:77,0 +DA:80,0 +DA:92,0 +DA:96,0 +DA:102,0 +DA:103,0 +DA:105,0 +DA:106,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:110,0 +DA:112,0 +DA:113,0 +DA:114,0 +DA:115,0 +DA:119,0 +DA:121,0 +DA:124,0 +DA:129,0 +DA:131,0 +DA:138,0 +DA:140,0 +DA:141,0 +DA:146,0 +DA:153,0 +DA:155,0 +DA:162,0 +DA:164,0 +DA:171,0 +DA:173,0 +DA:174,0 +DA:175,0 +DA:182,0 +DA:185,0 +DA:199,0 +DA:202,0 +DA:203,0 +DA:209,0 +DA:211,0 +DA:216,0 +DA:217,0 +DA:218,0 +DA:219,0 +DA:221,0 +DA:222,0 +DA:226,0 +DA:227,0 +DA:228,0 +DA:230,0 +DA:236,0 +DA:242,0 +DA:243,0 +DA:251,0 +DA:252,0 +DA:253,0 +DA:258,0 +DA:259,0 +DA:264,1 +DA:265,0 +LF:102 +LH:24 +BRDA:58,0,0,0 +BRDA:58,0,1,0 +BRDA:67,1,0,0 +BRDA:67,1,1,0 +BRDA:131,2,0,0 +BRDA:138,3,0,0 +BRDA:141,4,0,0 +BRDA:145,5,0,0 +BRDA:145,5,1,0 +BRDA:145,5,2,0 +BRDA:145,5,3,0 +BRDA:236,6,0,0 +BRDA:236,6,1,0 +BRDA:237,7,0,0 +BRDA:237,7,1,0 +BRDA:237,7,2,0 +BRDA:237,7,3,0 +BRDA:258,8,0,0 +BRDA:258,8,1,0 +BRF:19 +BRH:0 +end_of_record +TN: +SF:src/core/package/analyser/AnalyzerRegistry.ts +FN:7,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:4,1 +DA:6,1 +DA:8,0 +DA:11,0 +DA:12,0 +DA:13,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:18,0 +LF:12 +LH:4 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/package/analyser/FHTAnalyzer.ts +FN:11,(anonymous_7) +FN:17,(anonymous_8) +FN:51,(anonymous_9) +FN:72,(anonymous_10) +FNF:4 +FNH:3 +FNDA:0,(anonymous_7) +FNDA:4,(anonymous_8) +FNDA:4,(anonymous_9) +FNDA:3,(anonymous_10) +DA:1,2 +DA:2,2 +DA:3,2 +DA:4,2 +DA:5,2 +DA:7,2 +DA:9,2 +DA:12,0 +DA:20,4 +DA:23,4 +DA:33,3 +DA:38,4 +DA:41,4 +DA:42,4 +DA:46,0 +DA:48,4 +DA:55,4 +DA:59,0 +DA:62,3 +DA:64,2 +DA:65,2 +DA:66,2 +DA:69,4 +DA:73,3 +DA:74,1 +LF:25 +LH:22 +BRDA:65,0,0,2 +BRDA:73,1,0,2 +BRDA:73,1,1,1 +BRF:3 +BRH:3 +end_of_record +TN: +SF:src/core/package/analyser/FTAnalyzer.ts +FN:11,(anonymous_7) +FN:15,(anonymous_8) +FN:49,(anonymous_9) +FN:70,(anonymous_10) +FNF:4 +FNH:3 +FNDA:0,(anonymous_7) +FNDA:4,(anonymous_8) +FNDA:4,(anonymous_9) +FNDA:3,(anonymous_10) +DA:1,2 +DA:2,2 +DA:3,2 +DA:4,2 +DA:5,2 +DA:7,2 +DA:9,2 +DA:12,0 +DA:18,4 +DA:21,4 +DA:31,3 +DA:36,4 +DA:39,4 +DA:40,4 +DA:44,0 +DA:46,4 +DA:53,4 +DA:57,0 +DA:60,3 +DA:62,2 +DA:63,2 +DA:64,2 +DA:67,4 +DA:71,3 +DA:72,1 +LF:25 +LH:22 +BRDA:63,0,0,2 +BRDA:71,1,0,2 +BRDA:71,1,1,1 +BRF:3 +BRH:3 +end_of_record +TN: +SF:src/core/package/analyser/PicklistAnalyzer.ts +FN:8,(anonymous_6) +FN:14,(anonymous_7) +FN:48,(anonymous_8) +FNF:3 +FNH:0 +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +DA:1,1 +DA:2,1 +DA:4,1 +DA:6,1 +DA:9,0 +DA:16,0 +DA:17,0 +DA:23,0 +DA:27,0 +DA:33,0 +DA:37,0 +DA:38,0 +DA:43,0 +DA:45,0 +DA:49,0 +DA:50,0 +LF:16 +LH:4 +BRDA:36,0,0,0 +BRDA:36,0,1,0 +BRDA:49,1,0,0 +BRDA:49,1,1,0 +BRF:4 +BRH:0 +end_of_record +TN: +SF:src/core/package/components/PackageManifest.ts +FN:14,(anonymous_7) +FN:21,(anonymous_8) +FN:25,(anonymous_9) +FN:32,(anonymous_10) +FN:49,(anonymous_11) +FN:58,(anonymous_12) +FN:59,(anonymous_13) +FN:92,(anonymous_14) +FN:109,(anonymous_15) +FN:132,(anonymous_16) +FN:151,(anonymous_17) +FN:170,(anonymous_18) +FN:196,(anonymous_19) +FN:226,(anonymous_20) +FN:243,(anonymous_21) +FNF:15 +FNH:14 +FNDA:3,(anonymous_7) +FNDA:3,(anonymous_8) +FNDA:13,(anonymous_9) +FNDA:6,(anonymous_10) +FNDA:6,(anonymous_11) +FNDA:48,(anonymous_12) +FNDA:150,(anonymous_13) +FNDA:1,(anonymous_14) +FNDA:2,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:2,(anonymous_17) +FNDA:1,(anonymous_18) +FNDA:2,(anonymous_19) +FNDA:1,(anonymous_20) +FNDA:1,(anonymous_21) +DA:1,2 +DA:2,2 +DA:4,2 +DA:5,2 +DA:7,2 +DA:15,3 +DA:22,3 +DA:33,6 +DA:35,6 +DA:37,6 +DA:38,6 +DA:40,6 +DA:50,6 +DA:52,6 +DA:58,6 +DA:59,150 +DA:62,6 +DA:65,42 +DA:69,42 +DA:73,6 +DA:77,6 +DA:81,6 +DA:82,6 +DA:84,6 +DA:93,1 +DA:94,1 +DA:96,1 +DA:100,1 +DA:102,1 +DA:110,2 +DA:116,1 +DA:117,1 +DA:121,0 +DA:125,2 +DA:133,0 +DA:139,0 +DA:140,0 +DA:144,0 +DA:148,0 +DA:152,2 +DA:156,1 +DA:157,1 +DA:161,0 +DA:163,2 +DA:171,1 +DA:177,1 +DA:178,1 +DA:185,0 +DA:189,1 +DA:202,2 +DA:205,0 +DA:213,1 +DA:216,0 +DA:218,1 +DA:223,2 +DA:227,1 +DA:232,1 +DA:233,1 +DA:237,0 +DA:240,1 +DA:244,1 +DA:256,1 +DA:261,1 +DA:262,1 +DA:266,0 +DA:269,1 +LF:66 +LH:54 +BRDA:176,0,0,2 +BRDA:176,0,1,1 +BRDA:182,1,0,0 +BRDA:182,1,1,0 +BRF:4 +BRH:2 +end_of_record +TN: +SF:src/core/package/components/PackageToComponent.ts +FN:6,(anonymous_0) +FN:8,(anonymous_1) +FNF:2 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +DA:1,1 +DA:5,1 +DA:6,0 +DA:9,0 +DA:11,0 +DA:13,0 +DA:16,0 +DA:23,0 +DA:26,0 +LF:9 +LH:2 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/package/coverage/PackageTestCoverage.ts +FN:13,(anonymous_7) +FN:22,(anonymous_8) +FN:47,(anonymous_9) +FN:55,(anonymous_10) +FN:66,(anonymous_11) +FN:78,(anonymous_12) +FN:149,(anonymous_13) +FN:182,(anonymous_14) +FN:195,(anonymous_15) +FN:211,(anonymous_16) +FN:213,(anonymous_17) +FN:232,(anonymous_18) +FN:234,(anonymous_19) +FN:252,(anonymous_20) +FN:253,(anonymous_21) +FNF:15 +FNH:15 +FNDA:7,(anonymous_7) +FNDA:7,(anonymous_8) +FNDA:1,(anonymous_9) +FNDA:1,(anonymous_10) +FNDA:2,(anonymous_11) +FNDA:5,(anonymous_12) +FNDA:8,(anonymous_13) +FNDA:2,(anonymous_14) +FNDA:2,(anonymous_15) +FNDA:15,(anonymous_16) +FNDA:15,(anonymous_17) +FNDA:15,(anonymous_18) +FNDA:30,(anonymous_19) +FNDA:15,(anonymous_20) +FNDA:39,(anonymous_21) +DA:1,1 +DA:2,1 +DA:3,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:9,1 +DA:11,7 +DA:14,7 +DA:15,7 +DA:16,7 +DA:17,7 +DA:19,7 +DA:23,7 +DA:24,7 +DA:26,7 +DA:32,7 +DA:33,7 +DA:36,19 +DA:37,19 +DA:41,7 +DA:43,7 +DA:45,1 +DA:47,1 +DA:48,1 +DA:51,7 +DA:53,1 +DA:55,1 +DA:56,1 +DA:60,1 +DA:65,1 +DA:66,1 +DA:67,2 +DA:69,1 +DA:73,7 +DA:74,7 +DA:75,7 +DA:87,5 +DA:89,5 +DA:91,5 +DA:94,3 +DA:95,3 +DA:103,0 +DA:112,2 +DA:120,3 +DA:122,3 +DA:128,2 +DA:136,1 +DA:145,0 +DA:153,8 +DA:155,8 +DA:156,8 +DA:158,8 +DA:166,20 +DA:173,8 +DA:182,2 +DA:183,2 +DA:185,2 +DA:189,8 +DA:195,2 +DA:196,2 +DA:198,2 +DA:201,8 +DA:212,15 +DA:213,15 +DA:217,12 +DA:220,3 +DA:222,0 +DA:233,15 +DA:234,15 +DA:238,27 +DA:241,3 +DA:243,0 +DA:253,15 +DA:256,63 +DA:263,12 +DA:268,0 +DA:271,15 +LF:78 +LH:73 +BRDA:87,0,0,5 +BRDA:93,1,0,5 +BRDA:93,1,1,2 +BRDA:119,2,0,3 +BRDA:119,2,1,0 +BRDA:212,3,0,15 +BRDA:212,3,1,0 +BRDA:233,4,0,15 +BRDA:233,4,1,0 +BRDA:256,5,0,27 +BRF:10 +BRH:7 +end_of_record +TN: +SF:src/core/package/dependencies/ExternalPackage2DependencyResolver.ts +FN:12,(anonymous_1) +FN:14,(anonymous_2) +FN:55,(anonymous_3) +FN:86,(anonymous_4) +FNF:4 +FNH:3 +FNDA:2,(anonymous_1) +FNDA:2,(anonymous_2) +FNDA:2,(anonymous_3) +FNDA:0,(anonymous_4) +DA:2,2 +DA:4,2 +DA:10,2 +DA:12,2 +DA:19,2 +DA:22,2 +DA:30,2 +DA:31,2 +DA:35,0 +DA:41,12 +DA:42,0 +DA:45,10 +DA:46,4 +DA:50,0 +DA:51,0 +DA:52,0 +DA:55,2 +DA:56,2 +DA:58,0 +DA:59,0 +DA:64,0 +DA:67,2 +DA:71,0 +DA:73,2 +DA:78,2 +DA:87,0 +DA:89,0 +DA:90,0 +DA:93,0 +DA:95,0 +DA:98,0 +DA:101,0 +LF:32 +LH:16 +BRDA:19,0,0,2 +BRDA:41,1,0,0 +BRDA:41,2,0,12 +BRDA:41,2,1,0 +BRDA:44,3,0,12 +BRDA:44,3,1,10 +BRDA:48,4,0,4 +BRDA:48,4,1,0 +BRF:8 +BRH:5 +end_of_record +TN: +SF:src/core/package/dependencies/PackageDependencyResolver.ts +FN:15,(anonymous_7) +FN:31,(anonymous_8) +FN:116,(anonymous_9) +FN:124,(anonymous_10) +FN:176,(anonymous_11) +FN:191,(anonymous_12) +FN:221,(anonymous_13) +FN:227,(anonymous_14) +FN:236,(anonymous_15) +FN:249,(anonymous_16) +FN:265,(anonymous_17) +FNF:11 +FNH:11 +FNDA:10,(anonymous_7) +FNDA:10,(anonymous_8) +FNDA:16,(anonymous_9) +FNDA:38,(anonymous_10) +FNDA:13,(anonymous_11) +FNDA:4,(anonymous_12) +FNDA:55,(anonymous_13) +FNDA:10,(anonymous_14) +FNDA:6,(anonymous_15) +FNDA:5,(anonymous_16) +FNDA:6,(anonymous_17) +DA:2,3 +DA:3,3 +DA:4,3 +DA:5,3 +DA:6,3 +DA:12,3 +DA:13,10 +DA:16,10 +DA:17,10 +DA:18,10 +DA:19,10 +DA:20,10 +DA:23,10 +DA:34,0 +DA:38,24 +DA:42,0 +DA:45,19 +DA:46,28 +DA:49,1 +DA:52,27 +DA:56,9 +DA:61,2 +DA:63,16 +DA:65,1 +DA:66,1 +DA:72,1 +DA:78,1 +DA:84,1 +DA:85,1 +DA:86,1 +DA:87,1 +DA:88,1 +DA:91,15 +DA:99,13 +DA:100,10 +DA:101,10 +DA:103,3 +DA:107,7 +DA:124,38 +DA:125,16 +DA:130,6 +DA:131,6 +DA:133,6 +DA:138,1 +DA:143,5 +DA:146,1 +DA:152,4 +DA:159,5 +DA:164,5 +DA:171,1 +DA:176,13 +DA:177,4 +DA:180,1 +DA:182,4 +DA:197,4 +DA:198,4 +DA:199,4 +DA:202,9 +DA:205,3 +DA:206,3 +DA:209,9 +DA:213,1 +DA:218,3 +DA:222,55 +DA:223,55 +DA:228,10 +DA:237,6 +DA:238,6 +DA:239,5 +DA:254,5 +DA:255,5 +DA:256,5 +DA:266,6 +DA:267,6 +LF:74 +LH:72 +BRDA:33,0,0,47 +BRDA:33,0,1,0 +BRDA:37,1,0,47 +BRDA:37,1,1,35 +BRDA:41,2,0,23 +BRDA:41,2,1,0 +BRDA:44,3,0,23 +BRDA:44,3,1,19 +BRDA:47,4,0,28 +BRDA:47,4,1,1 +BRDA:52,5,0,0 +BRDA:52,5,1,27 +BRDA:59,6,0,18 +BRDA:59,6,1,8 +BRDA:59,6,2,2 +BRDA:64,7,0,16 +BRDA:64,7,1,1 +BRDA:99,8,0,10 +BRDA:99,8,1,3 +BRDA:125,9,0,10 +BRDA:132,10,0,6 +BRDA:132,10,1,6 +BRDA:176,11,0,13 +BRDA:176,11,1,5 +BRDA:209,12,0,3 +BRDA:238,13,0,1 +BRDA:238,13,1,5 +BRF:27 +BRH:24 +end_of_record +TN: +SF:src/core/package/dependencies/TransitiveDependencyResolver.ts +FN:11,(anonymous_7) +FN:13,(anonymous_8) +FN:28,(anonymous_9) +FN:40,(anonymous_10) +FN:68,(anonymous_11) +FN:69,(anonymous_12) +FN:90,(anonymous_13) +FN:91,(anonymous_14) +FN:98,(anonymous_15) +FNF:9 +FNH:8 +FNDA:7,(anonymous_7) +FNDA:7,(anonymous_8) +FNDA:7,(anonymous_9) +FNDA:7,(anonymous_10) +FNDA:221,(anonymous_11) +FNDA:152,(anonymous_12) +FNDA:727,(anonymous_13) +FNDA:727,(anonymous_14) +FNDA:0,(anonymous_15) +DA:1,2 +DA:2,2 +DA:3,2 +DA:4,2 +DA:5,2 +DA:6,2 +DA:8,2 +DA:10,2 +DA:11,7 +DA:14,7 +DA:16,7 +DA:17,7 +DA:18,7 +DA:19,7 +DA:23,7 +DA:25,7 +DA:34,7 +DA:37,7 +DA:43,7 +DA:45,42 +DA:50,42 +DA:54,65 +DA:55,65 +DA:64,87 +DA:67,42 +DA:68,221 +DA:69,152 +DA:70,42 +DA:72,96 +DA:74,96 +DA:76,7 +DA:79,0 +DA:82,7 +DA:83,7 +DA:89,145 +DA:90,727 +DA:91,727 +DA:93,42 +DA:95,7 +DA:100,0 +DA:103,0 +DA:104,0 +DA:105,0 +LF:43 +LH:38 +BRDA:99,0,0,0 +BRDA:99,0,1,0 +BRDA:99,0,2,0 +BRDA:99,0,3,0 +BRF:4 +BRH:0 +end_of_record +TN: +SF:src/core/package/deploymentFilters/EntitlementVersionFilter.ts +FN:16,(anonymous_7) +FN:51,(anonymous_8) +FN:93,(anonymous_9) +FNF:3 +FNH:2 +FNDA:5,(anonymous_7) +FNDA:7,(anonymous_8) +FNDA:0,(anonymous_9) +DA:1,1 +DA:3,1 +DA:4,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:11,1 +DA:12,1 +DA:14,1 +DA:18,5 +DA:19,5 +DA:22,5 +DA:23,5 +DA:26,5 +DA:29,5 +DA:33,3 +DA:34,3 +DA:36,1 +DA:37,1 +DA:40,4 +DA:42,4 +DA:43,4 +DA:48,8 +DA:50,8 +DA:51,7 +DA:61,1 +DA:62,1 +DA:67,1 +DA:68,1 +DA:69,1 +DA:71,3 +DA:78,4 +DA:81,0 +DA:85,4 +DA:86,4 +DA:88,1 +DA:89,1 +DA:94,0 +DA:96,0 +DA:97,0 +LF:41 +LH:37 +BRDA:26,0,0,0 +BRDA:55,1,0,8 +BRDA:55,1,1,4 +BRDA:55,1,2,4 +BRDA:94,2,0,0 +BRDA:96,3,0,0 +BRDA:96,3,1,0 +BRF:7 +BRH:3 +end_of_record +TN: +SF:src/core/package/diff/PackageComponentDiff.ts +FN:30,(anonymous_7) +FN:51,(anonymous_8) +FN:79,(anonymous_9) +FN:156,(anonymous_10) +FN:163,(anonymous_11) +FN:174,(anonymous_12) +FN:178,(anonymous_13) +FN:289,(anonymous_14) +FN:294,(anonymous_15) +FN:316,(anonymous_16) +FN:336,(anonymous_17) +FN:418,(anonymous_18) +FNF:12 +FNH:1 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:1,(anonymous_18) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:10,1 +DA:11,1 +DA:12,1 +DA:14,1 +DA:15,1 +DA:18,1 +DA:31,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:38,0 +DA:41,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:47,0 +DA:48,0 +DA:52,0 +DA:54,0 +DA:55,0 +DA:58,0 +DA:59,0 +DA:61,0 +DA:64,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:76,0 +DA:77,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:85,0 +DA:86,0 +DA:89,0 +DA:93,0 +DA:96,0 +DA:99,0 +DA:102,0 +DA:104,0 +DA:107,0 +DA:108,0 +DA:110,0 +DA:111,0 +DA:112,0 +DA:114,0 +DA:116,0 +DA:118,0 +DA:120,0 +DA:125,0 +DA:126,0 +DA:130,0 +DA:140,0 +DA:141,0 +DA:146,0 +DA:147,0 +DA:150,0 +DA:152,0 +DA:157,0 +DA:159,0 +DA:162,0 +DA:163,0 +DA:168,0 +DA:171,0 +DA:176,0 +DA:178,0 +DA:179,0 +DA:182,0 +DA:184,0 +DA:185,0 +DA:187,0 +DA:188,0 +DA:190,0 +DA:192,0 +DA:195,0 +DA:202,0 +DA:210,0 +DA:212,0 +DA:214,0 +DA:216,0 +DA:222,0 +DA:228,0 +DA:236,0 +DA:242,0 +DA:248,0 +DA:256,0 +DA:257,0 +DA:262,0 +DA:276,0 +DA:286,0 +DA:291,0 +DA:292,0 +DA:294,0 +DA:295,0 +DA:299,0 +DA:308,0 +DA:309,0 +DA:310,0 +DA:311,0 +DA:312,0 +DA:317,0 +DA:318,0 +DA:320,0 +DA:321,0 +DA:322,0 +DA:327,0 +DA:331,0 +DA:333,0 +DA:337,0 +DA:338,0 +DA:339,0 +DA:340,0 +DA:341,0 +DA:343,0 +DA:345,0 +DA:350,0 +DA:352,0 +DA:354,0 +DA:355,0 +DA:356,0 +DA:359,0 +DA:363,0 +DA:366,0 +DA:370,0 +DA:377,0 +DA:384,0 +DA:386,0 +DA:388,0 +DA:389,0 +DA:390,0 +DA:393,0 +DA:397,0 +DA:400,0 +DA:408,0 +DA:415,0 +DA:418,1 +DA:419,1 +DA:420,1 +DA:421,1 +DA:422,1 +DA:423,1 +LF:153 +LH:21 +BRDA:37,0,0,0 +BRDA:37,0,1,0 +BRDA:98,1,0,0 +BRDA:98,1,1,0 +BRDA:111,2,0,0 +BRDA:111,2,1,0 +BRDA:125,3,0,0 +BRDA:157,4,0,0 +BRDA:157,4,1,0 +BRDA:165,5,0,0 +BRDA:165,5,1,0 +BRDA:179,6,0,0 +BRDA:179,6,1,0 +BRDA:256,7,0,0 +BRDA:256,7,1,0 +BRDA:295,8,0,0 +BRDA:295,8,1,0 +BRDA:418,9,0,1 +BRDA:418,9,1,1 +BRF:19 +BRH:2 +end_of_record +TN: +SF:src/core/package/diff/PackageDiffImpl.ts +FN:12,(anonymous_7) +FN:21,(anonymous_8) +FN:28,(anonymous_9) +FN:109,(anonymous_10) +FN:123,(anonymous_11) +FN:137,(anonymous_12) +FN:159,(anonymous_13) +FNF:7 +FNH:6 +FNDA:1,(anonymous_7) +FNDA:9,(anonymous_8) +FNDA:9,(anonymous_9) +FNDA:6,(anonymous_10) +FNDA:8,(anonymous_11) +FNDA:5,(anonymous_12) +FNDA:0,(anonymous_13) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:10,1 +DA:12,1 +DA:13,1 +DA:15,1 +DA:20,1 +DA:22,9 +DA:23,9 +DA:24,9 +DA:25,9 +DA:29,9 +DA:31,9 +DA:32,9 +DA:34,8 +DA:44,0 +DA:46,8 +DA:50,6 +DA:55,6 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:64,6 +DA:66,6 +DA:68,6 +DA:77,7 +DA:78,7 +DA:80,7 +DA:83,1 +DA:84,1 +DA:88,5 +DA:93,5 +DA:95,1 +DA:98,4 +DA:100,2 +DA:105,2 +DA:111,6 +DA:112,6 +DA:113,6 +DA:115,6 +DA:118,6 +DA:120,6 +DA:124,8 +DA:125,8 +DA:127,8 +DA:129,0 +DA:131,8 +DA:134,8 +DA:138,5 +DA:139,5 +DA:144,5 +DA:148,5 +DA:149,2 +DA:152,2 +DA:153,1 +DA:154,1 +DA:155,1 +DA:156,3 +DA:161,0 +DA:163,0 +LF:69 +LH:60 +BRDA:43,0,0,8 +BRDA:43,0,1,7 +BRDA:66,1,0,6 +BRDA:111,2,0,0 +BRDA:111,2,1,6 +BRDA:112,3,0,0 +BRDA:112,3,1,6 +BRDA:148,4,0,2 +BRDA:148,4,1,3 +BRDA:152,5,0,1 +BRDA:152,5,1,1 +BRF:11 +BRH:9 +end_of_record +TN: +SF:src/core/package/packageCreators/CreateDataPackageImpl.ts +FN:12,(anonymous_7) +FN:22,(anonymous_8) +FN:26,(anonymous_9) +FN:29,(anonymous_10) +FN:31,(anonymous_11) +FN:33,(anonymous_12) +FN:41,(anonymous_13) +FN:45,(anonymous_14) +FN:49,(anonymous_15) +FN:51,(anonymous_16) +FN:54,(anonymous_17) +FNF:11 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:8,1 +DA:9,1 +DA:11,1 +DA:13,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:19,0 +DA:23,0 +DA:27,0 +DA:29,0 +DA:31,0 +DA:33,0 +DA:35,0 +DA:37,0 +DA:38,0 +DA:42,0 +DA:55,0 +DA:60,0 +DA:61,0 +DA:65,0 +DA:69,0 +DA:75,0 +DA:81,0 +LF:30 +LH:8 +BRDA:35,0,0,0 +BRDA:37,1,0,0 +BRDA:37,1,1,0 +BRDA:37,2,0,0 +BRDA:37,2,1,0 +BRDA:60,3,0,0 +BRDA:61,4,0,0 +BRDA:64,5,0,0 +BRDA:64,5,1,0 +BRF:9 +BRH:0 +end_of_record +TN: +SF:src/core/package/packageCreators/CreateDiffPackageImpl.ts +FN:21,(anonymous_7) +FN:31,(anonymous_8) +FN:35,(anonymous_9) +FN:37,(anonymous_10) +FN:41,(anonymous_11) +FN:62,(anonymous_12) +FN:68,(anonymous_13) +FN:109,(anonymous_14) +FN:111,(anonymous_15) +FN:141,(anonymous_16) +FN:147,getPackagesToCommits +FN:162,(anonymous_18) +FN:168,(anonymous_19) +FN:180,(anonymous_20) +FN:239,getOnlyChangedClassesFromPackage +FN:254,(anonymous_22) +FN:255,(anonymous_23) +FN:256,(anonymous_24) +FN:257,(anonymous_25) +FN:258,(anonymous_26) +FN:262,(anonymous_27) +FN:266,(anonymous_28) +FNF:22 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,getPackagesToCommits +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,getOnlyChangedClassesFromPackage +FNDA:0,(anonymous_22) +FNDA:0,(anonymous_23) +FNDA:0,(anonymous_24) +FNDA:0,(anonymous_25) +FNDA:0,(anonymous_26) +FNDA:0,(anonymous_27) +FNDA:0,(anonymous_28) +DA:1,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:10,1 +DA:11,1 +DA:12,1 +DA:13,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:18,1 +DA:20,1 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:28,0 +DA:32,0 +DA:38,0 +DA:42,0 +DA:45,0 +DA:48,0 +DA:50,0 +DA:52,0 +DA:56,0 +DA:58,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:72,0 +DA:79,0 +DA:82,0 +DA:83,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:94,0 +DA:96,0 +DA:102,0 +DA:116,0 +DA:117,0 +DA:120,0 +DA:123,0 +DA:126,0 +DA:127,0 +DA:131,0 +DA:134,0 +DA:137,0 +DA:138,0 +DA:142,0 +DA:143,0 +DA:145,0 +DA:148,0 +DA:149,0 +DA:154,0 +DA:155,0 +DA:157,0 +DA:162,0 +DA:163,0 +DA:168,0 +DA:170,0 +DA:174,0 +DA:176,0 +DA:185,0 +DA:187,0 +DA:192,0 +DA:197,0 +DA:200,0 +DA:207,0 +DA:209,0 +DA:211,0 +DA:212,0 +DA:213,0 +DA:217,0 +DA:218,0 +DA:219,0 +DA:220,0 +DA:222,0 +DA:226,0 +DA:229,0 +DA:230,0 +DA:231,0 +DA:232,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:236,0 +DA:245,0 +DA:250,0 +DA:254,0 +DA:255,0 +DA:256,0 +DA:257,0 +DA:258,0 +DA:261,0 +DA:262,0 +DA:266,0 +DA:268,0 +LF:103 +LH:16 +BRDA:82,0,0,0 +BRDA:82,0,1,0 +BRDA:120,1,0,0 +BRDA:137,2,0,0 +BRDA:174,3,0,0 +BRDA:244,4,0,0 +BRDA:244,4,1,0 +BRDA:262,5,0,0 +BRDA:262,5,1,0 +BRF:9 +BRH:0 +end_of_record +TN: +SF:src/core/package/packageCreators/CreatePackage.ts +FN:9,(anonymous_7) +FN:20,(anonymous_8) +FN:49,(anonymous_9) +FN:81,(anonymous_10) +FN:95,(anonymous_11) +FN:105,(anonymous_12) +FN:121,(anonymous_13) +FNF:7 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +DA:1,1 +DA:2,1 +DA:3,1 +DA:6,1 +DA:10,0 +DA:11,0 +DA:12,0 +DA:13,0 +DA:14,0 +DA:17,0 +DA:22,0 +DA:25,0 +DA:28,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:35,0 +DA:38,0 +DA:40,0 +DA:50,0 +DA:52,0 +DA:57,0 +DA:58,0 +DA:63,0 +DA:69,0 +DA:74,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:97,0 +DA:98,0 +DA:99,0 +DA:106,0 +DA:112,0 +DA:117,0 +DA:118,0 +DA:122,0 +DA:123,0 +DA:128,0 +DA:134,0 +DA:140,0 +DA:142,0 +LF:45 +LH:4 +BRDA:17,0,0,0 +BRDA:57,1,0,0 +BRDA:57,2,0,0 +BRDA:57,2,1,0 +BRDA:83,3,0,0 +BRDA:83,3,1,0 +BRDA:89,4,0,0 +BRDA:89,4,1,0 +BRDA:97,5,0,0 +BRDA:97,5,1,0 +BRF:10 +BRH:0 +end_of_record +TN: +SF:src/core/package/propertyFetchers/AssignPermissionSetFetcher.ts +FN:6,(anonymous_0) +FNF:1 +FNH:1 +FNDA:4,(anonymous_0) +DA:5,2 +DA:8,4 +DA:9,4 +DA:11,0 +DA:15,4 +DA:16,4 +DA:18,0 +DA:21,4 +LF:8 +LH:6 +BRDA:8,0,0,4 +BRDA:8,0,1,0 +BRDA:15,1,0,4 +BRDA:15,1,1,0 +BRF:4 +BRH:2 +end_of_record +TN: +SF:src/core/package/propertyFetchers/DestructiveManifestPathFetcher.ts +FN:8,(anonymous_7) +FNF:1 +FNH:1 +FNDA:4,(anonymous_7) +DA:1,2 +DA:4,2 +DA:7,2 +DA:12,0 +DA:16,4 +DA:17,4 +DA:22,4 +DA:25,0 +DA:27,4 +LF:9 +LH:7 +BRDA:11,0,0,4 +BRDA:11,0,1,4 +BRF:2 +BRH:2 +end_of_record +TN: +SF:src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.ts +FN:5,(anonymous_0) +FNF:1 +FNH:1 +FNDA:2,(anonymous_0) +DA:4,2 +DA:7,2 +LF:2 +LH:2 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/package/validators/PackageEmptyChecker.ts +FN:7,(anonymous_1) +FN:60,(anonymous_2) +FN:71,(anonymous_3) +FNF:3 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:6,1 +DA:16,0 +DA:21,0 +DA:22,0 +DA:23,0 +DA:29,0 +DA:30,0 +DA:32,0 +DA:33,0 +DA:35,0 +DA:38,0 +DA:39,0 +DA:41,0 +DA:42,0 +DA:44,0 +DA:46,0 +DA:47,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:56,0 +DA:64,0 +DA:66,0 +DA:69,0 +DA:71,0 +DA:74,0 +DA:75,0 +DA:78,0 +DA:82,0 +DA:83,0 +LF:37 +LH:5 +BRDA:21,0,0,0 +BRDA:21,0,1,0 +BRDA:50,1,0,0 +BRDA:50,1,1,0 +BRDA:52,2,0,0 +BRDA:52,2,1,0 +BRDA:74,3,0,0 +BRDA:74,3,1,0 +BRDA:82,4,0,0 +BRDA:82,4,1,0 +BRDA:82,5,0,0 +BRDA:82,5,1,0 +BRF:12 +BRH:0 +end_of_record +TN: +SF:src/core/package/version/Package2VersionFetcher.ts +FN:12,(anonymous_1) +FN:22,(anonymous_2) +FN:51,(anonymous_3) +FN:59,(anonymous_4) +FN:69,(anonymous_5) +FNF:5 +FNH:4 +FNDA:8,(anonymous_1) +FNDA:4,(anonymous_2) +FNDA:6,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:2,(anonymous_5) +DA:2,4 +DA:3,4 +DA:8,4 +DA:9,8 +DA:12,8 +DA:27,4 +DA:29,4 +DA:33,4 +DA:35,4 +DA:36,4 +DA:37,4 +DA:38,4 +DA:41,4 +DA:43,4 +DA:44,4 +DA:47,4 +DA:50,4 +DA:51,3 +DA:52,6 +DA:53,6 +DA:54,6 +DA:56,1 +DA:60,0 +DA:62,0 +DA:63,0 +DA:65,0 +DA:66,0 +DA:75,2 +DA:77,2 +DA:80,1 +DA:81,1 +DA:82,1 +DA:83,1 +DA:85,2 +DA:87,2 +DA:88,2 +DA:90,2 +DA:91,2 +LF:38 +LH:33 +BRDA:35,0,0,4 +BRDA:36,1,0,4 +BRDA:37,2,0,4 +BRDA:38,3,0,0 +BRDA:41,4,0,4 +BRDA:50,5,0,3 +BRDA:50,5,1,1 +BRDA:81,6,0,1 +BRDA:82,7,0,1 +BRDA:83,8,0,1 +BRF:10 +BRH:9 +end_of_record +TN: +SF:src/core/package/version/PackageVersionUpdater.ts +FN:4,(anonymous_0) +FN:6,(anonymous_1) +FNF:2 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +DA:3,1 +DA:8,0 +DA:10,0 +DA:11,0 +DA:13,0 +DA:14,0 +DA:15,0 +LF:7 +LH:1 +BRDA:13,0,0,0 +BRDA:13,0,1,0 +BRF:2 +BRH:0 +end_of_record +TN: +SF:src/core/permsets/AssignPermissionSetsImpl.ts +FN:9,(anonymous_7) +FN:16,(anonymous_8) +FN:39,(anonymous_9) +FN:81,(anonymous_10) +FN:87,(anonymous_11) +FNF:5 +FNH:5 +FNDA:3,(anonymous_7) +FNDA:3,(anonymous_8) +FNDA:12,(anonymous_9) +FNDA:4,(anonymous_10) +FNDA:6,(anonymous_11) +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:8,1 +DA:10,3 +DA:11,3 +DA:12,3 +DA:13,3 +DA:26,3 +DA:27,3 +DA:32,3 +DA:36,3 +DA:39,6 +DA:40,12 +DA:45,0 +DA:46,0 +DA:50,6 +DA:59,6 +DA:60,6 +DA:61,3 +DA:62,3 +DA:64,0 +DA:69,2 +DA:70,2 +DA:74,2 +DA:75,2 +DA:78,3 +DA:82,4 +DA:87,4 +DA:88,6 +DA:91,4 +LF:33 +LH:30 +BRDA:60,0,0,3 +BRDA:60,0,1,3 +BRF:2 +BRH:2 +end_of_record +TN: +SF:src/core/permsets/PermissionSetFetcher.ts +FN:8,(anonymous_1) +FN:10,(anonymous_2) +FNF:2 +FNH:2 +FNDA:3,(anonymous_1) +FNDA:3,(anonymous_2) +DA:2,1 +DA:7,1 +DA:8,3 +DA:11,3 +DA:13,3 +LF:5 +LH:5 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/permsets/PermissionSetGroupUpdateAwaiter.ts +FN:9,(anonymous_7) +FN:11,(anonymous_8) +FNF:2 +FNH:2 +FNDA:1,(anonymous_7) +FNDA:1,(anonymous_8) +DA:2,1 +DA:3,1 +DA:4,1 +DA:6,1 +DA:8,1 +DA:9,1 +DA:12,1 +DA:19,1 +DA:21,0 +DA:26,0 +DA:31,0 +DA:33,1 +DA:38,1 +DA:41,0 +DA:42,0 +LF:15 +LH:10 +BRDA:9,0,0,1 +BRF:1 +BRH:1 +end_of_record +TN: +SF:src/core/project/ProjectConfig.ts +FN:16,(anonymous_1) +FN:28,(anonymous_2) +FN:31,(anonymous_3) +FN:38,(anonymous_4) +FN:44,(anonymous_5) +FN:48,(anonymous_6) +FN:63,(anonymous_7) +FN:65,(anonymous_8) +FN:72,(anonymous_9) +FN:85,(anonymous_10) +FN:88,(anonymous_11) +FN:95,(anonymous_12) +FN:97,(anonymous_13) +FN:108,(anonymous_14) +FN:129,(anonymous_15) +FN:150,(anonymous_16) +FN:163,(anonymous_17) +FN:167,(anonymous_18) +FN:183,(anonymous_19) +FN:190,(anonymous_20) +FN:206,(anonymous_21) +FN:217,(anonymous_22) +FN:242,(anonymous_23) +FN:263,(anonymous_24) +FN:270,(anonymous_25) +FN:275,(anonymous_26) +FNF:26 +FNH:20 +FNDA:2,(anonymous_1) +FNDA:1,(anonymous_2) +FNDA:5,(anonymous_3) +FNDA:2,(anonymous_4) +FNDA:3,(anonymous_5) +FNDA:15,(anonymous_6) +FNDA:1,(anonymous_7) +FNDA:5,(anonymous_8) +FNDA:7,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:13,(anonymous_12) +FNDA:60,(anonymous_13) +FNDA:4,(anonymous_14) +FNDA:13,(anonymous_15) +FNDA:1,(anonymous_16) +FNDA:24,(anonymous_17) +FNDA:123,(anonymous_18) +FNDA:1,(anonymous_19) +FNDA:5,(anonymous_20) +FNDA:1,(anonymous_21) +FNDA:1,(anonymous_22) +FNDA:0,(anonymous_23) +FNDA:0,(anonymous_24) +FNDA:0,(anonymous_25) +FNDA:0,(anonymous_26) +DA:1,5 +DA:3,5 +DA:4,5 +DA:5,5 +DA:10,5 +DA:18,1 +DA:20,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:33,5 +DA:35,1 +DA:41,2 +DA:42,2 +DA:43,2 +DA:44,2 +DA:45,3 +DA:49,15 +DA:54,1 +DA:56,2 +DA:64,1 +DA:65,1 +DA:67,5 +DA:69,1 +DA:75,7 +DA:76,7 +DA:79,35 +DA:82,7 +DA:86,0 +DA:87,0 +DA:88,0 +DA:90,0 +DA:92,0 +DA:96,13 +DA:97,13 +DA:99,60 +DA:101,13 +DA:112,0 +DA:114,4 +DA:118,4 +DA:120,0 +DA:133,13 +DA:136,7 +DA:138,6 +DA:139,5 +DA:141,5 +DA:151,1 +DA:153,1 +DA:155,1 +DA:167,24 +DA:169,23 +DA:174,24 +DA:176,23 +DA:187,1 +DA:190,1 +DA:192,1 +DA:193,1 +DA:197,1 +DA:198,1 +DA:207,1 +DA:209,1 +DA:219,1 +DA:222,4 +DA:226,0 +DA:229,0 +DA:233,1 +DA:234,1 +DA:243,0 +DA:244,0 +DA:248,0 +DA:249,0 +DA:253,0 +DA:254,0 +DA:255,0 +DA:264,0 +DA:265,0 +DA:274,0 +DA:275,0 +DA:276,0 +DA:279,0 +LF:80 +LH:58 +BRDA:33,0,0,5 +BRDA:33,1,0,5 +BRDA:33,1,1,5 +BRDA:43,2,0,2 +BRDA:43,2,1,0 +BRDA:45,3,0,1 +BRDA:67,4,0,5 +BRDA:67,5,0,5 +BRDA:67,5,1,5 +BRDA:90,6,0,0 +BRDA:90,7,0,0 +BRDA:90,7,1,0 +BRDA:99,8,0,60 +BRDA:99,9,0,60 +BRDA:99,9,1,60 +BRDA:138,10,0,1 +BRDA:138,10,1,5 +BRDA:139,11,0,0 +BRDA:139,11,1,5 +BRDA:174,12,0,1 +BRDA:197,13,0,0 +BRDA:197,13,1,1 +BRF:22 +BRH:16 +end_of_record +TN: +SF:src/core/project/UserDefinedExternalDependency.ts +FN:13,(anonymous_1) +FN:23,(anonymous_2) +FN:44,(anonymous_3) +FNF:3 +FNH:3 +FNDA:9,(anonymous_1) +FNDA:2,(anonymous_2) +FNDA:7,(anonymous_3) +DA:1,2 +DA:2,2 +DA:3,2 +DA:4,2 +DA:10,2 +DA:14,9 +DA:15,9 +DA:16,9 +DA:17,9 +DA:20,0 +DA:24,2 +DA:25,2 +DA:26,2 +DA:28,2 +DA:30,2 +DA:33,2 +DA:37,0 +DA:40,2 +DA:41,2 +DA:45,7 +DA:47,7 +DA:50,0 +DA:55,0 +DA:59,7 +LF:24 +LH:20 +BRDA:14,0,0,9 +BRDA:14,0,1,0 +BRDA:52,1,0,7 +BRDA:52,1,1,7 +BRF:4 +BRH:3 +end_of_record +TN: +SF:src/core/queryHelper/ChunkCollection.ts +FN:9,chunkCollection +FNF:1 +FNH:1 +FNDA:3,chunkCollection +DA:9,2 +DA:10,3 +DA:11,3 +DA:13,3 +DA:14,3 +DA:17,1 +DA:20,7 +DA:22,6 +DA:23,6 +DA:25,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:35,2 +DA:37,2 +LF:16 +LH:16 +BRDA:9,0,0,1 +BRDA:9,1,0,1 +BRF:2 +BRH:2 +end_of_record +TN: +SF:src/core/queryHelper/QueryHelper.ts +FN:6,(anonymous_0) +FN:8,(anonymous_1) +FNF:2 +FNH:2 +FNDA:21,(anonymous_0) +FNDA:27,(anonymous_1) +DA:3,11 +DA:5,11 +DA:7,21 +DA:10,27 +DA:11,20 +DA:13,19 +LF:6 +LH:6 +BRDA:10,0,0,7 +BRDA:10,0,1,20 +BRF:2 +BRH:2 +end_of_record +TN: +SF:src/core/stats/NativeMetricSender.ts +FN:4,(anonymous_0) +FN:12,(anonymous_1) +FNF:2 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +DA:3,1 +DA:4,0 +DA:14,0 +DA:16,0 +DA:18,0 +DA:20,0 +LF:6 +LH:1 +BRDA:13,0,0,0 +BRDA:13,0,1,0 +BRF:2 +BRH:0 +end_of_record +TN: +SF:src/core/stats/SFPStatsSender.ts +FN:15,(anonymous_7) +FN:25,(anonymous_8) +FN:47,(anonymous_9) +FN:56,(anonymous_10) +FN:74,(anonymous_11) +FN:92,(anonymous_12) +FN:109,(anonymous_13) +FNF:7 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +DA:1,1 +DA:2,1 +DA:3,1 +DA:5,1 +DA:7,1 +DA:8,1 +DA:10,1 +DA:16,0 +DA:22,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:43,0 +DA:49,0 +DA:50,0 +DA:52,0 +DA:57,0 +DA:61,0 +DA:64,0 +DA:71,0 +DA:75,0 +DA:79,0 +DA:82,0 +DA:89,0 +DA:93,0 +DA:97,0 +DA:100,0 +DA:106,0 +DA:111,0 +LF:35 +LH:7 +BRDA:18,0,0,0 +BRDA:18,0,1,0 +BRDA:19,1,0,0 +BRDA:19,1,1,0 +BRDA:27,2,0,0 +BRDA:27,2,1,0 +BRDA:27,2,2,0 +BRDA:27,2,3,0 +BRDA:57,3,0,0 +BRDA:75,4,0,0 +BRDA:93,5,0,0 +BRF:11 +BRH:0 +end_of_record +TN: +SF:src/core/stats/nativeMetricSenderImpl/DataDogMetricSender.ts +FN:6,(anonymous_6) +FN:12,(anonymous_7) +FN:25,(anonymous_8) +FN:39,(anonymous_9) +FNF:4 +FNH:0 +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +DA:1,1 +DA:2,1 +DA:3,1 +DA:5,1 +DA:7,0 +DA:14,0 +DA:21,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:31,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:45,0 +LF:15 +LH:4 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/stats/nativeMetricSenderImpl/NewRelicMetricSender.ts +FN:12,(anonymous_6) +FN:19,(anonymous_7) +FN:27,(anonymous_8) +FN:33,(anonymous_9) +FN:46,(anonymous_10) +FN:54,(anonymous_11) +FNF:6 +FNH:0 +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +DA:1,1 +DA:3,1 +DA:9,1 +DA:11,1 +DA:13,0 +DA:21,0 +DA:23,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:33,0 +DA:35,0 +DA:37,0 +DA:38,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:54,0 +DA:56,0 +DA:58,0 +DA:59,0 +LF:26 +LH:4 +BRDA:37,0,0,0 +BRDA:58,1,0,0 +BRF:2 +BRH:0 +end_of_record +TN: +SF:src/core/stats/nativeMetricSenderImpl/SplunkMetricSender.ts +FN:8,(anonymous_7) +FN:14,(anonymous_8) +FN:21,(anonymous_9) +FN:25,(anonymous_10) +FN:26,(anonymous_11) +FN:35,(anonymous_12) +FN:39,(anonymous_13) +FN:40,(anonymous_14) +FNF:8 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +DA:1,1 +DA:2,1 +DA:3,1 +DA:7,1 +DA:9,0 +DA:15,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:27,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:41,0 +LF:16 +LH:4 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/utils/AliasList.ts +FN:4,convertAliasToUsername +FN:10,convertUsernameToAlias +FNF:2 +FNH:0 +FNDA:0,convertAliasToUsername +FNDA:0,convertUsernameToAlias +DA:1,4 +DA:4,4 +DA:5,0 +DA:6,0 +DA:7,0 +DA:10,4 +DA:12,0 +DA:13,0 +DA:14,0 +LF:9 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/utils/ChunkArray.ts +FN:1,chunkArray +FNF:1 +FNH:1 +FNDA:2,chunkArray +DA:1,1 +DA:2,2 +DA:3,2 +DA:4,2 +DA:7,9 +DA:10,2 +LF:6 +LH:6 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/utils/Delay.ts +FN:1,delay +FN:2,(anonymous_1) +FNF:2 +FNH:0 +FNDA:0,delay +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,0 +LF:2 +LH:1 +BRDA:1,0,0,0 +BRF:1 +BRH:0 +end_of_record +TN: +SF:src/core/utils/FileSystem.ts +FN:11,(anonymous_0) +FN:20,readdirRecursiveHandler +FN:23,(anonymous_2) +FNF:3 +FNH:3 +FNDA:4,(anonymous_0) +FNDA:24,readdirRecursiveHandler +FNDA:44,(anonymous_2) +DA:1,2 +DA:2,2 +DA:4,2 +DA:16,4 +DA:18,4 +DA:20,4 +DA:21,24 +DA:23,24 +DA:26,22 +DA:28,22 +DA:34,5 +DA:36,5 +DA:39,20 +DA:42,12 +DA:44,12 +DA:50,4 +LF:16 +LH:16 +BRDA:13,0,0,0 +BRDA:14,1,0,0 +BRDA:18,2,0,0 +BRF:3 +BRH:0 +end_of_record +TN: +SF:src/core/utils/Fileutils.ts +FN:15,(anonymous_0) +FN:16,(anonymous_1) +FN:31,(anonymous_2) +FN:40,(anonymous_3) +FN:55,(anonymous_4) +FN:70,(anonymous_5) +FN:85,(anonymous_6) +FN:90,(anonymous_7) +FN:107,(anonymous_8) +FN:123,(anonymous_9) +FN:133,(anonymous_10) +FN:146,(anonymous_11) +FN:177,(anonymous_12) +FN:179,(anonymous_13) +FN:195,(anonymous_14) +FNF:15 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:6,1 +DA:8,1 +DA:10,1 +DA:16,0 +DA:19,0 +DA:21,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:36,0 +DA:37,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:45,0 +DA:48,0 +DA:49,0 +DA:52,0 +DA:56,0 +DA:57,0 +DA:59,0 +DA:60,0 +DA:63,0 +DA:71,0 +DA:72,0 +DA:74,0 +DA:75,0 +DA:77,0 +DA:86,0 +DA:87,0 +DA:88,0 +DA:90,0 +DA:91,0 +DA:93,0 +DA:96,0 +DA:99,0 +DA:108,0 +DA:109,0 +DA:111,0 +DA:113,0 +DA:115,0 +DA:124,0 +DA:126,0 +DA:127,0 +DA:129,0 +DA:131,0 +DA:133,0 +DA:134,0 +DA:137,0 +DA:147,0 +DA:148,0 +DA:150,0 +DA:151,0 +DA:153,0 +DA:154,0 +DA:155,0 +DA:157,0 +DA:159,0 +DA:161,0 +DA:165,0 +DA:170,0 +DA:179,0 +DA:180,0 +DA:184,0 +DA:188,0 +DA:192,0 +DA:196,0 +DA:197,0 +DA:198,0 +DA:199,0 +DA:200,0 +DA:202,0 +LF:77 +LH:7 +BRDA:31,0,0,0 +BRDA:44,1,0,0 +BRDA:44,1,1,0 +BRDA:85,2,0,0 +BRDA:85,3,0,0 +BRDA:87,4,0,0 +BRDA:87,4,1,0 +BRDA:88,5,0,0 +BRDA:88,5,1,0 +BRDA:95,6,0,0 +BRDA:95,6,1,0 +BRDA:95,6,2,0 +BRF:12 +BRH:0 +end_of_record +TN: +SF:src/core/utils/ObjectCRUDHelper.ts +FN:8,(anonymous_0) +FN:10,(anonymous_1) +FN:28,(anonymous_2) +FN:30,(anonymous_3) +FNF:4 +FNH:2 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:3,(anonymous_2) +FNDA:3,(anonymous_3) +DA:3,4 +DA:5,4 +DA:7,4 +DA:9,0 +DA:11,0 +DA:12,0 +DA:13,0 +DA:16,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:29,3 +DA:31,3 +DA:32,3 +DA:33,1 +LF:16 +LH:7 +BRDA:12,0,0,0 +BRDA:12,0,1,0 +BRDA:19,1,0,0 +BRDA:19,1,1,0 +BRDA:21,2,0,0 +BRDA:21,2,1,0 +BRDA:32,3,0,2 +BRDA:32,3,1,1 +BRF:8 +BRH:2 +end_of_record +TN: +SF:src/core/utils/VersionNumberConverter.ts +FN:6,convertBuildNumDotDelimToHyphen +FN:22,getIndexOfBuildNumDelimeter +FNF:2 +FNH:2 +FNDA:103,convertBuildNumDotDelimToHyphen +FNDA:103,getIndexOfBuildNumDelimeter +DA:6,2 +DA:7,103 +DA:9,103 +DA:11,103 +DA:14,103 +DA:23,103 +DA:24,103 +DA:26,309 +DA:29,103 +DA:32,0 +LF:10 +LH:9 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/utils/extractDomainFromUrl.ts +FN:6,extractDomainFromUrl +FNF:1 +FNH:1 +FNDA:7,extractDomainFromUrl +DA:6,1 +DA:7,7 +DA:8,4 +DA:9,4 +LF:4 +LH:4 +BRDA:7,0,0,3 +BRDA:9,1,0,4 +BRDA:9,1,1,3 +BRF:3 +BRH:3 +end_of_record +TN: +SF:src/core/utils/xml2json.ts +FN:3,xml2json +FN:4,(anonymous_1) +FN:5,(anonymous_2) +FNF:3 +FNH:3 +FNDA:12,xml2json +FNDA:12,(anonymous_1) +FNDA:12,(anonymous_2) +DA:1,4 +DA:3,4 +DA:4,12 +DA:5,12 +DA:6,12 +DA:7,12 +LF:6 +LH:6 +BRDA:6,0,0,0 +BRDA:6,0,1,12 +BRF:2 +BRH:1 +end_of_record +TN: +SF:src/impl/changelog/CommitUpdater.ts +FN:6,(anonymous_0) +FN:18,(anonymous_1) +FN:30,(anonymous_2) +FNF:3 +FNH:3 +FNDA:4,(anonymous_0) +FNDA:4,(anonymous_1) +FNDA:76,(anonymous_2) +DA:5,1 +DA:7,4 +DA:8,4 +DA:9,4 +DA:10,4 +DA:20,10 +DA:25,10 +DA:29,6 +DA:30,76 +DA:33,2 +DA:38,2 +DA:39,2 +DA:40,2 +DA:45,4 +DA:48,0 +DA:50,0 +DA:53,4 +LF:17 +LH:15 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/impl/changelog/OrgsUpdater.ts +FN:8,(anonymous_0) +FN:21,(anonymous_1) +FN:24,(anonymous_2) +FN:58,(anonymous_3) +FN:62,(anonymous_4) +FN:119,(anonymous_5) +FNF:6 +FNH:6 +FNDA:4,(anonymous_0) +FNDA:4,(anonymous_1) +FNDA:1,(anonymous_2) +FNDA:3,(anonymous_3) +FNDA:2,(anonymous_4) +FNDA:6,(anonymous_5) +DA:2,1 +DA:4,1 +DA:9,4 +DA:10,4 +DA:11,4 +DA:12,4 +DA:14,4 +DA:17,2 +DA:24,2 +DA:27,1 +DA:28,1 +DA:29,1 +DA:31,1 +DA:40,0 +DA:49,2 +DA:58,3 +DA:61,2 +DA:62,2 +DA:67,0 +DA:68,0 +DA:71,1 +DA:75,1 +DA:76,1 +DA:79,1 +DA:81,0 +DA:85,1 +DA:86,1 +DA:89,2 +DA:98,0 +DA:104,0 +DA:120,6 +DA:121,6 +LF:32 +LH:26 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/impl/changelog/WorkItemUpdater.ts +FN:6,(anonymous_6) +FN:11,(anonymous_7) +FNF:2 +FNH:2 +FNDA:1,(anonymous_6) +FNDA:1,(anonymous_7) +DA:1,1 +DA:5,1 +DA:6,1 +DA:14,2 +DA:15,2 +DA:19,24 +DA:20,24 +DA:24,3 +DA:25,3 +DA:27,1 +DA:38,3 +LF:11 +LH:11 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/impl/dependency/ShrinkImpl.ts +FN:14,(anonymous_7) +FN:15,(anonymous_8) +FN:32,(anonymous_9) +FN:73,(anonymous_10) +FN:74,(anonymous_11) +FNF:5 +FNH:5 +FNDA:2,(anonymous_7) +FNDA:2,(anonymous_8) +FNDA:2,(anonymous_9) +FNDA:12,(anonymous_10) +FNDA:72,(anonymous_11) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:6,1 +DA:7,1 +DA:9,1 +DA:14,2 +DA:16,2 +DA:18,2 +DA:20,2 +DA:24,2 +DA:25,2 +DA:27,2 +DA:29,2 +DA:33,2 +DA:36,12 +DA:41,12 +DA:42,12 +DA:45,22 +DA:53,36 +DA:55,24 +DA:60,18 +DA:68,12 +DA:74,12 +DA:76,10 +LF:26 +LH:26 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/impl/parallelBuilder/BuildCollections.ts +FN:11,(anonymous_1) +FN:15,(anonymous_2) +FN:23,(anonymous_3) +FN:32,(anonymous_4) +FN:36,(anonymous_5) +FN:58,(anonymous_6) +FN:62,(anonymous_7) +FNF:7 +FNH:5 +FNDA:4,(anonymous_1) +FNDA:2,(anonymous_2) +FNDA:4,(anonymous_3) +FNDA:6,(anonymous_4) +FNDA:9,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +DA:1,1 +DA:2,1 +DA:7,1 +DA:12,4 +DA:16,2 +DA:24,4 +DA:26,4 +DA:29,7 +DA:30,6 +DA:32,6 +DA:36,9 +DA:37,2 +DA:39,1 +DA:44,5 +DA:47,1 +DA:59,0 +DA:63,0 +LF:17 +LH:15 +BRDA:29,0,0,6 +BRDA:29,0,1,1 +BRDA:30,1,0,6 +BRDA:36,2,0,2 +BRDA:36,2,1,1 +BRDA:63,3,0,0 +BRDA:63,3,1,0 +BRF:7 +BRH:5 +end_of_record +TN: +SF:src/impl/parallelBuilder/UndirectedGraph.ts +FN:4,(anonymous_0) +FN:8,(anonymous_1) +FN:12,(anonymous_2) +FN:17,(anonymous_3) +FN:30,(anonymous_4) +FN:35,dfsHandler +FN:40,(anonymous_6) +FNF:7 +FNH:7 +FNDA:12,(anonymous_0) +FNDA:17,(anonymous_1) +FNDA:22,(anonymous_2) +FNDA:17,(anonymous_3) +FNDA:4,(anonymous_4) +FNDA:19,dfsHandler +FNDA:42,(anonymous_6) +DA:1,2 +DA:5,12 +DA:9,17 +DA:13,22 +DA:14,1 +DA:18,17 +DA:19,16 +DA:20,14 +DA:22,13 +DA:23,13 +DA:31,4 +DA:32,4 +DA:33,4 +DA:35,4 +DA:36,19 +DA:37,19 +DA:38,18 +DA:39,18 +DA:40,18 +DA:42,15 +DA:47,3 +LF:21 +LH:21 +BRDA:13,0,0,21 +BRDA:13,0,1,1 +BRDA:18,1,0,1 +BRDA:19,2,0,2 +BRDA:20,3,0,1 +BRDA:22,4,0,13 +BRDA:23,5,0,13 +BRDA:36,6,0,0 +BRDA:37,7,0,1 +BRF:9 +BRH:8 +end_of_record +TN: +SF:src/impl/release/ReleaseDefinition.ts +FN:12,(anonymous_1) +FN:16,(anonymous_2) +FN:28,(anonymous_3) +FN:48,(anonymous_4) +FN:54,(anonymous_5) +FN:68,(anonymous_6) +FNF:6 +FNH:4 +FNDA:0,(anonymous_1) +FNDA:5,(anonymous_2) +FNDA:5,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:5,(anonymous_5) +FNDA:6,(anonymous_6) +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:8,1 +DA:9,1 +DA:11,1 +DA:14,0 +DA:16,5 +DA:17,5 +DA:20,1 +DA:21,1 +DA:24,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:38,5 +DA:41,0 +DA:44,5 +DA:45,0 +DA:50,0 +DA:55,5 +DA:60,5 +DA:61,5 +DA:65,4 +DA:68,4 +DA:69,6 +DA:76,4 +LF:30 +LH:21 +BRDA:20,0,0,1 +BRDA:20,1,0,1 +BRDA:20,1,1,1 +BRF:3 +BRH:3 +end_of_record +TN: +SF:src/utils/Get18DigitSalesforceId.ts +FN:1,get18DigitSalesforceId +FNF:1 +FNH:0 +FNDA:0,get18DigitSalesforceId +DA:1,1 +DA:3,0 +DA:5,0 +DA:6,0 +DA:7,0 +DA:8,0 +DA:9,0 +DA:10,0 +DA:12,0 +DA:14,0 +DA:15,0 +DA:17,0 +LF:12 +LH:1 +BRDA:2,0,0,0 +BRDA:2,0,1,0 +BRDA:4,1,0,0 +BRDA:4,1,1,0 +BRDA:10,2,0,0 +BRDA:10,3,0,0 +BRDA:10,3,1,0 +BRF:7 +BRH:0 +end_of_record diff --git a/packages/sfpowerscripts-cli/messages/generate_changelog.json b/packages/sfpowerscripts-cli/messages/generate_changelog.json index fb6f78cf1..8844686d9 100644 --- a/packages/sfpowerscripts-cli/messages/generate_changelog.json +++ b/packages/sfpowerscripts-cli/messages/generate_changelog.json @@ -1,7 +1,7 @@ { "commandDescription": "Generates release changelog, providing a summary of artifact versions, work items and commits introduced in a release. Creates a release definition based on artifacts contained in the artifact directory, and compares it to previous release definition in changelog stored on a source repository", "limitFlagDescription": "limit the number of releases to display in changelog markdown", - "artifactDirectoryFlagDescription": "Directory containing sfpowerscripts artifacts", + "artifactDirectoryFlagDescription": "Directory containing sfp artifacts", "releaseNameFlagDescription": "Name of the release for which to generate changelog", "workItemFilterFlagDescription": "Regular expression used to search for work items (user stories) introduced in release", "workItemUrlFlagDescription": "Generic URL for work items. Each work item ID will be appended to the URL, providing quick access to work items", diff --git a/packages/sfpowerscripts-cli/messages/install_package.json b/packages/sfpowerscripts-cli/messages/install_package.json index 89db89720..2dfc9c318 100644 --- a/packages/sfpowerscripts-cli/messages/install_package.json +++ b/packages/sfpowerscripts-cli/messages/install_package.json @@ -1,5 +1,5 @@ { - "commandDescription": "Installs a sfpowerscripts artifact to an org", + "commandDescription": "Installs a sfp artifact to an org", "packageFlagDescription": "Name of the package to be installed", "targetOrgFlagDescription": "Alias/User Name of the target environment", "apexCompileOnlyPackageFlagDescription": "(unlocked) package installation triggers a compilation of apex, flag to trigger compilation of package only", diff --git a/packages/sfpowerscripts-cli/messages/install_source_package.json b/packages/sfpowerscripts-cli/messages/install_source_package.json index 11e743801..0d5be458a 100644 --- a/packages/sfpowerscripts-cli/messages/install_source_package.json +++ b/packages/sfpowerscripts-cli/messages/install_source_package.json @@ -1,5 +1,5 @@ { - "commandDescription": "(DEPRECATED) Installs a sfpowerscripts source package to the target org", + "commandDescription": "(DEPRECATED) Installs a sfp source package to the target org", "packageFlagDescription": "Name of the package to be installed", "targetOrgFlagDescription": "Alias/User Name of the target environment", "artifactDirectoryFlagDescription": "The directory where the artifact is located", diff --git a/packages/sfpowerscripts-cli/messages/install_unlocked_package.json b/packages/sfpowerscripts-cli/messages/install_unlocked_package.json index 454034c9d..ac93c9e9d 100644 --- a/packages/sfpowerscripts-cli/messages/install_unlocked_package.json +++ b/packages/sfpowerscripts-cli/messages/install_unlocked_package.json @@ -1,5 +1,5 @@ { - "commandDescription": "(DEPRECATED) Installs an unlocked package using sfpowerscripts metadata", + "commandDescription": "(DEPRECATED) Installs an unlocked package using sfp metadata", "packageFlagDescription": "Name of the package to be installed", "targetOrgFlagDescription": "Alias/User Name of the target environment", "packageInstalledFromFlagDescription": "automatically retrieve the version ID of the package to be installed, from the build artifact", diff --git a/packages/sfpowerscripts-cli/messages/metrics_report.json b/packages/sfpowerscripts-cli/messages/metrics_report.json index 77853e3af..9a2718a83 100644 --- a/packages/sfpowerscripts-cli/messages/metrics_report.json +++ b/packages/sfpowerscripts-cli/messages/metrics_report.json @@ -1,3 +1,3 @@ { - "commandDescription": "Report a custom metric to any sfpowerscripts supported metric provider" + "commandDescription": "Report a custom metric to any sfp supported metric provider" } diff --git a/packages/sfpowerscripts-cli/messages/pool_delete.json b/packages/sfpowerscripts-cli/messages/pool_delete.json index 1e558d567..a1ddb8c53 100644 --- a/packages/sfpowerscripts-cli/messages/pool_delete.json +++ b/packages/sfpowerscripts-cli/messages/pool_delete.json @@ -4,5 +4,5 @@ "mypoolDescription": "Filter only Scratch orgs created by current user in the pool", "allscratchorgsDescription": "Deletes all used and unused Scratch orgs from pool by the tag", "inprogressonlyDescription": "Deletes all In Progress Scratch orgs from pool by the tag", - "recoverOrphanedScratchOrgsDescription": "Recovers scratch orgs that were created by salesforce but were not tagged to sfpowerscripts due to timeouts etc." + "recoverOrphanedScratchOrgsDescription": "Recovers scratch orgs that were created by salesforce but were not tagged to sfp due to timeouts etc." } diff --git a/packages/sfpowerscripts-cli/package.json b/packages/sfpowerscripts-cli/package.json index 426418c15..c65d9f6bb 100644 --- a/packages/sfpowerscripts-cli/package.json +++ b/packages/sfpowerscripts-cli/package.json @@ -1,20 +1,33 @@ { - "name": "@dxatscale/sfpowerscripts", - "description": "DX@Scale Toolkit", - "version": "25.6.0", + "name": "@flxblio/sfp", + "description": "Flxbl Toolkit", + "version": "1.0.0", "license": "MIT", - "author": "dxatscale", + "author": "flxblio", "release": "January 24", "bin": { - "sfp": "./bin/run", - "sfpowerscripts": "./bin/run" + "sfp": "./bin/run" }, - "bugs": "https://github.com/dxatscale/sfpowerscripts/issues", + "bugs": "https://github.com/flxblio/sfp/issues", "dependencies": { - "@dxatscale/sfdx-process-wrapper": "^1.0.2", - "@dxatscale/sfp-logger": "^2.1.2", - "@dxatscale/sfpowerscripts.core": "^36.5.3", - "@dxatscale/sfprofiles": "^2.0.8", + "@flxblio/apexlink": "^1.0.2", + "@newrelic/telemetry-sdk": "^0.6.0", + "@salesforce/apex-node": "2.1.0", + "@salesforce/packaging": "2.3.3", + "@salesforce/source-deploy-retrieve": "9.7.24", + "@salesforce/source-tracking": "4.2.16", + "apex-parser": "2.13.0", + "axios": "^1.4.0", + "datadog-metrics": "^0.9.3", + "fast-xml-parser": "4.2.7", + "hot-shots": "^8.5.0", + "ignore": "^5.1.6", + "tar": "^6.1.9", + "tmp": "^0.2.1", + "xml2js": "^0.6.0", + "@flxblio/sfdx-process-wrapper": "^1.0.2", + "@flxblio/sfp-logger": "^2.1.2", + "@flxblio/sfprofiles": "^2.0.8", "@oclif/core": "2.11.8", "@oclif/plugin-commands": "^3.0.3", "@oclif/plugin-help": "5.2.17", @@ -61,6 +74,7 @@ "oclif": "^3.10.0", "ts-jest": "29.1.1", "ts-node": "10.7.0", + "@types/fs-extra": "11.0.4", "typescript": "^5" }, "engines": { @@ -74,10 +88,10 @@ "/resources", "/oclif.manifest.json" ], - "homepage": "https://github.com/dxatscale/sfpowerscripts", + "homepage": "https://github.com/flxblio/sfp", "keywords": [ - "dxatscale", - "sf-cli", + "flxblio", + "sfp-cli", "sfdx", "salesforce", "sf", @@ -93,7 +107,7 @@ "description": "Orchestrate packages from a monorepo through its lifecycle, driven by descriptors in your sfdx-project.json" }, "pool": { - "description": "Manage the pooled orgs created by the sfpowerscripts orchestrator in prepare stage" + "description": "Manage the pooled orgs created by the sfp orchestrator in prepare stage" }, "changelog": { "description": "Track your artifacts & user stories as they progress through different environments, with release changelogs" @@ -108,12 +122,12 @@ "description": "Commands to create and install unlocked packages" }, "source": { - "description": "Commands to create and install sfpowerscripts source packages" + "description": "Commands to create and install sfp source packages" } } }, "impact": { - "description": "Figures out the impact of various components of sfpowerscripts", + "description": "Figures out the impact of various components of sfp", "external": true }, "analyze": { @@ -148,8 +162,8 @@ }, "repository": { "type": "git", - "url": "https://github.com/dxatscale/sfpowerscripts.git", - "directory": "packages/sfpowerscripts-cli" + "url": "https://github.com/flxblio/sfp.git", + "directory": "packages/sfp-cli" }, "scripts": { "build": "pnpm run clean && pnpm run compile", diff --git a/packages/sfpowerscripts-cli/resources/metadatainfo.json b/packages/sfpowerscripts-cli/resources/metadatainfo.json new file mode 100644 index 000000000..abcbaba65 --- /dev/null +++ b/packages/sfpowerscripts-cli/resources/metadatainfo.json @@ -0,0 +1,1075 @@ +{ + "metadataObjects": [ + { + "directoryName": "installedPackages", + "inFolder": false, + "metaFile": false, + "suffix": "installedPackage", + "xmlName": "InstalledPackage" + }, + { + "childXmlNames": ["CustomLabel"], + "directoryName": "labels", + "inFolder": false, + "metaFile": false, + "suffix": "labels", + "xmlName": "CustomLabels" + }, + { + "directoryName": "staticresources", + "inFolder": false, + "metaFile": true, + "suffix": "resource", + "xmlName": "StaticResource" + }, + { + "directoryName": "scontrols", + "inFolder": false, + "metaFile": true, + "suffix": "scf", + "xmlName": "Scontrol" + }, + { + "directoryName": "certs", + "inFolder": false, + "metaFile": true, + "suffix": "crt", + "xmlName": "Certificate" + }, + { + "directoryName": "messageChannels", + "inFolder": false, + "metaFile": false, + "suffix": "messageChannel", + "xmlName": "LightningMessageChannel" + }, + { + "directoryName": "aura", + "inFolder": false, + "metaFile": false, + "xmlName": "AuraDefinitionBundle" + }, + { + "directoryName": "lwc", + "inFolder": false, + "metaFile": false, + "xmlName": "LightningComponentBundle" + }, + { + "directoryName": "components", + "inFolder": false, + "metaFile": true, + "suffix": "component", + "xmlName": "ApexComponent" + }, + { + "directoryName": "pages", + "inFolder": false, + "metaFile": true, + "suffix": "page", + "xmlName": "ApexPage" + }, + { + "directoryName": "queues", + "inFolder": false, + "metaFile": false, + "suffix": "queue", + "xmlName": "Queue" + }, + { + "directoryName": "CaseSubjectParticles", + "inFolder": false, + "metaFile": false, + "suffix": "CaseSubjectParticle", + "xmlName": "CaseSubjectParticle" + }, + { + "directoryName": "dataSources", + "inFolder": false, + "metaFile": false, + "suffix": "dataSource", + "xmlName": "ExternalDataSource" + }, + { + "directoryName": "namedCredentials", + "inFolder": false, + "metaFile": false, + "suffix": "namedCredential", + "xmlName": "NamedCredential" + }, + { + "directoryName": "externalServiceRegistrations", + "inFolder": false, + "metaFile": false, + "suffix": "externalServiceRegistration", + "xmlName": "ExternalServiceRegistration" + }, + { + "directoryName": "roles", + "inFolder": false, + "metaFile": false, + "suffix": "role", + "xmlName": "Role" + }, + { + "directoryName": "groups", + "inFolder": false, + "metaFile": false, + "suffix": "group", + "xmlName": "Group" + }, + { + "directoryName": "globalValueSets", + "inFolder": false, + "metaFile": false, + "suffix": "globalValueSet", + "xmlName": "GlobalValueSet" + }, + { + "directoryName": "standardValueSets", + "inFolder": false, + "metaFile": false, + "suffix": "standardValueSet", + "xmlName": "StandardValueSet" + }, + { + "directoryName": "customPermissions", + "inFolder": false, + "metaFile": false, + "suffix": "customPermission", + "xmlName": "CustomPermission" + }, + { + "childXmlNames": [ + "CustomField", + "Index", + "BusinessProcess", + "RecordType", + "CompactLayout", + "WebLink", + "ValidationRule", + "SharingReason", + "ListView", + "FieldSet" + ], + "directoryName": "objects", + "inFolder": false, + "metaFile": false, + "suffix": "object", + "xmlName": "CustomObject" + }, + { + "directoryName": "reportTypes", + "inFolder": false, + "metaFile": false, + "suffix": "reportType", + "xmlName": "ReportType" + }, + { + "directoryName": "reports", + "inFolder": true, + "metaFile": false, + "suffix": "report", + "xmlName": "Report" + }, + { + "directoryName": "dashboards", + "inFolder": true, + "metaFile": false, + "suffix": "dashboard", + "xmlName": "Dashboard" + }, + { + "directoryName": "analyticSnapshots", + "inFolder": false, + "metaFile": false, + "suffix": "snapshot", + "xmlName": "AnalyticSnapshot" + }, + { + "directoryName": "feedFilters", + "inFolder": false, + "metaFile": false, + "suffix": "feedFilter", + "xmlName": "CustomFeedFilter" + }, + { + "directoryName": "layouts", + "inFolder": false, + "metaFile": false, + "suffix": "layout", + "xmlName": "Layout" + }, + { + "directoryName": "documents", + "inFolder": true, + "metaFile": true, + "suffix": "document", + "xmlName": "Document" + }, + { + "directoryName": "weblinks", + "inFolder": false, + "metaFile": false, + "suffix": "weblink", + "xmlName": "CustomPageWebLink" + }, + { + "directoryName": "letterhead", + "inFolder": false, + "metaFile": false, + "suffix": "letter", + "xmlName": "Letterhead" + }, + { + "directoryName": "email", + "inFolder": true, + "metaFile": true, + "suffix": "email", + "xmlName": "EmailTemplate" + }, + { + "directoryName": "quickActions", + "inFolder": false, + "metaFile": false, + "suffix": "quickAction", + "xmlName": "QuickAction" + }, + { + "directoryName": "flexipages", + "inFolder": false, + "metaFile": false, + "suffix": "flexipage", + "xmlName": "FlexiPage" + }, + { + "directoryName": "tabs", + "inFolder": false, + "metaFile": false, + "suffix": "tab", + "xmlName": "CustomTab" + }, + { + "directoryName": "customApplicationComponents", + "inFolder": false, + "metaFile": false, + "suffix": "customApplicationComponent", + "xmlName": "CustomApplicationComponent" + }, + { + "directoryName": "applications", + "inFolder": false, + "metaFile": false, + "suffix": "app", + "xmlName": "CustomApplication" + }, + { + "directoryName": "customMetadata", + "inFolder": false, + "metaFile": false, + "suffix": "md", + "xmlName": "CustomMetadata" + }, + { + "directoryName": "flows", + "inFolder": false, + "metaFile": false, + "suffix": "flow", + "xmlName": "Flow" + }, + { + "directoryName": "flowDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "flowDefinition", + "xmlName": "FlowDefinition" + }, + { + "childXmlNames": [ + "WorkflowFieldUpdate", + "WorkflowKnowledgePublish", + "WorkflowTask", + "WorkflowAlert", + "WorkflowSend", + "WorkflowOutboundMessage", + "WorkflowRule" + ], + "directoryName": "workflows", + "inFolder": false, + "metaFile": false, + "suffix": "workflow", + "xmlName": "Workflow" + }, + { + "childXmlNames": ["AssignmentRule"], + "directoryName": "assignmentRules", + "inFolder": false, + "metaFile": false, + "suffix": "assignmentRules", + "xmlName": "AssignmentRules" + }, + { + "childXmlNames": ["AutoResponseRule"], + "directoryName": "autoResponseRules", + "inFolder": false, + "metaFile": false, + "suffix": "autoResponseRules", + "xmlName": "AutoResponseRules" + }, + { + "childXmlNames": ["EscalationRule"], + "directoryName": "escalationRules", + "inFolder": false, + "metaFile": false, + "suffix": "escalationRules", + "xmlName": "EscalationRules" + }, + { + "directoryName": "postTemplates", + "inFolder": false, + "metaFile": false, + "suffix": "postTemplate", + "xmlName": "PostTemplate" + }, + { + "directoryName": "approvalProcesses", + "inFolder": false, + "metaFile": false, + "suffix": "approvalProcess", + "xmlName": "ApprovalProcess" + }, + { + "directoryName": "homePageComponents", + "inFolder": false, + "metaFile": false, + "suffix": "homePageComponent", + "xmlName": "HomePageComponent" + }, + { + "directoryName": "homePageLayouts", + "inFolder": false, + "metaFile": false, + "suffix": "homePageLayout", + "xmlName": "HomePageLayout" + }, + { + "directoryName": "objectTranslations", + "inFolder": false, + "metaFile": false, + "suffix": "objectTranslation", + "xmlName": "CustomObjectTranslation" + }, + { + "directoryName": "objectTranslations", + "inFolder": false, + "metaFile": false, + "suffix": "fieldTranslation", + "xmlName": "CustomFieldTranslation" + }, + { + "directoryName": "translations", + "inFolder": false, + "metaFile": false, + "suffix": "translation", + "xmlName": "Translations" + }, + { + "directoryName": "globalValueSetTranslations", + "inFolder": false, + "metaFile": false, + "suffix": "globalValueSetTranslation", + "xmlName": "GlobalValueSetTranslation" + }, + { + "directoryName": "standardValueSetTranslations", + "inFolder": false, + "metaFile": false, + "suffix": "standardValueSetTranslation", + "xmlName": "StandardValueSetTranslation" + }, + { + "directoryName": "classes", + "inFolder": false, + "metaFile": true, + "suffix": "cls", + "xmlName": "ApexClass" + }, + { + "directoryName": "triggers", + "inFolder": false, + "metaFile": true, + "suffix": "trigger", + "xmlName": "ApexTrigger" + }, + { + "directoryName": "testSuites", + "inFolder": false, + "metaFile": false, + "suffix": "testSuite", + "xmlName": "ApexTestSuite" + }, + { + "directoryName": "profiles", + "inFolder": false, + "metaFile": false, + "suffix": "profile", + "xmlName": "Profile" + }, + { + "directoryName": "permissionsets", + "inFolder": false, + "metaFile": false, + "suffix": "permissionset", + "xmlName": "PermissionSet" + }, + { + "directoryName": "mutingpermissionsets", + "inFolder": false, + "metaFile": false, + "suffix": "mutingpermissionset", + "xmlName": "MutingPermissionSet" + }, + { + "directoryName": "permissionsetgroups", + "inFolder": false, + "metaFile": false, + "suffix": "permissionsetgroup", + "xmlName": "PermissionSetGroup" + }, + { + "directoryName": "profilePasswordPolicies", + "inFolder": false, + "metaFile": false, + "suffix": "profilePasswordPolicy", + "xmlName": "ProfilePasswordPolicy" + }, + { + "directoryName": "profileSessionSettings", + "inFolder": false, + "metaFile": false, + "suffix": "profileSessionSetting", + "xmlName": "ProfileSessionSetting" + }, + { + "directoryName": "myDomainDiscoverableLogins", + "inFolder": false, + "metaFile": false, + "suffix": "myDomainDiscoverableLogin", + "xmlName": "MyDomainDiscoverableLogin" + }, + { + "directoryName": "oauthcustomscopes", + "inFolder": false, + "metaFile": false, + "suffix": "oauthcustomscope", + "xmlName": "OauthCustomScope" + }, + { + "directoryName": "datacategorygroups", + "inFolder": false, + "metaFile": false, + "suffix": "datacategorygroup", + "xmlName": "DataCategoryGroup" + }, + { + "directoryName": "remoteSiteSettings", + "inFolder": false, + "metaFile": false, + "suffix": "remoteSite", + "xmlName": "RemoteSiteSetting" + }, + { + "directoryName": "cspTrustedSites", + "inFolder": false, + "metaFile": false, + "suffix": "cspTrustedSite", + "xmlName": "CspTrustedSite" + }, + { + "directoryName": "redirectWhitelistUrls", + "inFolder": false, + "metaFile": false, + "suffix": "redirectWhitelistUrl", + "xmlName": "RedirectWhitelistUrl" + }, + { + "childXmlNames": ["MatchingRule"], + "directoryName": "matchingRules", + "inFolder": false, + "metaFile": false, + "suffix": "matchingRule", + "xmlName": "MatchingRules" + }, + { + "directoryName": "duplicateRules", + "inFolder": false, + "metaFile": false, + "suffix": "duplicateRule", + "xmlName": "DuplicateRule" + }, + { + "directoryName": "cleanDataServices", + "inFolder": false, + "metaFile": false, + "suffix": "cleanDataService", + "xmlName": "CleanDataService" + }, + { + "directoryName": "skills", + "inFolder": false, + "metaFile": false, + "suffix": "skill", + "xmlName": "Skill" + }, + { + "directoryName": "serviceChannels", + "inFolder": false, + "metaFile": false, + "suffix": "serviceChannel", + "xmlName": "ServiceChannel" + }, + { + "directoryName": "queueRoutingConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "queueRoutingConfig", + "xmlName": "QueueRoutingConfig" + }, + { + "directoryName": "servicePresenceStatuses", + "inFolder": false, + "metaFile": false, + "suffix": "servicePresenceStatus", + "xmlName": "ServicePresenceStatus" + }, + { + "directoryName": "presenceDeclineReasons", + "inFolder": false, + "metaFile": false, + "suffix": "presenceDeclineReason", + "xmlName": "PresenceDeclineReason" + }, + { + "directoryName": "presenceUserConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "presenceUserConfig", + "xmlName": "PresenceUserConfig" + }, + { + "directoryName": "authproviders", + "inFolder": false, + "metaFile": false, + "suffix": "authprovider", + "xmlName": "AuthProvider" + }, + { + "directoryName": "eclair", + "inFolder": false, + "metaFile": true, + "suffix": "geodata", + "xmlName": "EclairGeoData" + }, + { + "directoryName": "sites", + "inFolder": false, + "metaFile": false, + "suffix": "site", + "xmlName": "CustomSite" + }, + { + "directoryName": "channelLayouts", + "inFolder": false, + "metaFile": false, + "suffix": "channelLayout", + "xmlName": "ChannelLayout" + }, + { + "directoryName": "contentassets", + "inFolder": false, + "metaFile": true, + "suffix": "asset", + "xmlName": "ContentAsset" + }, + { + "directoryName": "sites", + "inFolder": false, + "metaFile": false, + "suffix": "site", + "xmlName": "CustomSite" + }, + { + "childXmlNames": ["SharingOwnerRule", "SharingCriteriaRule"], + "directoryName": "sharingRules", + "inFolder": false, + "metaFile": false, + "suffix": "sharingRules", + "xmlName": "SharingRules" + }, + { + "directoryName": "sharingSets", + "inFolder": false, + "metaFile": false, + "suffix": "sharingSet", + "xmlName": "SharingSet" + }, + { + "directoryName": "communities", + "inFolder": false, + "metaFile": false, + "suffix": "community", + "xmlName": "Community" + }, + { + "directoryName": "ChatterExtensions", + "inFolder": false, + "metaFile": false, + "suffix": "ChatterExtension", + "xmlName": "ChatterExtension" + }, + { + "directoryName": "platformEventChannels", + "inFolder": false, + "metaFile": false, + "suffix": "platformEventChannel", + "xmlName": "PlatformEventChannel" + }, + { + "directoryName": "platformEventChannelMembers", + "inFolder": false, + "metaFile": false, + "suffix": "platformEventChannelMember", + "xmlName": "PlatformEventChannelMember" + }, + { + "directoryName": "callCenters", + "inFolder": false, + "metaFile": false, + "suffix": "callCenter", + "xmlName": "CallCenter" + }, + { + "directoryName": "milestoneTypes", + "inFolder": false, + "metaFile": false, + "suffix": "milestoneType", + "xmlName": "MilestoneType" + }, + { + "directoryName": "entitlementProcesses", + "inFolder": false, + "metaFile": false, + "suffix": "entitlementProcess", + "xmlName": "EntitlementProcess" + }, + { + "directoryName": "entitlementTemplates", + "inFolder": false, + "metaFile": false, + "suffix": "entitlementTemplate", + "xmlName": "EntitlementTemplate" + }, + { + "directoryName": "timeSheetTemplates", + "inFolder": false, + "metaFile": false, + "suffix": "timeSheetTemplate", + "xmlName": "TimeSheetTemplate" + }, + { + "directoryName": "Canvases", + "inFolder": false, + "metaFile": false, + "suffix": "Canvas", + "xmlName": "CanvasMetadata" + }, + { + "directoryName": "MobileApplicationDetails", + "inFolder": false, + "metaFile": false, + "suffix": "MobileApplicationDetail", + "xmlName": "MobileApplicationDetail" + }, + { + "directoryName": "notificationtypes", + "inFolder": false, + "metaFile": false, + "suffix": "notiftype", + "xmlName": "CustomNotificationType" + }, + { + "directoryName": "connectedApps", + "inFolder": false, + "metaFile": false, + "suffix": "connectedApp", + "xmlName": "ConnectedApp" + }, + { + "directoryName": "appMenus", + "inFolder": false, + "metaFile": false, + "suffix": "appMenu", + "xmlName": "AppMenu" + }, + { + "directoryName": "notificationTypeConfig", + "inFolder": false, + "metaFile": false, + "suffix": "config", + "xmlName": "NotificationTypeConfig" + }, + { + "directoryName": "delegateGroups", + "inFolder": false, + "metaFile": false, + "suffix": "delegateGroup", + "xmlName": "DelegateGroup" + }, + { + "directoryName": "siteDotComSites", + "inFolder": false, + "metaFile": true, + "suffix": "site", + "xmlName": "SiteDotCom" + }, + { + "directoryName": "experiences", + "inFolder": false, + "metaFile": false, + "xmlName": "ExperienceBundle" + }, + { + "directoryName": "networks", + "inFolder": false, + "metaFile": false, + "suffix": "network", + "xmlName": "Network" + }, + { + "directoryName": "networkBranding", + "inFolder": false, + "metaFile": true, + "suffix": "networkBranding", + "xmlName": "NetworkBranding" + }, + { + "directoryName": "audience", + "inFolder": false, + "metaFile": false, + "suffix": "audience", + "xmlName": "Audience" + }, + { + "directoryName": "brandingSets", + "inFolder": false, + "metaFile": false, + "suffix": "brandingSet", + "xmlName": "BrandingSet" + }, + { + "directoryName": "communityThemeDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "communityThemeDefinition", + "xmlName": "CommunityThemeDefinition" + }, + { + "directoryName": "communityTemplateDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "communityTemplateDefinition", + "xmlName": "CommunityTemplateDefinition" + }, + { + "directoryName": "navigationMenus", + "inFolder": false, + "metaFile": false, + "suffix": "navigationMenu", + "xmlName": "NavigationMenu" + }, + { + "directoryName": "flowCategories", + "inFolder": false, + "metaFile": false, + "suffix": "flowCategory", + "xmlName": "FlowCategory" + }, + { + "directoryName": "lightningBolts", + "inFolder": false, + "metaFile": false, + "suffix": "lightningBolt", + "xmlName": "LightningBolt" + }, + { + "directoryName": "lightningExperienceThemes", + "inFolder": false, + "metaFile": false, + "suffix": "lightningExperienceTheme", + "xmlName": "LightningExperienceTheme" + }, + { + "directoryName": "lightningOnboardingConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "lightningOnboardingConfig", + "xmlName": "LightningOnboardingConfig" + }, + { + "directoryName": "customHelpMenuSections", + "inFolder": false, + "metaFile": false, + "suffix": "customHelpMenuSection", + "xmlName": "CustomHelpMenuSection" + }, + { + "directoryName": "prompts", + "inFolder": false, + "metaFile": false, + "suffix": "prompt", + "xmlName": "Prompt" + }, + { + "childXmlNames": ["ManagedTopic"], + "directoryName": "managedTopics", + "inFolder": false, + "metaFile": false, + "suffix": "managedTopics", + "xmlName": "ManagedTopics" + }, + { + "directoryName": "moderation", + "inFolder": false, + "metaFile": false, + "suffix": "keywords", + "xmlName": "KeywordList" + }, + { + "directoryName": "userCriteria", + "inFolder": false, + "metaFile": false, + "suffix": "userCriteria", + "xmlName": "UserCriteria" + }, + { + "directoryName": "moderation", + "inFolder": false, + "metaFile": false, + "suffix": "rule", + "xmlName": "ModerationRule" + }, + { + "directoryName": "cmsConnectSource", + "inFolder": false, + "metaFile": false, + "suffix": "cmsConnectSource", + "xmlName": "CMSConnectSource" + }, + { + "directoryName": "managedContentTypes", + "inFolder": false, + "metaFile": false, + "suffix": "managedContentType", + "xmlName": "ManagedContentType" + }, + { + "directoryName": "samlssoconfigs", + "inFolder": false, + "metaFile": false, + "suffix": "samlssoconfig", + "xmlName": "SamlSsoConfig" + }, + { + "directoryName": "corsWhitelistOrigins", + "inFolder": false, + "metaFile": false, + "suffix": "corsWhitelistOrigin", + "xmlName": "CorsWhitelistOrigin" + }, + { + "directoryName": "actionLinkGroupTemplates", + "inFolder": false, + "metaFile": false, + "suffix": "actionLinkGroupTemplate", + "xmlName": "ActionLinkGroupTemplate" + }, + { + "directoryName": "liveChatDeployments", + "inFolder": false, + "metaFile": false, + "suffix": "liveChatDeployment", + "xmlName": "LiveChatDeployment" + }, + { + "directoryName": "liveChatButtons", + "inFolder": false, + "metaFile": false, + "suffix": "liveChatButton", + "xmlName": "LiveChatButton" + }, + { + "directoryName": "liveChatAgentConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "liveChatAgentConfig", + "xmlName": "LiveChatAgentConfig" + }, + { + "directoryName": "liveChatSensitiveDataRule", + "inFolder": false, + "metaFile": false, + "suffix": "liveChatSensitiveDataRule", + "xmlName": "LiveChatSensitiveDataRule" + }, + { + "directoryName": "transactionSecurityPolicies", + "inFolder": false, + "metaFile": false, + "suffix": "transactionSecurityPolicy", + "xmlName": "TransactionSecurityPolicy" + }, + { + "directoryName": "synonymDictionaries", + "inFolder": false, + "metaFile": false, + "suffix": "synonymDictionary", + "xmlName": "SynonymDictionary" + }, + { + "directoryName": "pathAssistants", + "inFolder": false, + "metaFile": false, + "suffix": "pathAssistant", + "xmlName": "PathAssistant" + }, + { + "directoryName": "animationRules", + "inFolder": false, + "metaFile": false, + "suffix": "animationRule", + "xmlName": "AnimationRule" + }, + { + "directoryName": "LeadConvertSettings", + "inFolder": false, + "metaFile": false, + "suffix": "LeadConvertSetting", + "xmlName": "LeadConvertSettings" + }, + { + "directoryName": "cachePartitions", + "inFolder": false, + "metaFile": false, + "suffix": "cachePartition", + "xmlName": "PlatformCachePartition" + }, + { + "directoryName": "topicsForObjects", + "inFolder": false, + "metaFile": false, + "suffix": "topicsForObjects", + "xmlName": "TopicsForObjects" + }, + { + "directoryName": "recommendationStrategies", + "inFolder": false, + "metaFile": false, + "suffix": "recommendationStrategy", + "xmlName": "RecommendationStrategy" + }, + { + "directoryName": "emailservices", + "inFolder": false, + "metaFile": false, + "suffix": "xml", + "xmlName": "EmailServicesFunction" + }, + { + "directoryName": "recordActionDeployments", + "inFolder": false, + "metaFile": false, + "suffix": "deployment", + "xmlName": "RecordActionDeployment" + }, + { + "directoryName": "salesAgreementSettings", + "inFolder": false, + "metaFile": false, + "suffix": "salesAgreementSetting", + "xmlName": "SalesAgreementSettings" + }, + { + "directoryName": "AccountForecastSettings", + "inFolder": false, + "metaFile": false, + "suffix": "accountForecastSetting", + "xmlName": "AccountForecastSettings" + }, + { + "directoryName": "icons", + "inFolder": false, + "metaFile": false, + "suffix": "icon", + "xmlName": "Icon" + }, + { + "directoryName": "EmbeddedServiceLiveAgent", + "inFolder": false, + "metaFile": false, + "suffix": "EmbeddedServiceLiveAgent", + "xmlName": "EmbeddedServiceLiveAgent" + }, + { + "directoryName": "EmbeddedServiceConfig", + "inFolder": false, + "metaFile": false, + "suffix": "EmbeddedServiceConfig", + "xmlName": "EmbeddedServiceConfig" + }, + { + "directoryName": "EmbeddedServiceBranding", + "inFolder": false, + "metaFile": false, + "suffix": "EmbeddedServiceBranding", + "xmlName": "EmbeddedServiceBranding" + }, + { + "directoryName": "EmbeddedServiceFlowConfig", + "inFolder": false, + "metaFile": false, + "suffix": "EmbeddedServiceFlowConfig", + "xmlName": "EmbeddedServiceFlowConfig" + }, + { + "directoryName": "EmbeddedServiceMenuSettings", + "inFolder": false, + "metaFile": false, + "suffix": "EmbeddedServiceMenuSettings", + "xmlName": "EmbeddedServiceMenuSettings" + }, + { + "directoryName": "uiObjectRelationConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "uiObjectRelationConfig", + "xmlName": "UIObjectRelationConfig" + }, + { + "directoryName": "careProviderSearchConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "careProviderSearchConfig", + "xmlName": "CareProviderSearchConfig" + }, + { + "directoryName": "settings", + "inFolder": false, + "metaFile": false, + "suffix": "settings", + "xmlName": "Settings" + } + ], + "organizationNamespace": "", + "partialSaveAllowed": false, + "testRequired": true +} diff --git a/packages/sfpowerscripts-cli/resources/schemas/pooldefinition.schema.json b/packages/sfpowerscripts-cli/resources/schemas/pooldefinition.schema.json new file mode 100644 index 000000000..b3102e17d --- /dev/null +++ b/packages/sfpowerscripts-cli/resources/schemas/pooldefinition.schema.json @@ -0,0 +1,147 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/flxblio/sfp/develop/packages/sfp-cli/resources/schemas/pooldefinition.schema.json", + "title": "pool definition", + "description": "The definition for creating a pool of scratch orgs in sfp", + "type": "object", + "required": ["tag", "maxAllocation"], + "additionalProperties": false, + "properties": { + "$schema": { + "description": "Support editors like vscode to help with IntelliSense", + "type": "string", + "default": "https://raw.githubusercontent.com/flxblio/sfp/develop/packages/sfp-cli/resources/schemas/pooldefinition.schema.json" + }, + "tag": { + "title": "Tag of the pool", + "description": "Tag or name to identify the scratch org pool", + "type": "string" + }, + "waitTime": { + "title": "wait time", + "description": "Time to wait for scratch org creation in minutes (default:6 mins)", + "type": "integer", + "default": 6 + }, + "expiry": { + "title": "expiry", + "description": "Duration of the scratch org (in days) (default:2)", + "type": "integer", + "default": 2 + }, + "maxAllocation": { + "title": "Max number of scratch orgs to be allocated", + "description": "Maximum number of scratch orgs to be allocated in the pool", + "type": "integer" + }, + "batchSize": { + "title": "Batch Size", + "description": "Control the parallelism of the pool creation (default:5)", + "type": "integer", + "default": 5 + }, + "configFilePath": { + "title": "Path to config file", + "description": "Reference an external .json file to specify the features and org preferences required for the metadata of your package, such as the scratch org definition.", + "type": "string", + "default": "config/project-scratch-def.json" + }, + "releaseConfigFile": { + "title": "Path to release config file", + "description": "Path to the config file which determines how a release defintion should be generated, enable this for pools to use this release config to only utilize artifacts described the releae config", + "type": "string" + }, + "succeedOnDeploymentErrors": { + "title": "Succeed on Deployment Errors", + "description": "In case of a deployment error, whether to keep that scratch org in the pool", + "type": "boolean", + "default": true + }, + "installAll": { + "title": "Install all packages", + "description": "Install all package artifacts, in addition to the managed package dependencies", + "type": "boolean", + "default": false + }, + "enableVlocity": { + "title": "Enable vlocity config deployment", + "description": "[alpha] Enable vlocity settings and config deployment. Please note it doesnt install vlocity managed package", + "type": "boolean", + "default": "false" + }, + "enableSourceTracking": { + "title": "Enable source tracking", + "description": "Enable source tracking by deploying packages using source:push , and persisting local source tracking files", + "type": "boolean", + "default": true + }, + "relaxAllIPRanges": { + "title": "Relax all IP Ranges", + "description": "Relax all IP addresses to enable developers to login to scratch orgs", + "type": "boolean", + "default": "false" + }, + "ipRangesToBeRelaxed": { + "title": "IP ranges to be relaxed", + "description": "Relax IP address of developers to allow access to scratch orgs", + "type": "array" + }, + "retryOnFailure": { + "title": "Retry on failure", + "description": "Retry installation of packages on failed deployment", + "type": "boolean", + "default": "false" + }, + "maxRetryCount": { + "title": "Max Retry Count", + "description": "Maximum number of attempts sfp should retry installation of packages on failed deployment", + "type": "number", + "default": "2" + }, + "snapshotPool": { + "title": "Snapshot Pool", + "description": "Use a pre-prepared pool to further add packages on top of it", + "type": "string" + }, + "postDeploymentScriptPath": { + "title": "Post Script", + "description": "Execute a custom script after all the artifacts are deployed into a particular org", + "type": "string" + }, + "preDependencyInstallationScriptPath": { + "title": "Pre Script", + "description": "Execute a custom script before denpendencies install into a particular org", + "type": "string" + }, + "disableSourcePackageOverride": { + "title": "Disable installation of unlocked packages as source package", + "description": "Prepare by default utilizes source package for installing unlocked packages to the scratchorg, disabling this flag will allow to install it ", + "type": "boolean", + "default":false + }, + "fetchArtifacts": { + "title": "Fetch Artifacts using below mechanism", + "description": "Fetch artifacts from artifact registry using below mechanism", + "type": "object", + "oneOf": [{ "required": ["artifactFetchScript"] }, { "required": ["npm"] }], + "properties": { + "artifactFetchScript": { + "title": "Path to the script for fetching artifacts", + "description": "Path to Shell script that handles fetching artifacts from a registry", + "type": "string" + }, + "npm": { + "type": "object", + "required": ["scope"], + "properties": { + "scope": { + "title": "Scope of NPM packages", + "description": "Scope of NPM packages", + "type": "string" + } + } + } + } + } + } +} diff --git a/packages/sfpowerscripts-cli/resources/schemas/releasedefinition.schema.json b/packages/sfpowerscripts-cli/resources/schemas/releasedefinition.schema.json index 04c4f5d93..7a9071925 100644 --- a/packages/sfpowerscripts-cli/resources/schemas/releasedefinition.schema.json +++ b/packages/sfpowerscripts-cli/resources/schemas/releasedefinition.schema.json @@ -1,8 +1,8 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://github.com/dxatscale/sfpowerscripts/blob/develop/packages/sfpowerscripts-cli/resources/releasedefinition.schema.json", + "$id": "https://github.com/flxblio/sfp/blob/develop/packages/sfp-cli/resources/releasedefinition.schema.json", "title": "release definition", - "description": "The definition for a release using sfpowerscripts orchestrator", + "description": "The definition for a release using sfp orchestrator", "type": "object", "required": ["release", "artifacts"], "additionalProperties": false, diff --git a/packages/sfpowerscripts-cli/resources/schemas/releasedefinitiongenerator.schema.json b/packages/sfpowerscripts-cli/resources/schemas/releasedefinitiongenerator.schema.json index 9a7f2cc7c..9b9c4372d 100644 --- a/packages/sfpowerscripts-cli/resources/schemas/releasedefinitiongenerator.schema.json +++ b/packages/sfpowerscripts-cli/resources/schemas/releasedefinitiongenerator.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://github.com/dxatscale/sfpowerscripts/blob/develop/packages/sfpowerscripts-cli/resources/releasedefinitiongenerator.schema.json", + "$id": "https://github.com/flxblio/sfp/blob/develop/packages/sfp-cli/resources/releasedefinitiongenerator.schema.json", "title": "release definition generator", "description": "The definition for generating a release defintion using generator command", "type": "object", diff --git a/packages/sfpowerscripts-cli/resources/schemas/sfdx-project.schema.json b/packages/sfpowerscripts-cli/resources/schemas/sfdx-project.schema.json index 743d62127..db002e40b 100644 --- a/packages/sfpowerscripts-cli/resources/schemas/sfdx-project.schema.json +++ b/packages/sfpowerscripts-cli/resources/schemas/sfdx-project.schema.json @@ -1,8 +1,8 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://github.com/dxatscale/sfpowerscripts/blob/develop/packages/sfpowerscripts-cli/resources/sfdx-project.schema.json", - "title": "sfpowerscripts DX Project File", - "description": "The properties and shape of the SFDX project enhanced for sfpowerscripts", + "$id": "https://github.com/flxblio/sfp/blob/develop/packages/sfp-cli/resources/sfdx-project.schema.json", + "title": "sfp DX Project File", + "description": "The properties and shape of the SFDX project enhanced for sfp", "type": "object", "additionalProperties": true, "required": [ @@ -12,7 +12,7 @@ "$schema": { "description": "Support editors like vscode to help with IntelliSense", "type": "string", - "default": "https://raw.githubusercontent.com/dxatscale/sfpowerscripts/main/packages/sfpowerscripts-cli/resources/schemas/sfdx-project.schema.json" + "default": "https://raw.githubusercontent.com/flxblio/sfp/main/packages/sfp-cli/resources/schemas/sfdx-project.schema.json" }, "packageDirectories": { "title": "Package Directories", @@ -236,8 +236,8 @@ "description": "Salesforce CLI plugin configurations used with this project.", "additionalProperties": true, "properties": { - "sfpowerscripts": { - "$ref": "#/definitions/plugins.sfpowerscripts" + "sfp": { + "$ref": "#/definitions/plugins.sfp" } } }, @@ -560,10 +560,10 @@ "title": "Package branch", "description": "branched package for the specific dev team" }, - "plugins.sfpowerscripts": { + "plugins.sfp": { "type": "object", - "title": "sfpowerscripts plugin configuration", - "description": "Configuration for sfpowerscripts plugin", + "title": "sfp plugin configuration", + "description": "Configuration for sfp plugin", "additionalProperties": false, "properties": { "ignoreFiles": { @@ -626,7 +626,7 @@ "externalDependencyMap": { "title": "Map of external package and its dependencies", "type": "object", - "description": "Use this map to define dependencies of unlocked packages built elsewhere, This information will be used by sfpowerscripts while expanding package dependencies", + "description": "Use this map to define dependencies of unlocked packages built elsewhere, This information will be used by sfp while expanding package dependencies", "patternProperties": { ".*": { "type": "array", diff --git a/packages/sfpowerscripts-cli/src/BuildBase.ts b/packages/sfpowerscripts-cli/src/BuildBase.ts index d6eae1e1b..9f6e40201 100644 --- a/packages/sfpowerscripts-cli/src/BuildBase.ts +++ b/packages/sfpowerscripts-cli/src/BuildBase.ts @@ -1,12 +1,12 @@ -import ArtifactGenerator from '@dxatscale/sfpowerscripts.core/lib/artifacts/generators/ArtifactGenerator'; +import ArtifactGenerator from './core//artifacts/generators/ArtifactGenerator'; import { EOL } from 'os'; -import SfpowerscriptsCommand from './SfpowerscriptsCommand'; +import sfpCommand from './SfpCommand'; import { Messages } from '@salesforce/core'; import fs = require('fs'); -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; +import SFPStatsSender from './core/stats/SFPStatsSender'; import BuildImpl, { BuildProps } from './impl/parallelBuilder/BuildImpl'; -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; +import ProjectConfig from './core/project/ProjectConfig'; import { Stage } from './impl/Stage'; import SFPLogger, { COLOR_ERROR, @@ -19,9 +19,9 @@ import SFPLogger, { ConsoleLogger, LoggerLevel, COLOR_KEY_VALUE, -} from '@dxatscale/sfp-logger'; -import getFormattedTime from '@dxatscale/sfpowerscripts.core/lib/utils/GetFormattedTime'; -import SfpPackage from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; +} from '@flxblio/sfp-logger'; +import getFormattedTime from './core//utils/GetFormattedTime'; +import SfpPackage from './core//package/SfpPackage'; import ReleaseConfig from './impl/release/ReleaseConfig'; import { Flags } from '@oclif/core'; import { loglevel, orgApiVersionFlagSfdxStyle, targetdevhubusername } from './flags/sfdxflags'; @@ -32,9 +32,9 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'build'); +const messages = Messages.loadMessages('@flxblio/sfp', 'build'); -export default abstract class BuildBase extends SfpowerscriptsCommand { +export default abstract class BuildBase extends sfpCommand { protected static requiresUsername = false; protected static requiresDevhubUsername = false; protected static requiresProject = true; @@ -115,7 +115,7 @@ export default abstract class BuildBase extends SfpowerscriptsCommand { SFPLogger.log(COLOR_HEADER(`command: ${COLOR_KEY_MESSAGE(this.getStage())}`)); SFPLogger.log(COLOR_HEADER(`Build Packages Only Changed: ${flags.diffcheck}`)); - if(projectConfig?.plugins?.sfpowerscripts?.scratchOrgDefFilePaths?.enableMultiDefinitionFiles){ + if(projectConfig?.plugins?.sfp?.scratchOrgDefFilePaths?.enableMultiDefinitionFiles){ SFPLogger.log(COLOR_HEADER(`Multiple Config Files Mode: enabled`)); }else{ SFPLogger.log(COLOR_HEADER(`Config File Path: ${flags.configfilepath}`)); diff --git a/packages/sfpowerscripts-cli/src/InstallPackageCommand.ts b/packages/sfpowerscripts-cli/src/InstallPackageCommand.ts index ed6767c53..51c4fc53b 100644 --- a/packages/sfpowerscripts-cli/src/InstallPackageCommand.ts +++ b/packages/sfpowerscripts-cli/src/InstallPackageCommand.ts @@ -1,23 +1,23 @@ -import SfpowerscriptsCommand from './SfpowerscriptsCommand'; +import sfpCommand from './SfpCommand'; import { Messages } from '@salesforce/core'; -import ArtifactFetcher, { Artifact } from '@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactFetcher'; +import ArtifactFetcher, { Artifact } from './core//artifacts/ArtifactFetcher'; import * as rimraf from 'rimraf'; -import SfpPackage from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; -import { ConsoleLogger } from '@dxatscale/sfp-logger'; -import SfpPackageBuilder from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageBuilder'; -import SFPOrg from '@dxatscale/sfpowerscripts.core/lib/org/SFPOrg'; +import SfpPackage from './core//package/SfpPackage'; +import { ConsoleLogger } from '@flxblio/sfp-logger'; +import SfpPackageBuilder from './core//package/SfpPackageBuilder'; +import SFPOrg from './core//org/SFPOrg'; import { Flags } from '@oclif/core'; import { requiredUserNameFlag } from './flags/sfdxflags'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'install_package_command'); +const messages = Messages.loadMessages('@flxblio/sfp', 'install_package_command'); /** * Base class providing common functionality for package installation * - * @extends SfpowerscriptsCommand + * @extends sfpCommand */ -export default abstract class InstallPackageCommand extends SfpowerscriptsCommand { +export default abstract class InstallPackageCommand extends sfpCommand { protected sfpPackage: SfpPackage; protected sfpOrg: SFPOrg; /** @@ -86,6 +86,6 @@ export default abstract class InstallPackageCommand extends SfpowerscriptsComman */ private postInstall(): void { // Delete temp directory containing unzipped artifacts - rimraf.sync('.sfpowerscripts/unzippedArtifacts'); + rimraf.sync('.sfp/unzippedArtifacts'); } } diff --git a/packages/sfpowerscripts-cli/src/PackageCreateCommand.ts b/packages/sfpowerscripts-cli/src/PackageCreateCommand.ts index 0036b1ed1..f46a182f1 100644 --- a/packages/sfpowerscripts-cli/src/PackageCreateCommand.ts +++ b/packages/sfpowerscripts-cli/src/PackageCreateCommand.ts @@ -1,20 +1,20 @@ -import ArtifactGenerator from '@dxatscale/sfpowerscripts.core/lib/artifacts/generators/ArtifactGenerator'; -import { COLOR_HEADER, COLOR_KEY_MESSAGE, ConsoleLogger } from '@dxatscale/sfp-logger'; -import PackageDiffImpl from '@dxatscale/sfpowerscripts.core/lib/package/diff/PackageDiffImpl'; +import ArtifactGenerator from './core//artifacts/generators/ArtifactGenerator'; +import { COLOR_HEADER, COLOR_KEY_MESSAGE, ConsoleLogger } from '@flxblio/sfp-logger'; +import PackageDiffImpl from './core//package/diff/PackageDiffImpl'; import { Messages } from '@salesforce/core'; import { EOL } from 'os'; -import SfpowerscriptsCommand from './SfpowerscriptsCommand'; -import SfpPackage, { PackageType } from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; -import getFormattedTime from '@dxatscale/sfpowerscripts.core/lib/utils/GetFormattedTime'; +import sfpCommand from './SfpCommand'; +import SfpPackage, { PackageType } from './core//package/SfpPackage'; +import getFormattedTime from './core//utils/GetFormattedTime'; const fs = require('fs-extra'); -import Git from '@dxatscale/sfpowerscripts.core/lib/git/Git'; +import Git from './core//git/Git'; import { Flags } from '@oclif/core'; import { loglevel } from './flags/sfdxflags'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'create-package'); +const messages = Messages.loadMessages('@flxblio/sfp', 'create-package'); -export default abstract class PackageCreateCommand extends SfpowerscriptsCommand { +export default abstract class PackageCreateCommand extends sfpCommand { protected static requiresUsername = false; protected static requiresDevhubUsername = false; protected static requiresProject = true; @@ -115,7 +115,7 @@ export default abstract class PackageCreateCommand extends SfpowerscriptsCommand let git = await Git.initiateRepo(new ConsoleLogger()); let tagname = `${this.sfdxPackage}_v${sfpPackage.package_version_number}`; - await git.addAnnotatedTag(tagname, `${sfpPackage.packageName} sfpowerscripts package ${sfpPackage.package_version_number}`) + await git.addAnnotatedTag(tagname, `${sfpPackage.packageName} sfp package ${sfpPackage.package_version_number}`) sfpPackage.tag = tagname; } @@ -131,7 +131,7 @@ export default abstract class PackageCreateCommand extends SfpowerscriptsCommand } private generateEnvironmentVariables(artifactFilepath: string, sfpPackage: SfpPackage) { - let prefix = 'sfpowerscripts'; + let prefix = 'sfp'; if (this.refname != null) prefix = `${this.refname}_${prefix}`; console.log('\nOutput variables:'); diff --git a/packages/sfpowerscripts-cli/src/ProjectValidation.ts b/packages/sfpowerscripts-cli/src/ProjectValidation.ts index 64606e013..3ba631ec2 100644 --- a/packages/sfpowerscripts-cli/src/ProjectValidation.ts +++ b/packages/sfpowerscripts-cli/src/ProjectValidation.ts @@ -1,9 +1,9 @@ -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; +import ProjectConfig from './core/project/ProjectConfig'; import Ajv from 'ajv'; import path = require('path'); import * as fs from 'fs-extra'; -import { PackageType } from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; -import SFPLogger, { LoggerLevel } from '@dxatscale/sfp-logger'; +import { PackageType } from './core//package/SfpPackage'; +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; export default class ProjectValidation { private readonly projectConfig; @@ -32,7 +32,7 @@ export default class ProjectValidation { }); - SFPLogger.log(`The following attributes are not recognized by sfpowerscripts, You might need to remove them`,LoggerLevel.WARN) + SFPLogger.log(`The following attributes are not recognized by sfp, You might need to remove them`,LoggerLevel.WARN) SFPLogger.log(errorMsg, LoggerLevel.WARN); } } diff --git a/packages/sfpowerscripts-cli/src/SfpowerscriptsCommand.ts b/packages/sfpowerscripts-cli/src/SfpCommand.ts similarity index 78% rename from packages/sfpowerscripts-cli/src/SfpowerscriptsCommand.ts rename to packages/sfpowerscripts-cli/src/SfpCommand.ts index 78c3b1ab3..cff888a9a 100644 --- a/packages/sfpowerscripts-cli/src/SfpowerscriptsCommand.ts +++ b/packages/sfpowerscripts-cli/src/SfpCommand.ts @@ -1,8 +1,7 @@ -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; +import SFPStatsSender from './core/stats/SFPStatsSender'; import * as rimraf from 'rimraf'; import ProjectValidation from './ProjectValidation'; -import * as fs from 'fs-extra'; -import SFPLogger, { COLOR_HEADER, ConsoleLogger, LoggerLevel } from '@dxatscale/sfp-logger'; +import SFPLogger, { COLOR_HEADER, ConsoleLogger, LoggerLevel } from '@flxblio/sfp-logger'; import GroupConsoleLogs from './ui/GroupConsoleLogs'; import { Command, Flags, ux } from '@oclif/core'; import { FlagOutput } from '@oclif/core/lib/interfaces/parser'; @@ -10,11 +9,11 @@ import { Org } from '@salesforce/core'; /** - * A base class that provides common funtionality for sfpowerscripts commands + * A base class that provides common funtionality for sfp commands * * @extends SfdxCommand */ -export default abstract class SfpowerscriptsCommand extends Command { +export default abstract class sfpCommand extends Command { protected static requiresProject: boolean; @@ -24,7 +23,7 @@ export default abstract class SfpowerscriptsCommand extends Command { private isSfpowerkitFound: boolean; - private sfpowerscriptsConfig; + private sfpConfig; private isSfdmuFound: boolean; protected static requiresUsername: boolean=false; protected static requiresDevhubUsername: boolean=false; @@ -40,7 +39,7 @@ export default abstract class SfpowerscriptsCommand extends Command { */ async run(): Promise { //Always enable color by default - if (process.env.SFPOWERSCRIPTS_NOCOLOR) SFPLogger.disableColor(); + if (process.env.sfp_NOCOLOR) SFPLogger.disableColor(); else SFPLogger.enableColor(); @@ -78,7 +77,7 @@ export default abstract class SfpowerscriptsCommand extends Command { //Clear temp directory before every run - rimraf.sync('.sfpowerscripts'); + rimraf.sync('.sfp'); //Initialise StatsD @@ -122,32 +121,32 @@ export default abstract class SfpowerscriptsCommand extends Command { private initializeStatsD() { - if (process.env.SFPOWERSCRIPTS_STATSD) { + if (process.env.sfp_STATSD) { SFPStatsSender.initialize( - process.env.SFPOWERSCRIPTS_STATSD_PORT, - process.env.SFPOWERSCRIPTS_STATSD_HOST, - process.env.SFPOWERSCRIPTS_STATSD_PROTOCOL + process.env.sfp_STATSD_PORT, + process.env.sfp_STATSD_HOST, + process.env.sfp_STATSD_PROTOCOL ); } - if (process.env.SFPOWERSCRIPTS_DATADOG) { + if (process.env.sfp_DATADOG) { SFPStatsSender.initializeNativeMetrics( 'DataDog', - process.env.SFPOWERSCRIPTS_DATADOG_HOST, - process.env.SFPOWERSCRIPTS_DATADOG_API_KEY, + process.env.sfp_DATADOG_HOST, + process.env.sfp_DATADOG_API_KEY, new ConsoleLogger() ); - } else if (process.env.SFPOWERSCRIPTS_NEWRELIC) { + } else if (process.env.sfp_NEWRELIC) { SFPStatsSender.initializeNativeMetrics( 'NewRelic', null, - process.env.SFPOWERSCRIPTS_NEWRELIC_API_KEY, + process.env.sfp_NEWRELIC_API_KEY, new ConsoleLogger() ); - } else if (process.env.SFPOWERSCRIPTS_SPLUNK) { + } else if (process.env.sfp_SPLUNK) { SFPStatsSender.initializeNativeMetrics( 'Splunk', - process.env.SFPOWERSCRIPTS_SPLUNK_HOST, - process.env.SFPOWERSCRIPTS_SPLUNK_API_KEY, + process.env.sfp_SPLUNK_HOST, + process.env.sfp_SPLUNK_API_KEY, new ConsoleLogger() ); } @@ -171,8 +170,8 @@ export default abstract class SfpowerscriptsCommand extends Command { } - protected get statics(): typeof SfpowerscriptsCommand { - return this.constructor as typeof SfpowerscriptsCommand; + protected get statics(): typeof sfpCommand { + return this.constructor as typeof sfpCommand; } } diff --git a/packages/sfpowerscripts-cli/src/commands/apextests/trigger.ts b/packages/sfpowerscripts-cli/src/commands/apextests/trigger.ts index afeeed9a2..53e600e87 100644 --- a/packages/sfpowerscripts-cli/src/commands/apextests/trigger.ts +++ b/packages/sfpowerscripts-cli/src/commands/apextests/trigger.ts @@ -6,16 +6,16 @@ import { TestLevel, TestOptions, RunAllTestsInPackageOptions, -} from '@dxatscale/sfpowerscripts.core/lib/apextest/TestOptions'; -import TriggerApexTests from '@dxatscale/sfpowerscripts.core/lib/apextest/TriggerApexTests'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +} from '../../core/apextest/TestOptions'; +import TriggerApexTests from '../../core/apextest/TriggerApexTests'; +import sfpCommand from '../../SfpCommand'; import { Messages } from '@salesforce/core'; -import SfpPackage from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; +import SfpPackage from '../../core/package/SfpPackage'; -import { ConsoleLogger } from '@dxatscale/sfp-logger'; -import { CoverageOptions } from '@dxatscale/sfpowerscripts.core/lib/apex/coverage/IndividualClassCoverage'; -import SfpPackageBuilder from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageBuilder'; -import { PackageType } from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; +import { ConsoleLogger } from '@flxblio/sfp-logger'; +import { CoverageOptions } from '../../core/apex/coverage/IndividualClassCoverage'; +import SfpPackageBuilder from '../../core/package/SfpPackageBuilder'; +import { PackageType } from '../../core/package/SfpPackage'; import { Flags } from '@oclif/core'; import { loglevel, orgApiVersionFlagSfdxStyle, requiredUserNameFlag } from '../../flags/sfdxflags'; const path = require('path'); @@ -25,9 +25,9 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'trigger_apex_test'); +const messages = Messages.loadMessages('@flxblio/sfp', 'trigger_apex_test'); -export default class TriggerApexTest extends SfpowerscriptsCommand { +export default class TriggerApexTest extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [ diff --git a/packages/sfpowerscripts-cli/src/commands/artifacts/fetch.ts b/packages/sfpowerscripts-cli/src/commands/artifacts/fetch.ts index 849ec10a0..1a8854edd 100644 --- a/packages/sfpowerscripts-cli/src/commands/artifacts/fetch.ts +++ b/packages/sfpowerscripts-cli/src/commands/artifacts/fetch.ts @@ -1,18 +1,18 @@ -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import { LoggerLevel, Messages } from '@salesforce/core'; import FetchImpl, { ArtifactVersion } from '../../impl/artifacts/FetchImpl'; import ReleaseDefinition from '../../impl/release/ReleaseDefinition'; import FetchArtifactsError from '../../impl/artifacts/FetchArtifactsError'; -import { ConsoleLogger } from '@dxatscale/sfp-logger'; +import { ConsoleLogger } from '@flxblio/sfp-logger'; import { Flags } from '@oclif/core'; import { loglevel } from '../../flags/sfdxflags'; -import SFPLogger from '@dxatscale/sfp-logger'; -import { COLOR_HEADER } from '@dxatscale/sfp-logger'; +import SFPLogger from '@flxblio/sfp-logger'; +import { COLOR_HEADER } from '@flxblio/sfp-logger'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'fetch'); +const messages = Messages.loadMessages('@flxblio/sfp', 'fetch'); -export default class Fetch extends SfpowerscriptsCommand { +export default class Fetch extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [ diff --git a/packages/sfpowerscripts-cli/src/commands/artifacts/query.ts b/packages/sfpowerscripts-cli/src/commands/artifacts/query.ts index 464755f64..7c0ed8ba7 100644 --- a/packages/sfpowerscripts-cli/src/commands/artifacts/query.ts +++ b/packages/sfpowerscripts-cli/src/commands/artifacts/query.ts @@ -1,15 +1,15 @@ -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import { LoggerLevel, Messages } from '@salesforce/core'; -import SFPOrg from '@dxatscale/sfpowerscripts.core/lib/org/SFPOrg'; -import SFPLogger, { ConsoleLogger } from '@dxatscale/sfp-logger'; +import SFPOrg from '../../core/org/SFPOrg'; +import SFPLogger, { ConsoleLogger } from '@flxblio/sfp-logger'; import { ZERO_BORDER_TABLE } from '../../ui/TableConstants'; import { loglevel, requiredUserNameFlag } from '../../flags/sfdxflags'; const Table = require('cli-table'); Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'artifacts_query'); +const messages = Messages.loadMessages('@flxblio/sfp', 'artifacts_query'); -export default class Query extends SfpowerscriptsCommand { +export default class Query extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [`$ sfp artifacts:query -u `]; @@ -35,7 +35,7 @@ export default class Query extends SfpowerscriptsCommand { 'Commmit Id', 'Subcriber Version', 'Type', - 'Is Sfpowerscripts Installed', + 'Is sfp Installed', ], chars: ZERO_BORDER_TABLE }); @@ -46,7 +46,7 @@ export default class Query extends SfpowerscriptsCommand { installedArtifact.commitId.substring(0,8), installedArtifact.subscriberVersion, installedArtifact.type, - installedArtifact.isInstalledBySfpowerscripts, + installedArtifact.isInstalledBysfp, ]); }); SFPLogger.log(minTable.toString(), LoggerLevel.INFO, new ConsoleLogger()); diff --git a/packages/sfpowerscripts-cli/src/commands/changelog/generate.ts b/packages/sfpowerscripts-cli/src/commands/changelog/generate.ts index a65b56183..bc94d8ce8 100644 --- a/packages/sfpowerscripts-cli/src/commands/changelog/generate.ts +++ b/packages/sfpowerscripts-cli/src/commands/changelog/generate.ts @@ -1,14 +1,14 @@ -import { ConsoleLogger } from '@dxatscale/sfp-logger'; +import { ConsoleLogger } from '@flxblio/sfp-logger'; import { Messages } from '@salesforce/core'; import ChangelogImpl from '../../impl/changelog/ChangelogImpl'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import { Flags } from '@oclif/core'; import { loglevel } from '../../flags/sfdxflags'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'generate_changelog'); +const messages = Messages.loadMessages('@flxblio/sfp', 'generate_changelog'); -export default class GenerateChangelog extends SfpowerscriptsCommand { +export default class GenerateChangelog extends sfpCommand { public static description = messages.getMessage('commandDescription'); diff --git a/packages/sfpowerscripts-cli/src/commands/dependency/expand.ts b/packages/sfpowerscripts-cli/src/commands/dependency/expand.ts index 0faac4c5c..87070e906 100644 --- a/packages/sfpowerscripts-cli/src/commands/dependency/expand.ts +++ b/packages/sfpowerscripts-cli/src/commands/dependency/expand.ts @@ -1,11 +1,11 @@ -import TransitiveDependencyResolver from '@dxatscale/sfpowerscripts.core/lib/package/dependencies/TransitiveDependencyResolver'; +import TransitiveDependencyResolver from '../../core/package/dependencies/TransitiveDependencyResolver'; import { Messages } from '@salesforce/core'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; -import SFPLogger, { LoggerLevel, Logger } from '@dxatscale/sfp-logger'; +import sfpCommand from '../../SfpCommand'; +import ProjectConfig from '../../core/project/ProjectConfig'; +import SFPLogger, { LoggerLevel, Logger } from '@flxblio/sfp-logger'; import * as fs from 'fs-extra'; import path = require('path'); -import UserDefinedExternalDependency from "@dxatscale/sfpowerscripts.core/lib/project/UserDefinedExternalDependency"; +import UserDefinedExternalDependency from "../../core/project/UserDefinedExternalDependency"; import { Flags } from '@oclif/core'; import { loglevel, targetdevhubusername } from '../../flags/sfdxflags'; @@ -14,9 +14,9 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'dependency_expand'); +const messages = Messages.loadMessages('@flxblio/sfp', 'dependency_expand'); -export default class Expand extends SfpowerscriptsCommand { +export default class Expand extends sfpCommand { public static description = messages.getMessage('commandDescription'); diff --git a/packages/sfpowerscripts-cli/src/commands/dependency/install.ts b/packages/sfpowerscripts-cli/src/commands/dependency/install.ts index 9414fa37a..9d275a225 100644 --- a/packages/sfpowerscripts-cli/src/commands/dependency/install.ts +++ b/packages/sfpowerscripts-cli/src/commands/dependency/install.ts @@ -1,11 +1,11 @@ -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import { Messages } from '@salesforce/core'; -import ExternalPackage2DependencyResolver from '@dxatscale/sfpowerscripts.core/lib/package/dependencies/ExternalPackage2DependencyResolver'; -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; -import SFPLogger, { COLOR_KEY_MESSAGE, ConsoleLogger, LoggerLevel } from '@dxatscale/sfp-logger'; -import ExternalDependencyDisplayer from '@dxatscale/sfpowerscripts.core/lib/display/ExternalDependencyDisplayer'; -import InstallUnlockedPackageCollection from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/InstallUnlockedPackageCollection'; -import SFPOrg from '@dxatscale/sfpowerscripts.core/lib/org/SFPOrg'; +import ExternalPackage2DependencyResolver from '../../core/package/dependencies/ExternalPackage2DependencyResolver'; +import ProjectConfig from '../../core/project/ProjectConfig'; +import SFPLogger, { COLOR_KEY_MESSAGE, ConsoleLogger, LoggerLevel } from '@flxblio/sfp-logger'; +import ExternalDependencyDisplayer from '../../core/display/ExternalDependencyDisplayer'; +import InstallUnlockedPackageCollection from '../../core/package/packageInstallers/InstallUnlockedPackageCollection'; +import SFPOrg from '../../core/org/SFPOrg'; import { Flags } from '@oclif/core'; import { loglevel, targetdevhubusername, requiredUserNameFlag } from '../../flags/sfdxflags'; @@ -14,9 +14,9 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'dependency_install'); +const messages = Messages.loadMessages('@flxblio/sfp', 'dependency_install'); -export default class Install extends SfpowerscriptsCommand { +export default class Install extends sfpCommand { public static description = messages.getMessage('commandDescription'); protected static requiresUsername = true; protected static requiresDevhubUsername = true; diff --git a/packages/sfpowerscripts-cli/src/commands/dependency/shrink.ts b/packages/sfpowerscripts-cli/src/commands/dependency/shrink.ts index 2a2573f06..c6aa0ced2 100644 --- a/packages/sfpowerscripts-cli/src/commands/dependency/shrink.ts +++ b/packages/sfpowerscripts-cli/src/commands/dependency/shrink.ts @@ -1,10 +1,10 @@ import ShrinkImpl from '../../impl/dependency/ShrinkImpl'; import { Messages } from '@salesforce/core'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; +import sfpCommand from '../../SfpCommand'; +import ProjectConfig from '../../core/project/ProjectConfig'; import * as fs from 'fs-extra'; import path = require('path'); -import SFPLogger, { LoggerLevel, Logger } from '@dxatscale/sfp-logger'; +import SFPLogger, { LoggerLevel, Logger } from '@flxblio/sfp-logger'; import { Flags } from '@oclif/core'; import { loglevel, targetdevhubusername } from '../../flags/sfdxflags'; @@ -14,9 +14,9 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'dependency_shrink'); +const messages = Messages.loadMessages('@flxblio/sfp', 'dependency_shrink'); -export default class Shrink extends SfpowerscriptsCommand { +export default class Shrink extends sfpCommand { public static description = messages.getMessage('commandDescription'); protected static requiresDevhubUsername = true; diff --git a/packages/sfpowerscripts-cli/src/commands/impact/package.ts b/packages/sfpowerscripts-cli/src/commands/impact/package.ts index a550b7d3f..4b803d1a1 100644 --- a/packages/sfpowerscripts-cli/src/commands/impact/package.ts +++ b/packages/sfpowerscripts-cli/src/commands/impact/package.ts @@ -1,7 +1,7 @@ import { Messages } from '@salesforce/core'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import { Stage } from '../../impl/Stage'; -import SFPLogger, { COLOR_KEY_MESSAGE, ConsoleLogger } from '@dxatscale/sfp-logger'; +import SFPLogger, { COLOR_KEY_MESSAGE, ConsoleLogger } from '@flxblio/sfp-logger'; import { Flags } from '@oclif/core'; import { loglevel } from '../../flags/sfdxflags'; import { ZERO_BORDER_TABLE } from '../../ui/TableConstants'; @@ -12,9 +12,9 @@ import * as fs from 'fs-extra'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'impact_package'); +const messages = Messages.loadMessages('@flxblio/sfp', 'impact_package'); -export default class Package extends SfpowerscriptsCommand { +export default class Package extends sfpCommand { public static flags = { loglevel, basebranch: Flags.string({ diff --git a/packages/sfpowerscripts-cli/src/commands/impact/releaseconfig.ts b/packages/sfpowerscripts-cli/src/commands/impact/releaseconfig.ts index c3dac0b9d..b8e7bec9c 100644 --- a/packages/sfpowerscripts-cli/src/commands/impact/releaseconfig.ts +++ b/packages/sfpowerscripts-cli/src/commands/impact/releaseconfig.ts @@ -1,8 +1,8 @@ import { Messages } from '@salesforce/core'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import { Stage } from '../../impl/Stage'; import * as fs from 'fs-extra'; -import SFPLogger, { COLOR_KEY_MESSAGE, ConsoleLogger } from '@dxatscale/sfp-logger'; +import SFPLogger, { COLOR_KEY_MESSAGE, ConsoleLogger } from '@flxblio/sfp-logger'; import { Flags } from '@oclif/core'; import { loglevel } from '../../flags/sfdxflags'; import { ZERO_BORDER_TABLE } from '../../ui/TableConstants'; @@ -13,9 +13,9 @@ const Table = require('cli-table'); Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'impact_release_config'); +const messages = Messages.loadMessages('@flxblio/sfp', 'impact_release_config'); -export default class ReleaseConfig extends SfpowerscriptsCommand { +export default class ReleaseConfig extends sfpCommand { public static flags = { loglevel, basebranch: Flags.string({ diff --git a/packages/sfpowerscripts-cli/src/commands/metrics/report.ts b/packages/sfpowerscripts-cli/src/commands/metrics/report.ts index bf596628b..6b5b0a3a7 100644 --- a/packages/sfpowerscripts-cli/src/commands/metrics/report.ts +++ b/packages/sfpowerscripts-cli/src/commands/metrics/report.ts @@ -1,6 +1,6 @@ -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; -import SFPLogger, { LoggerLevel, COLOR_KEY_MESSAGE } from '@dxatscale/sfp-logger'; +import sfpCommand from '../../SfpCommand'; +import SFPStatsSender from '../../core/stats/SFPStatsSender'; +import SFPLogger, { LoggerLevel, COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; import { Messages } from '@salesforce/core'; import { Flags } from '@oclif/core'; import { loglevel } from '../../flags/sfdxflags'; @@ -10,9 +10,9 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'metrics_report'); +const messages = Messages.loadMessages('@flxblio/sfp', 'metrics_report'); -export default class Report extends SfpowerscriptsCommand { +export default class Report extends sfpCommand { public static description = messages.getMessage('commandDescription'); protected static requiresDevhubUsername = false; @@ -75,10 +75,10 @@ export default class Report extends SfpowerscriptsCommand { private validateEnvVars() { if ( !( - process.env.SFPOWERSCRIPTS_STATSD || - process.env.SFPOWERSCRIPTS_DATADOG || - process.env.SFPOWERSCRIPTS_NEWRELIC || - process.env.SFPOWERSCRIPTS_SPLUNK + process.env.sfp_STATSD || + process.env.sfp_DATADOG || + process.env.sfp_NEWRELIC || + process.env.sfp_SPLUNK ) ) { throw new Error('Environment variable not set for metrics. No metrics will be published.'); diff --git a/packages/sfpowerscripts-cli/src/commands/orchestrator/build.ts b/packages/sfpowerscripts-cli/src/commands/orchestrator/build.ts index 0d2c20cff..6cbcfb656 100644 --- a/packages/sfpowerscripts-cli/src/commands/orchestrator/build.ts +++ b/packages/sfpowerscripts-cli/src/commands/orchestrator/build.ts @@ -8,7 +8,7 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'build'); +const messages = Messages.loadMessages('@flxblio/sfp', 'build'); export default class Build extends BuildBase { public static description = messages.getMessage('commandDescription'); diff --git a/packages/sfpowerscripts-cli/src/commands/orchestrator/deploy.ts b/packages/sfpowerscripts-cli/src/commands/orchestrator/deploy.ts index 968610594..18844663b 100644 --- a/packages/sfpowerscripts-cli/src/commands/orchestrator/deploy.ts +++ b/packages/sfpowerscripts-cli/src/commands/orchestrator/deploy.ts @@ -1,6 +1,6 @@ -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import { Messages } from '@salesforce/core'; -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; +import SFPStatsSender from '../../core/stats/SFPStatsSender'; import DeployImpl, { DeploymentMode, DeployProps, DeploymentResult } from '../../impl/deploy/DeployImpl'; import { Stage } from '../../impl/Stage'; import SFPLogger, { @@ -8,21 +8,21 @@ import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE, COLOR_SUCCESS, -} from '@dxatscale/sfp-logger'; -import { COLOR_TIME } from '@dxatscale/sfp-logger'; -import getFormattedTime from '@dxatscale/sfpowerscripts.core/lib/utils/GetFormattedTime'; +} from '@flxblio/sfp-logger'; +import { COLOR_TIME } from '@flxblio/sfp-logger'; +import getFormattedTime from '../../core/utils/GetFormattedTime'; import { Flags } from '@oclif/core'; import { arrayFlagSfdxStyle, loglevel, logsgroupsymbol, requiredUserNameFlag } from '../../flags/sfdxflags'; -import { LoggerLevel } from '@dxatscale/sfp-logger'; +import { LoggerLevel } from '@flxblio/sfp-logger'; // Initialize Messages with the current plugin directory Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'deploy'); +const messages = Messages.loadMessages('@flxblio/sfp', 'deploy'); -export default class Deploy extends SfpowerscriptsCommand { +export default class Deploy extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [`$ sfp orchestrator:deploy -u `]; diff --git a/packages/sfpowerscripts-cli/src/commands/orchestrator/prepare.ts b/packages/sfpowerscripts-cli/src/commands/orchestrator/prepare.ts index 36910cd39..18aa7daa9 100644 --- a/packages/sfpowerscripts-cli/src/commands/orchestrator/prepare.ts +++ b/packages/sfpowerscripts-cli/src/commands/orchestrator/prepare.ts @@ -1,13 +1,13 @@ import { Messages } from '@salesforce/core'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import PrepareImpl from '../../impl/prepare/PrepareImpl'; -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; +import SFPStatsSender from '../../core/stats/SFPStatsSender'; import { Stage } from '../../impl/Stage'; import * as fs from 'fs-extra'; -import ScratchOrgInfoFetcher from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/services/fetchers/ScratchOrgInfoFetcher'; +import ScratchOrgInfoFetcher from '../../core/scratchorg/pool/services/fetchers/ScratchOrgInfoFetcher'; import Ajv from 'ajv'; import path = require('path'); -import { PoolErrorCodes } from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolError'; +import { PoolErrorCodes } from '../../core/scratchorg/pool/PoolError'; import SFPLogger, { LoggerLevel, COLOR_ERROR, @@ -15,19 +15,18 @@ import SFPLogger, { COLOR_SUCCESS, COLOR_TIME, COLOR_KEY_MESSAGE, -} from '@dxatscale/sfp-logger'; -import getFormattedTime from '@dxatscale/sfpowerscripts.core/lib/utils/GetFormattedTime'; -import { PoolConfig } from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolConfig'; -import { COLOR_WARNING } from '@dxatscale/sfp-logger'; -import PoolSchema from '@dxatscale/sfpowerscripts.core/resources/pooldefinition.schema.json'; -import SFPOrg from '@dxatscale/sfpowerscripts.core/lib/org/SFPOrg'; +} from '@flxblio/sfp-logger'; +import getFormattedTime from '../../core/utils/GetFormattedTime'; +import { PoolConfig } from '../../core/scratchorg/pool/PoolConfig'; +import { COLOR_WARNING } from '@flxblio/sfp-logger'; +import SFPOrg from '../../core/org/SFPOrg'; import { Flags } from '@oclif/core'; import { loglevel, logsgroupsymbol, targetdevhubusername } from '../../flags/sfdxflags'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'prepare'); +const messages = Messages.loadMessages('@flxblio/sfp', 'prepare'); -export default class Prepare extends SfpowerscriptsCommand { +export default class Prepare extends sfpCommand { protected static requiresDevhubUsername = true; protected static requiresProject = true; @@ -200,7 +199,11 @@ export default class Prepare extends SfpowerscriptsCommand { public validatePoolConfig(poolConfig: any) { let ajv = new Ajv({ allErrors: true }); - let validator = ajv.compile(PoolSchema); + let schema = fs.readJSONSync( + path.join(__dirname, '..', '..', '..', 'resources', 'schemas', 'pooldefinition.schema.json'), + { encoding: 'UTF-8' } + ); + let validator = ajv.compile(schema); let isSchemaValid = validator(poolConfig); if (!isSchemaValid) { let errorMsg: string = `The pool configuration is invalid, Please fix the following errors\n`; diff --git a/packages/sfpowerscripts-cli/src/commands/orchestrator/promote.ts b/packages/sfpowerscripts-cli/src/commands/orchestrator/promote.ts index ea585d4c9..205acb622 100644 --- a/packages/sfpowerscripts-cli/src/commands/orchestrator/promote.ts +++ b/packages/sfpowerscripts-cli/src/commands/orchestrator/promote.ts @@ -1,20 +1,20 @@ -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import { Messages } from '@salesforce/core'; -import PromoteUnlockedPackageImpl from '@dxatscale/sfpowerscripts.core/lib/package/promote/PromoteUnlockedPackageImpl' -import ArtifactFetcher from '@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactFetcher'; -import { ConsoleLogger } from '@dxatscale/sfp-logger'; -import SfpPackageBuilder from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageBuilder'; -import { PackageType } from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; +import PromoteUnlockedPackageImpl from '../../core/package/promote/PromoteUnlockedPackageImpl' +import ArtifactFetcher from '../../core/artifacts/ArtifactFetcher'; +import { ConsoleLogger } from '@flxblio/sfp-logger'; +import SfpPackageBuilder from '../../core/package/SfpPackageBuilder'; +import { PackageType } from '../../core/package/SfpPackage'; import { Flags, ux } from '@oclif/core'; import { loglevel, targetdevhubusername } from '../../flags/sfdxflags'; -import { LoggerLevel } from '@dxatscale/sfp-logger'; -import { COLOR_HEADER } from '@dxatscale/sfp-logger'; -import SFPLogger from '@dxatscale/sfp-logger'; +import { LoggerLevel } from '@flxblio/sfp-logger'; +import { COLOR_HEADER } from '@flxblio/sfp-logger'; +import SFPLogger from '@flxblio/sfp-logger'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'promote'); +const messages = Messages.loadMessages('@flxblio/sfp', 'promote'); -export default class Promote extends SfpowerscriptsCommand { +export default class Promote extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [`$ sfp orchestrator:promote -d path/to/artifacts -v `]; diff --git a/packages/sfpowerscripts-cli/src/commands/orchestrator/publish.ts b/packages/sfpowerscripts-cli/src/commands/orchestrator/publish.ts index 71b255523..1ceb82900 100644 --- a/packages/sfpowerscripts-cli/src/commands/orchestrator/publish.ts +++ b/packages/sfpowerscripts-cli/src/commands/orchestrator/publish.ts @@ -1,35 +1,35 @@ -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import { Messages } from '@salesforce/core'; import * as fs from 'fs-extra'; import path = require('path'); -import ArtifactFetcher, { Artifact } from '@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactFetcher'; -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; +import ArtifactFetcher, { Artifact } from '../../core/artifacts/ArtifactFetcher'; +import SFPStatsSender from '../../core/stats/SFPStatsSender'; import SFPLogger, { COLOR_ERROR, COLOR_HEADER, COLOR_KEY_MESSAGE, COLOR_SUCCESS, COLOR_TIME, -} from '@dxatscale/sfp-logger'; -import getFormattedTime from '@dxatscale/sfpowerscripts.core/lib/utils/GetFormattedTime'; -import defaultShell from '@dxatscale/sfpowerscripts.core/lib/utils/DefaultShell'; -import SfpPackage, { PackageType } from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; -import { ConsoleLogger } from '@dxatscale/sfp-logger'; -import SfpPackageBuilder from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageBuilder'; -import Git from '@dxatscale/sfpowerscripts.core/lib/git/Git'; +} from '@flxblio/sfp-logger'; +import getFormattedTime from '../../core/utils/GetFormattedTime'; +import defaultShell from '../../core/utils/DefaultShell'; +import SfpPackage, { PackageType } from '../../core/package/SfpPackage'; +import { ConsoleLogger } from '@flxblio/sfp-logger'; +import SfpPackageBuilder from '../../core/package/SfpPackageBuilder'; +import Git from '../../core/git/Git'; import GroupConsoleLogs from '../../ui/GroupConsoleLogs'; -import PackageVersionLister from '@dxatscale/sfpowerscripts.core/lib/package/version/PackageVersionLister'; -import SFPOrg from '@dxatscale/sfpowerscripts.core/lib/org/SFPOrg'; -import ExecuteCommand from '@dxatscale/sfdx-process-wrapper/lib/commandExecutor/ExecuteCommand'; -import { LoggerLevel } from '@dxatscale/sfp-logger'; -import GitTags from '@dxatscale/sfpowerscripts.core/lib/git/GitTags'; +import PackageVersionLister from '../../core/package/version/PackageVersionLister'; +import SFPOrg from '../../core/org/SFPOrg'; +import ExecuteCommand from '@flxblio/sfdx-process-wrapper/lib/commandExecutor/ExecuteCommand'; +import { LoggerLevel } from '@flxblio/sfp-logger'; +import GitTags from '../../core/git/GitTags'; import { arrayFlagSfdxStyle, loglevel, logsgroupsymbol, optionalDevHubFlag } from '../../flags/sfdxflags'; import { Flags } from '@oclif/core'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'publish'); +const messages = Messages.loadMessages('@flxblio/sfp', 'publish'); -export default class Promote extends SfpowerscriptsCommand { +export default class Promote extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [ @@ -92,7 +92,7 @@ export default class Promote extends SfpowerscriptsCommand { required: false, deprecated: { message: - '--npmtag is deprecated, sfpowerscripts will automatically tag the artifact with the branch name', + '--npmtag is deprecated, sfp will automatically tag the artifact with the branch name', }, }), npmrcpath: Flags.file({ @@ -140,7 +140,7 @@ export default class Promote extends SfpowerscriptsCommand { let artifactFilePaths = ArtifactFetcher.fetchArtifacts(this.flags.artifactdir); // Pattern captures two named groups, the "package" name and "version" number - let pattern = new RegExp('(?^.*)(?:_sfpowerscripts_artifact_)(?.*)(?:\\.zip)'); + let pattern = new RegExp('(?^.*)(?:_sfp_artifact_)(?.*)(?:\\.zip)'); for (let artifact of artifacts) { let packageName: string; let packageVersionNumber: string; @@ -261,7 +261,7 @@ export default class Promote extends SfpowerscriptsCommand { let artifactRootDirectory = path.dirname(sfpPackage.sourceDir); // NPM does not accept packages with uppercase characters - let name: string = sfpPackage.packageName.toLowerCase() + '_sfpowerscripts_artifact'; + let name: string = sfpPackage.packageName.toLowerCase() + '_sfp_artifact'; //Check whether the user has already passed in @ diff --git a/packages/sfpowerscripts-cli/src/commands/orchestrator/quickbuild.ts b/packages/sfpowerscripts-cli/src/commands/orchestrator/quickbuild.ts index b8aabaf6f..0ff13dabb 100644 --- a/packages/sfpowerscripts-cli/src/commands/orchestrator/quickbuild.ts +++ b/packages/sfpowerscripts-cli/src/commands/orchestrator/quickbuild.ts @@ -8,7 +8,7 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'quickbuild'); +const messages = Messages.loadMessages('@flxblio/sfp', 'quickbuild'); export default class QuickBuild extends BuildBase { public static description = messages.getMessage('commandDescription'); diff --git a/packages/sfpowerscripts-cli/src/commands/orchestrator/release.ts b/packages/sfpowerscripts-cli/src/commands/orchestrator/release.ts index 930184777..57f8d913b 100644 --- a/packages/sfpowerscripts-cli/src/commands/orchestrator/release.ts +++ b/packages/sfpowerscripts-cli/src/commands/orchestrator/release.ts @@ -1,6 +1,6 @@ -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import { LoggerLevel, Messages } from '@salesforce/core'; -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; +import SFPStatsSender from '../../core/stats/SFPStatsSender'; import ReleaseImpl, { ReleaseProps, ReleaseResult } from '../../impl/release/ReleaseImpl'; import ReleaseDefinition from '../../impl/release/ReleaseDefinition'; import ReleaseError from '../../errors/ReleaseError'; @@ -13,15 +13,15 @@ import SFPLogger, { COLOR_WARNING, COLOR_KEY_MESSAGE, ConsoleLogger, -} from '@dxatscale/sfp-logger'; +} from '@flxblio/sfp-logger'; import ReleaseDefinitionSchema from '../../impl/release/ReleaseDefinitionSchema'; import { arrayFlagSfdxStyle, loglevel, logsgroupsymbol, optionalDevHubFlag, requiredUserNameFlag } from '../../flags/sfdxflags'; import { Flags } from '@oclif/core'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'release'); +const messages = Messages.loadMessages('@flxblio/sfp', 'release'); -export default class Release extends SfpowerscriptsCommand { +export default class Release extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [ diff --git a/packages/sfpowerscripts-cli/src/commands/orchestrator/validate.ts b/packages/sfpowerscripts-cli/src/commands/orchestrator/validate.ts index 70f4567f8..45dc3ffcb 100644 --- a/packages/sfpowerscripts-cli/src/commands/orchestrator/validate.ts +++ b/packages/sfpowerscripts-cli/src/commands/orchestrator/validate.ts @@ -1,19 +1,19 @@ import { Messages } from '@salesforce/core'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import ValidateImpl, { ValidateAgainst, ValidateProps, ValidationMode } from '../../impl/validate/ValidateImpl'; -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; -import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE } from '@dxatscale/sfp-logger'; +import SFPStatsSender from '../../core/stats/SFPStatsSender'; +import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; import ValidateError from '../../errors/ValidateError'; import ValidateResult from '../../impl/validate/ValidateResult'; import * as fs from 'fs-extra'; import { arrayFlagSfdxStyle, loglevel, logsgroupsymbol, targetdevhubusername } from '../../flags/sfdxflags'; import { Flags } from '@oclif/core'; -import { LoggerLevel } from '@dxatscale/sfp-logger'; +import { LoggerLevel } from '@flxblio/sfp-logger'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'validate'); +const messages = Messages.loadMessages('@flxblio/sfp', 'validate'); -export default class Validate extends SfpowerscriptsCommand { +export default class Validate extends sfpCommand { protected static requiresProject = true; public static description = messages.getMessage('commandDescription'); diff --git a/packages/sfpowerscripts-cli/src/commands/orchestrator/validateAgainstOrg.ts b/packages/sfpowerscripts-cli/src/commands/orchestrator/validateAgainstOrg.ts index 1c5a9d6c1..0b9b1f2ec 100644 --- a/packages/sfpowerscripts-cli/src/commands/orchestrator/validateAgainstOrg.ts +++ b/packages/sfpowerscripts-cli/src/commands/orchestrator/validateAgainstOrg.ts @@ -1,8 +1,8 @@ import { LoggerLevel, Messages, Org } from '@salesforce/core'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import ValidateImpl, { ValidateAgainst, ValidateProps, ValidationMode } from '../../impl/validate/ValidateImpl'; -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; -import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE } from '@dxatscale/sfp-logger'; +import SFPStatsSender from '../../core/stats/SFPStatsSender'; +import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; import * as fs from 'fs-extra'; import ValidateError from '../../errors/ValidateError'; import ValidateResult from '../../impl/validate/ValidateResult'; @@ -11,9 +11,9 @@ import { Flags } from '@oclif/core'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'validateAgainstOrg'); +const messages = Messages.loadMessages('@flxblio/sfp', 'validateAgainstOrg'); -export default class ValidateAgainstOrg extends SfpowerscriptsCommand { +export default class ValidateAgainstOrg extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [`$ sfp orchestrator:validateAgainstOrg -u `]; diff --git a/packages/sfpowerscripts-cli/src/commands/package/data/create.ts b/packages/sfpowerscripts-cli/src/commands/package/data/create.ts index aa4b5fbb3..4631d2c51 100644 --- a/packages/sfpowerscripts-cli/src/commands/package/data/create.ts +++ b/packages/sfpowerscripts-cli/src/commands/package/data/create.ts @@ -1,14 +1,14 @@ import { Messages } from '@salesforce/core'; -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; -import { COLOR_SUCCESS, ConsoleLogger } from '@dxatscale/sfp-logger'; +import ProjectConfig from '../../../core/project/ProjectConfig' +import { COLOR_SUCCESS, ConsoleLogger } from '@flxblio/sfp-logger'; import PackageCreateCommand from '../../../PackageCreateCommand'; -import SfpPackage, { PackageType } from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; -import SfpPackageBuilder from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageBuilder'; +import SfpPackage, { PackageType } from '../../../core/package/SfpPackage'; +import SfpPackageBuilder from '../../../core/package/SfpPackageBuilder'; import { Flags } from '@oclif/core'; import { loglevel } from '../../../flags/sfdxflags'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'create_data_package'); +const messages = Messages.loadMessages('@flxblio/sfp', 'create_data_package'); export default class CreateDataPackage extends PackageCreateCommand { public static description = messages.getMessage('commandDescription'); diff --git a/packages/sfpowerscripts-cli/src/commands/package/data/install.ts b/packages/sfpowerscripts-cli/src/commands/package/data/install.ts index 945d078d7..51827ebb6 100644 --- a/packages/sfpowerscripts-cli/src/commands/package/data/install.ts +++ b/packages/sfpowerscripts-cli/src/commands/package/data/install.ts @@ -1,9 +1,9 @@ import { Messages } from '@salesforce/core'; import InstallPackageCommand from '../../../InstallPackageCommand'; -import { PackageInstallationStatus } from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/PackageInstallationResult'; -import SFPLogger, { ConsoleLogger, LoggerLevel } from '@dxatscale/sfp-logger'; -import SfpPackageInstaller from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageInstaller'; -import { SfpPackageInstallationOptions } from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/InstallPackage'; +import { PackageInstallationStatus } from '../../../core/package/packageInstallers/PackageInstallationResult'; +import SFPLogger, { ConsoleLogger, LoggerLevel } from '@flxblio/sfp-logger'; +import SfpPackageInstaller from '../../../core/package/SfpPackageInstaller'; +import { SfpPackageInstallationOptions } from '../../../core/package/packageInstallers/InstallPackage'; import { Flags } from '@oclif/core'; import { loglevel, requiredUserNameFlag } from '../../../flags/sfdxflags'; @@ -13,7 +13,7 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'install_data_package'); +const messages = Messages.loadMessages('@flxblio/sfp', 'install_data_package'); export default class InstallDataPackage extends InstallPackageCommand { public static description = messages.getMessage('commandDescription'); diff --git a/packages/sfpowerscripts-cli/src/commands/package/install.ts b/packages/sfpowerscripts-cli/src/commands/package/install.ts index aa36e0717..7dd37751d 100644 --- a/packages/sfpowerscripts-cli/src/commands/package/install.ts +++ b/packages/sfpowerscripts-cli/src/commands/package/install.ts @@ -1,16 +1,16 @@ import { Messages } from '@salesforce/core'; import InstallPackageCommand from '../../InstallPackageCommand'; -import { PackageInstallationStatus } from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/PackageInstallationResult'; -import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE, ConsoleLogger, LoggerLevel } from '@dxatscale/sfp-logger'; -import { SfpPackageInstallationOptions } from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/InstallPackage'; -import SfpPackageInstaller from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageInstaller'; +import { PackageInstallationStatus } from '../../core/package/packageInstallers/PackageInstallationResult'; +import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE, ConsoleLogger, LoggerLevel } from '@flxblio/sfp-logger'; +import { SfpPackageInstallationOptions } from '../../core/package/packageInstallers/InstallPackage'; +import SfpPackageInstaller from '../../core/package/SfpPackageInstaller'; import { Flags } from '@oclif/core'; import { loglevel, requiredUserNameFlag } from '../../flags/sfdxflags'; -import { PackageType } from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; +import { PackageType } from '../../core/package/SfpPackage'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'install_package'); +const messages = Messages.loadMessages('@flxblio/sfp', 'install_package'); export default class Install extends InstallPackageCommand { public static description = messages.getMessage('commandDescription'); diff --git a/packages/sfpowerscripts-cli/src/commands/package/source/create.ts b/packages/sfpowerscripts-cli/src/commands/package/source/create.ts index c36fb405f..0c2817fd6 100644 --- a/packages/sfpowerscripts-cli/src/commands/package/source/create.ts +++ b/packages/sfpowerscripts-cli/src/commands/package/source/create.ts @@ -1,13 +1,13 @@ import { Messages } from '@salesforce/core'; -import { COLOR_SUCCESS, ConsoleLogger } from '@dxatscale/sfp-logger'; +import { COLOR_SUCCESS, ConsoleLogger } from '@flxblio/sfp-logger'; import PackageCreateCommand from '../../../PackageCreateCommand'; -import SfpPackage, { PackageType } from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; -import SfpPackageBuilder from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageBuilder'; +import SfpPackage, { PackageType } from '../../../core/package/SfpPackage'; +import SfpPackageBuilder from '../../../core/package/SfpPackageBuilder'; import { Flags } from '@oclif/core'; import { loglevel } from '../../../flags/sfdxflags'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'create_source_package'); +const messages = Messages.loadMessages('@flxblio/sfp', 'create_source_package'); export default class CreateSourcePackage extends PackageCreateCommand { public static description = messages.getMessage('commandDescription'); diff --git a/packages/sfpowerscripts-cli/src/commands/package/source/install.ts b/packages/sfpowerscripts-cli/src/commands/package/source/install.ts index f553bc66a..14746c316 100644 --- a/packages/sfpowerscripts-cli/src/commands/package/source/install.ts +++ b/packages/sfpowerscripts-cli/src/commands/package/source/install.ts @@ -1,11 +1,11 @@ import { Messages } from '@salesforce/core'; import InstallPackageCommand from '../../../InstallPackageCommand'; import * as fs from 'fs-extra'; -import { PackageInstallationStatus } from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/PackageInstallationResult'; -import SFPLogger, { ConsoleLogger, LoggerLevel } from '@dxatscale/sfp-logger'; -import { DeploymentType } from '@dxatscale/sfpowerscripts.core/lib/deployers/DeploymentExecutor'; -import { SfpPackageInstallationOptions } from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/InstallPackage'; -import SfpPackageInstaller from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageInstaller'; +import { PackageInstallationStatus } from '../../../core/package/packageInstallers/PackageInstallationResult'; +import SFPLogger, { ConsoleLogger, LoggerLevel } from '@flxblio/sfp-logger'; +import { DeploymentType } from '../../../core/deployers/DeploymentExecutor'; +import { SfpPackageInstallationOptions } from '../../../core/package/packageInstallers/InstallPackage'; +import SfpPackageInstaller from '../../../core/package/SfpPackageInstaller'; import { loglevel, requiredUserNameFlag } from '../../../flags/sfdxflags'; import { Flags } from '@oclif/core'; @@ -14,7 +14,7 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'install_source_package'); +const messages = Messages.loadMessages('@flxblio/sfp', 'install_source_package'); export default class InstallSourcePackage extends InstallPackageCommand { public static description = messages.getMessage('commandDescription'); @@ -74,7 +74,7 @@ export default class InstallSourcePackage extends InstallPackageCommand { const wait_time: string = this.flags.waittime; const skipIfAlreadyInstalled = this.flags.skipifalreadyinstalled; - console.log('sfpowerscripts.Install Source Package To Org'); + console.log('sfp.Install Source Package To Org'); try { let options: SfpPackageInstallationOptions = { @@ -106,19 +106,19 @@ export default class InstallSourcePackage extends InstallPackageCommand { if (this.flags.refname) { fs.writeFileSync( '.env', - `${this.flags.refname}_sfpowerscripts_installsourcepackage_deployment_id=${result.deploy_id}\n`, + `${this.flags.refname}_sfp_installsourcepackage_deployment_id=${result.deploy_id}\n`, { flag: 'a' } ); console.log( - `${this.flags.refname}_sfpowerscripts_installsourcepackage_deployment_id=${result.deploy_id}` + `${this.flags.refname}_sfp_installsourcepackage_deployment_id=${result.deploy_id}` ); } else { fs.writeFileSync( '.env', - `sfpowerscripts_installsourcepackage_deployment_id=${result.deploy_id}\n`, + `sfp_installsourcepackage_deployment_id=${result.deploy_id}\n`, { flag: 'a' } ); - console.log(`sfpowerscripts_installsourcepackage_deployment_id=${result.deploy_id}`); + console.log(`sfp_installsourcepackage_deployment_id=${result.deploy_id}`); } } } diff --git a/packages/sfpowerscripts-cli/src/commands/package/unlocked/create.ts b/packages/sfpowerscripts-cli/src/commands/package/unlocked/create.ts index fc233f270..81972d059 100644 --- a/packages/sfpowerscripts-cli/src/commands/package/unlocked/create.ts +++ b/packages/sfpowerscripts-cli/src/commands/package/unlocked/create.ts @@ -1,8 +1,8 @@ import { Messages } from '@salesforce/core'; import PackageCreateCommand from '../../../PackageCreateCommand'; -import { COLOR_SUCCESS, ConsoleLogger } from '@dxatscale/sfp-logger'; -import SfpPackage from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; -import SfpPackageBuilder from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageBuilder'; +import { COLOR_SUCCESS, ConsoleLogger } from '@flxblio/sfp-logger'; +import SfpPackage from '../../../core/package/SfpPackage'; +import SfpPackageBuilder from '../../../core/package/SfpPackageBuilder'; import { loglevel, targetdevhubusername } from '../../../flags/sfdxflags'; import { Flags } from '@oclif/core'; @@ -11,7 +11,7 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'create_unlocked_package'); +const messages = Messages.loadMessages('@flxblio/sfp', 'create_unlocked_package'); export default class CreateUnlockedPackage extends PackageCreateCommand { public static description = messages.getMessage('commandDescription'); diff --git a/packages/sfpowerscripts-cli/src/commands/package/unlocked/install.ts b/packages/sfpowerscripts-cli/src/commands/package/unlocked/install.ts index e41fdb666..001e06743 100644 --- a/packages/sfpowerscripts-cli/src/commands/package/unlocked/install.ts +++ b/packages/sfpowerscripts-cli/src/commands/package/unlocked/install.ts @@ -1,9 +1,9 @@ import { Messages } from '@salesforce/core'; import InstallPackageCommand from '../../../InstallPackageCommand'; -import { PackageInstallationStatus } from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/PackageInstallationResult'; -import SFPLogger, { ConsoleLogger, LoggerLevel } from '@dxatscale/sfp-logger'; -import { SfpPackageInstallationOptions } from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/InstallPackage'; -import SfpPackageInstaller from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageInstaller'; +import { PackageInstallationStatus } from '../../../core/package/packageInstallers/PackageInstallationResult'; +import SFPLogger, { ConsoleLogger, LoggerLevel } from '@flxblio/sfp-logger'; +import { SfpPackageInstallationOptions } from '../../../core/package/packageInstallers/InstallPackage'; +import SfpPackageInstaller from '../../../core/package/SfpPackageInstaller'; import { Flags } from '@oclif/core'; import { loglevel, requiredUserNameFlag } from '../../../flags/sfdxflags'; @@ -13,7 +13,7 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'install_unlocked_package'); +const messages = Messages.loadMessages('@flxblio/sfp', 'install_unlocked_package'); export default class InstallUnlockedPackage extends InstallPackageCommand { public static description = messages.getMessage('commandDescription'); diff --git a/packages/sfpowerscripts-cli/src/commands/pool/delete.ts b/packages/sfpowerscripts-cli/src/commands/pool/delete.ts index eb049636e..8ef6856d3 100644 --- a/packages/sfpowerscripts-cli/src/commands/pool/delete.ts +++ b/packages/sfpowerscripts-cli/src/commands/pool/delete.ts @@ -1,12 +1,12 @@ import { Messages } from '@salesforce/core'; -import PoolDeleteImpl from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolDeleteImpl'; -import OrphanedOrgsDeleteImpl from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/OrphanedOrgsDeleteImpl'; -import ScratchOrg from '@dxatscale/sfpowerscripts.core/lib/scratchorg/ScratchOrg'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import PoolDeleteImpl from '../../core/scratchorg/pool/PoolDeleteImpl'; +import OrphanedOrgsDeleteImpl from '../../core/scratchorg/pool/OrphanedOrgsDeleteImpl'; +import ScratchOrg from '../../core/scratchorg/ScratchOrg'; +import sfpCommand from '../../SfpCommand'; import { ZERO_BORDER_TABLE } from '../../ui/TableConstants'; -import SFPLogger, { ConsoleLogger, LoggerLevel } from '@dxatscale/sfp-logger'; -import { COLOR_KEY_MESSAGE } from '@dxatscale/sfp-logger'; -import { COLOR_WARNING } from '@dxatscale/sfp-logger'; +import SFPLogger, { ConsoleLogger, LoggerLevel } from '@flxblio/sfp-logger'; +import { COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; +import { COLOR_WARNING } from '@flxblio/sfp-logger'; import { Flags } from '@oclif/core'; import { loglevel, orgApiVersionFlagSfdxStyle, targetdevhubusername } from '../../flags/sfdxflags'; const Table = require('cli-table'); @@ -16,17 +16,17 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'pool_delete'); +const messages = Messages.loadMessages('@flxblio/sfp', 'pool_delete'); -export default class Delete extends SfpowerscriptsCommand { +export default class Delete extends sfpCommand { public static description = messages.getMessage('commandDescription'); protected static requiresDevhubUsername = true; public static examples = [ - `$ sfpowerscripts pool:delete -t core `, - `$ sfpowerscripts pool:delete -t core -v devhub`, - `$ sfpowerscripts pool:delete --orphans -v devhub`, + `$ sfp pool:delete -t core `, + `$ sfp pool:delete -t core -v devhub`, + `$ sfp pool:delete --orphans -v devhub`, ]; public static flags = { diff --git a/packages/sfpowerscripts-cli/src/commands/pool/fetch.ts b/packages/sfpowerscripts-cli/src/commands/pool/fetch.ts index c96fe9472..02baaa0d8 100644 --- a/packages/sfpowerscripts-cli/src/commands/pool/fetch.ts +++ b/packages/sfpowerscripts-cli/src/commands/pool/fetch.ts @@ -1,18 +1,18 @@ import { Messages } from '@salesforce/core'; -import ScratchOrg from '@dxatscale/sfpowerscripts.core/lib/scratchorg/ScratchOrg'; +import ScratchOrg from '../../core/scratchorg/ScratchOrg'; import { AnyJson } from '@salesforce/ts-types'; -import PoolFetchImpl from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolFetchImpl'; +import PoolFetchImpl from '../../core/scratchorg/pool/PoolFetchImpl'; import * as fs from 'fs-extra'; -import SFPLogger, { LoggerLevel } from '@dxatscale/sfp-logger'; -import InstalledArtifactsDisplayer from '@dxatscale/sfpowerscripts.core/lib/display/InstalledArtifactsDisplayer'; -import InstalledPackageDisplayer from '@dxatscale/sfpowerscripts.core/lib/display/InstalledPackagesDisplayer'; -import { COLOR_KEY_MESSAGE } from '@dxatscale/sfp-logger'; -import SFPOrg from '@dxatscale/sfpowerscripts.core/lib/org/SFPOrg'; -import { COLOR_HEADER } from '@dxatscale/sfp-logger'; -import { COLOR_SUCCESS } from '@dxatscale/sfp-logger'; -import { COLOR_TIME } from '@dxatscale/sfp-logger'; -import getFormattedTime from '@dxatscale/sfpowerscripts.core/lib/utils/GetFormattedTime'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; +import InstalledArtifactsDisplayer from '../../core/display/InstalledArtifactsDisplayer'; +import InstalledPackageDisplayer from '../../core/display/InstalledPackagesDisplayer'; +import { COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; +import SFPOrg from '../../core/org/SFPOrg'; +import { COLOR_HEADER } from '@flxblio/sfp-logger'; +import { COLOR_SUCCESS } from '@flxblio/sfp-logger'; +import { COLOR_TIME } from '@flxblio/sfp-logger'; +import getFormattedTime from '../../core/utils/GetFormattedTime'; +import sfpCommand from '../../SfpCommand'; import { Flags, ux } from '@oclif/core'; import { loglevel, orgApiVersionFlagSfdxStyle, targetdevhubusername } from '../../flags/sfdxflags'; @@ -21,9 +21,9 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'scratchorg_poolFetch'); +const messages = Messages.loadMessages('@flxblio/sfp', 'scratchorg_poolFetch'); -export default class Fetch extends SfpowerscriptsCommand { +export default class Fetch extends sfpCommand { public static description = messages.getMessage('commandDescription'); protected static requiresDevhubUsername = true; diff --git a/packages/sfpowerscripts-cli/src/commands/pool/list.ts b/packages/sfpowerscripts-cli/src/commands/pool/list.ts index 3d24d8c36..e4761f392 100644 --- a/packages/sfpowerscripts-cli/src/commands/pool/list.ts +++ b/packages/sfpowerscripts-cli/src/commands/pool/list.ts @@ -1,10 +1,10 @@ import { AnyJson } from '@salesforce/ts-types'; -import poolListImpl from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolListImpl'; -import ScratchOrg from '@dxatscale/sfpowerscripts.core/lib/scratchorg/ScratchOrg'; -import SFPLogger, { LoggerLevel } from '@dxatscale/sfp-logger'; +import poolListImpl from '../../core/scratchorg/pool/PoolListImpl'; +import ScratchOrg from '../../core/scratchorg/ScratchOrg'; +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; import { Messages } from '@salesforce/core'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import { Flags, ux } from '@oclif/core'; import { loglevel, orgApiVersionFlagSfdxStyle, targetdevhubusername } from '../../flags/sfdxflags'; @@ -13,19 +13,19 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'scratchorg_poollist'); +const messages = Messages.loadMessages('@flxblio/sfp', 'scratchorg_poollist'); -export default class List extends SfpowerscriptsCommand { +export default class List extends sfpCommand { public static description = messages.getMessage('commandDescription'); protected static requiresDevhubUsername = true; public static enableJsonFlag = true public static examples = [ - `$ sfpowerscripts pool:list -t core `, - `$ sfpowerscripts pool:list -t core -v devhub`, - `$ sfpowerscripts pool:list -t core -v devhub -m`, - `$ sfpowerscripts pool:list -t core -v devhub -m -a`, + `$ sfp pool:list -t core `, + `$ sfp pool:list -t core -v devhub`, + `$ sfp pool:list -t core -v devhub -m`, + `$ sfp pool:list -t core -v devhub -m -a`, ]; public static flags = { diff --git a/packages/sfpowerscripts-cli/src/commands/pool/metrics/publish.ts b/packages/sfpowerscripts-cli/src/commands/pool/metrics/publish.ts index 2466c76a7..6a0251f65 100644 --- a/packages/sfpowerscripts-cli/src/commands/pool/metrics/publish.ts +++ b/packages/sfpowerscripts-cli/src/commands/pool/metrics/publish.ts @@ -1,10 +1,10 @@ -import SfpowerscriptsCommand from '../../../SfpowerscriptsCommand'; -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; -import PoolListImpl from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolListImpl'; -import ScratchOrg from '@dxatscale/sfpowerscripts.core/lib/scratchorg/ScratchOrg'; -import LimitsFetcher from '@dxatscale/sfpowerscripts.core/lib/limits/LimitsFetcher'; +import sfpCommand from '../../../SfpCommand'; +import SFPStatsSender from '../../../core/stats/SFPStatsSender'; +import PoolListImpl from '../../../core/scratchorg/pool/PoolListImpl'; +import ScratchOrg from '../../../core/scratchorg/ScratchOrg'; +import LimitsFetcher from '../../../core/limits/LimitsFetcher'; const Table = require('cli-table'); -import SFPLogger, { LoggerLevel, COLOR_KEY_MESSAGE } from '@dxatscale/sfp-logger'; +import SFPLogger, { LoggerLevel, COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; import { Messages } from '@salesforce/core'; import { loglevel, targetdevhubusername } from '../../../flags/sfdxflags'; @@ -13,9 +13,9 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'scratchorg_pool_metrics_publish'); +const messages = Messages.loadMessages('@flxblio/sfp', 'scratchorg_pool_metrics_publish'); -export default class Publish extends SfpowerscriptsCommand { +export default class Publish extends sfpCommand { public static description = messages.getMessage('commandDescription'); protected static requiresDevhubUsername = true; @@ -58,11 +58,11 @@ export default class Publish extends SfpowerscriptsCommand { SFPStatsSender.logGauge(`scratchorgs.active.remaining`, remainingActiveScratchOrgs, {target_org: devhubUserName}); SFPStatsSender.logGauge(`scratchorgs.daily.remaining`, remainingDailyScratchOrgs, {target_org: devhubUserName}); - table.push(['sfpowerscripts.scratchorgs.active.remaining', remainingActiveScratchOrgs, devhubUserName]); - table.push(['sfpowerscripts.scratchorgs.daily.remaining', remainingDailyScratchOrgs, devhubUserName]); + table.push(['sfp.scratchorgs.active.remaining', remainingActiveScratchOrgs, devhubUserName]); + table.push(['sfp.scratchorgs.daily.remaining', remainingDailyScratchOrgs, devhubUserName]); SFPStatsSender.logGauge(`pool.footprint`, nPooledScratchOrgs); - table.push(['sfpowerscripts.pool.footprint', nPooledScratchOrgs, '']); + table.push(['sfp.pool.footprint', nPooledScratchOrgs, '']); if (pools) { for (let pool of Object.entries(pools)) { @@ -71,10 +71,10 @@ export default class Publish extends SfpowerscriptsCommand { SFPStatsSender.logGauge('pool.inuse', pool[1].nInUse, { poolName: pool[0] }); SFPStatsSender.logGauge('pool.provisioning', pool[1].nProvisioningInProgress, { poolName: pool[0] }); - table.push(['sfpowerscripts.pool.total', pool[1].nTotal, pool[0]]); - table.push(['sfpowerscripts.pool.available', pool[1].nAvailable, pool[0]]); - table.push(['sfpowerscripts.pool.inuse', pool[1].nInUse, pool[0]]); - table.push(['sfpowerscripts.pool.provisioning', pool[1].nProvisioningInProgress, pool[0]]); + table.push(['sfp.pool.total', pool[1].nTotal, pool[0]]); + table.push(['sfp.pool.available', pool[1].nAvailable, pool[0]]); + table.push(['sfp.pool.inuse', pool[1].nInUse, pool[0]]); + table.push(['sfp.pool.provisioning', pool[1].nProvisioningInProgress, pool[0]]); } } @@ -114,10 +114,10 @@ export default class Publish extends SfpowerscriptsCommand { private validateEnvVars() { if ( !( - process.env.SFPOWERSCRIPTS_STATSD || - process.env.SFPOWERSCRIPTS_DATADOG || - process.env.SFPOWERSCRIPTS_NEWRELIC || - process.env.SFPOWERSCRIPTS_SPLUNK + process.env.sfp_STATSD || + process.env.sfp_DATADOG || + process.env.sfp_NEWRELIC || + process.env.sfp_SPLUNK ) ) { throw new Error('Environment variable not set for metrics. No metrics will be published.'); diff --git a/packages/sfpowerscripts-cli/src/commands/pool/org/delete.ts b/packages/sfpowerscripts-cli/src/commands/pool/org/delete.ts index 8b1c06573..98916f405 100644 --- a/packages/sfpowerscripts-cli/src/commands/pool/org/delete.ts +++ b/packages/sfpowerscripts-cli/src/commands/pool/org/delete.ts @@ -1,7 +1,7 @@ import { AnyJson } from '@salesforce/ts-types'; -import SfpowerscriptsCommand from '../../../SfpowerscriptsCommand'; -import PoolOrgDeleteImpl from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolOrgDeleteImpl'; -import SFPLogger from '@dxatscale/sfp-logger'; +import sfpCommand from '../../../SfpCommand'; +import PoolOrgDeleteImpl from '../../../core/scratchorg/pool/PoolOrgDeleteImpl'; +import SFPLogger from '@flxblio/sfp-logger'; import { Messages } from '@salesforce/core'; import { loglevel, @@ -16,9 +16,9 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'scratchorg_pool_org_delete'); +const messages = Messages.loadMessages('@flxblio/sfp', 'scratchorg_pool_org_delete'); -export default class Delete extends SfpowerscriptsCommand { +export default class Delete extends sfpCommand { public static description = messages.getMessage('commandDescription'); protected static requiresDevhubUsername = true; diff --git a/packages/sfpowerscripts-cli/src/commands/profile/merge.ts b/packages/sfpowerscripts-cli/src/commands/profile/merge.ts index 01d93482d..1955a40d1 100644 --- a/packages/sfpowerscripts-cli/src/commands/profile/merge.ts +++ b/packages/sfpowerscripts-cli/src/commands/profile/merge.ts @@ -1,10 +1,10 @@ import { Messages, Org } from '@salesforce/core'; import { isNil } from 'lodash'; -import { Sfpowerkit } from '@dxatscale/sfprofiles/lib/utils/sfpowerkit'; -import SFPLogger, { LoggerLevel } from '@dxatscale/sfp-logger'; -import ProfileRetriever from '@dxatscale/sfprofiles/lib/impl/metadata/retriever/profileRetriever'; -import ProfileMerge from '@dxatscale/sfprofiles/lib/impl/source/profileMerge'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import { Sfpowerkit } from '@flxblio/sfprofiles/lib/utils/sfpowerkit'; +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; +import ProfileRetriever from '@flxblio/sfprofiles/lib/impl/metadata/retriever/profileRetriever'; +import ProfileMerge from '@flxblio/sfprofiles/lib/impl/source/profileMerge'; +import sfpCommand from '../../SfpCommand'; import Table from 'cli-table'; import { ZERO_BORDER_TABLE } from '../../ui/TableConstants'; import { arrayFlagSfdxStyle, loglevel, orgApiVersionFlagSfdxStyle, requiredUserNameFlag } from '../../flags/sfdxflags'; @@ -12,9 +12,9 @@ import { Flags } from '@oclif/core'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'profile_merge'); +const messages = Messages.loadMessages('@flxblio/sfp', 'profile_merge'); -export default class Merge extends SfpowerscriptsCommand { +export default class Merge extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [ diff --git a/packages/sfpowerscripts-cli/src/commands/profile/reconcile.ts b/packages/sfpowerscripts-cli/src/commands/profile/reconcile.ts index cb9e85402..571af5b55 100644 --- a/packages/sfpowerscripts-cli/src/commands/profile/reconcile.ts +++ b/packages/sfpowerscripts-cli/src/commands/profile/reconcile.ts @@ -1,12 +1,12 @@ import { Messages, Org } from '@salesforce/core'; import * as _ from 'lodash'; -import { Sfpowerkit } from '@dxatscale/sfprofiles/lib/utils/sfpowerkit'; -import SFPLogger, { LoggerLevel } from '@dxatscale/sfp-logger'; -import { METADATA_INFO } from '@dxatscale/sfprofiles/lib/impl/metadata/metadataInfo'; +import { Sfpowerkit } from '@flxblio/sfprofiles/lib/utils/sfpowerkit'; +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; +import { METADATA_INFO } from '@flxblio/sfprofiles/lib/impl/metadata/metadataInfo'; import * as path from 'path'; -import ProfileReconcile from '@dxatscale/sfprofiles/lib/impl/source/profileReconcile'; -import MetadataFiles from '@dxatscale/sfprofiles/lib/impl/metadata/metadataFiles'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import ProfileReconcile from '@flxblio/sfprofiles/lib/impl/source/profileReconcile'; +import MetadataFiles from '@flxblio/sfprofiles/lib/impl/metadata/metadataFiles'; +import sfpCommand from '../../SfpCommand'; const Table = require('cli-table'); import { ZERO_BORDER_TABLE } from '../../ui/TableConstants'; import { Flags } from '@oclif/core'; @@ -15,9 +15,9 @@ import { arrayFlagSfdxStyle, loglevel, orgApiVersionFlagSfdxStyle, requiredUserN Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'profile_reconcile'); +const messages = Messages.loadMessages('@flxblio/sfp', 'profile_reconcile'); -export default class Reconcile extends SfpowerscriptsCommand { +export default class Reconcile extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [ diff --git a/packages/sfpowerscripts-cli/src/commands/profile/retrieve.ts b/packages/sfpowerscripts-cli/src/commands/profile/retrieve.ts index 0f55d6be9..d1b1853e0 100644 --- a/packages/sfpowerscripts-cli/src/commands/profile/retrieve.ts +++ b/packages/sfpowerscripts-cli/src/commands/profile/retrieve.ts @@ -1,20 +1,20 @@ import { Messages, Org } from '@salesforce/core'; import * as fs from 'fs-extra'; import { isNil } from 'lodash'; -import { Sfpowerkit } from '@dxatscale/sfprofiles/lib/utils/sfpowerkit'; -import ProfileSync from '@dxatscale/sfprofiles/lib/impl/source/profileSync'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import { Sfpowerkit } from '@flxblio/sfprofiles/lib/utils/sfpowerkit'; +import ProfileSync from '@flxblio/sfprofiles/lib/impl/source/profileSync'; +import sfpCommand from '../../SfpCommand'; import Table from 'cli-table'; import { ZERO_BORDER_TABLE } from '../../ui/TableConstants'; import { arrayFlagSfdxStyle, loglevel, orgApiVersionFlagSfdxStyle, requiredUserNameFlag } from '../../flags/sfdxflags'; import { Flags } from '@oclif/core'; -import SFPLogger, { COLOR_KEY_MESSAGE, COLOR_WARNING, LoggerLevel } from '@dxatscale/sfp-logger'; +import SFPLogger, { COLOR_KEY_MESSAGE, COLOR_WARNING, LoggerLevel } from '@flxblio/sfp-logger'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'profile_retrieve'); +const messages = Messages.loadMessages('@flxblio/sfp', 'profile_retrieve'); -export default class Retrieve extends SfpowerscriptsCommand { +export default class Retrieve extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [ diff --git a/packages/sfpowerscripts-cli/src/commands/releasedefinition/generate.ts b/packages/sfpowerscripts-cli/src/commands/releasedefinition/generate.ts index ab8928011..0bad08e90 100644 --- a/packages/sfpowerscripts-cli/src/commands/releasedefinition/generate.ts +++ b/packages/sfpowerscripts-cli/src/commands/releasedefinition/generate.ts @@ -1,14 +1,14 @@ -import { ConsoleLogger } from '@dxatscale/sfp-logger'; +import { ConsoleLogger } from '@flxblio/sfp-logger'; import { Messages } from '@salesforce/core'; import ReleaseDefinitionGenerator from '../../impl/release/ReleaseDefinitionGenerator'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import { Flags } from '@oclif/core'; import { loglevel } from '../../flags/sfdxflags'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'releasedefinition_generate'); +const messages = Messages.loadMessages('@flxblio/sfp', 'releasedefinition_generate'); -export default class Generate extends SfpowerscriptsCommand { +export default class Generate extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [ diff --git a/packages/sfpowerscripts-cli/src/commands/repo/patch.ts b/packages/sfpowerscripts-cli/src/commands/repo/patch.ts index 4eea2d5b4..1781b3286 100644 --- a/packages/sfpowerscripts-cli/src/commands/repo/patch.ts +++ b/packages/sfpowerscripts-cli/src/commands/repo/patch.ts @@ -1,29 +1,29 @@ import { Messages } from '@salesforce/core'; -import SfpowerscriptsCommand from '../../SfpowerscriptsCommand'; +import sfpCommand from '../../SfpCommand'; import ReleaseDefinition from '../../impl/release/ReleaseDefinition'; -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; +import ProjectConfig from '../../core/project/ProjectConfig'; import GroupConsoleLogs from '../../ui/GroupConsoleLogs'; import FetchImpl from '../../impl/artifacts/FetchImpl'; import ReleaseDefinitionSchema from '../../impl/release/ReleaseDefinitionSchema'; import path = require('path'); -import ArtifactFetcher, { Artifact } from '@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactFetcher'; -import SfpPackage, { PackageType } from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; -import SfpPackageBuilder from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageBuilder'; -import SFPLogger, { ConsoleLogger, Logger, LoggerLevel } from '@dxatscale/sfp-logger'; -import SfpPackageInquirer from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageInquirer'; -import Git from '@dxatscale/sfpowerscripts.core/lib/git/Git'; +import ArtifactFetcher, { Artifact } from '../../core/artifacts/ArtifactFetcher'; +import SfpPackage, { PackageType } from '../../core/package/SfpPackage'; +import SfpPackageBuilder from '../../core/package/SfpPackageBuilder'; +import SFPLogger, { ConsoleLogger, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import SfpPackageInquirer from '../../core/package/SfpPackageInquirer'; +import Git from '../../core/git/Git'; import * as fs from 'fs-extra'; -import { COLOR_KEY_MESSAGE } from '@dxatscale/sfp-logger'; +import { COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; import { EOL } from 'os'; -import { COLOR_WARNING } from '@dxatscale/sfp-logger'; -import { COLOR_HEADER } from '@dxatscale/sfp-logger'; +import { COLOR_WARNING } from '@flxblio/sfp-logger'; +import { COLOR_HEADER } from '@flxblio/sfp-logger'; import { Flags } from '@oclif/core'; import { arrayFlagSfdxStyle, loglevel, logsgroupsymbol } from '../../flags/sfdxflags'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'patch'); +const messages = Messages.loadMessages('@flxblio/sfp', 'patch'); -export default class Patch extends SfpowerscriptsCommand { +export default class Patch extends sfpCommand { public static description = messages.getMessage('commandDescription'); public static examples = [`$ sfp repo:patch -n `]; diff --git a/packages/sfpowerscripts-cli/src/core/apex/ApexClassFetcher.ts b/packages/sfpowerscripts-cli/src/core/apex/ApexClassFetcher.ts new file mode 100644 index 000000000..b7e20ea49 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apex/ApexClassFetcher.ts @@ -0,0 +1,28 @@ +import { Connection } from '@salesforce/core'; +import chunkCollection from "../queryHelper/ChunkCollection"; +import QueryHelper from '../queryHelper/QueryHelper'; + +export default class ApexClassFetcher { + constructor(private conn: Connection) {} + + /** + * Query Apex Classes by Name + * + * @param classNames + * @returns + */ + public async fetchApexClassByName(classNames: string[]): Promise<{ Id: string; Name: string }[]> { + let result: {Id: string; Name: string}[] = []; + + const chunks = chunkCollection(classNames); + for (const chunk of chunks) { + const formattedChunk = chunk.map(elem => `'${elem}'`).toString(); // transform into formatted string for query + const query = `SELECT ID, Name FROM ApexClass WHERE Name IN (${formattedChunk})`; + + const records = await QueryHelper.query<{ Id: string; Name: string }>(query, this.conn, false); + result = result.concat(records); + } + + return result; + } +} \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/src/core/apex/ApexTriggerFetcher.ts b/packages/sfpowerscripts-cli/src/core/apex/ApexTriggerFetcher.ts new file mode 100644 index 000000000..9b17c3d4d --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apex/ApexTriggerFetcher.ts @@ -0,0 +1,28 @@ +import { Connection } from '@salesforce/core'; +import chunkCollection from '../queryHelper/ChunkCollection'; +import QueryHelper from '../queryHelper/QueryHelper'; + +export default class ApexTriggerFetcher { + constructor(private conn: Connection) {} + + /** + * Query Triggers by Name + * + * @param triggerNames + * @returns + */ + public async fetchApexTriggerByName(triggerNames: string[]): Promise<{ Id: string; Name: string }[]> { + let result: {Id: string, Name: string}[] = []; + + const chunks = chunkCollection(triggerNames); + for (const chunk of chunks) { + const formattedChunk = chunk.map(elem => `'${elem}'`).toString(); // transform into formatted string for query + const query = `SELECT ID, Name FROM ApexTrigger WHERE Name IN (${formattedChunk})`; + + const records = await QueryHelper.query<{ Id: string; Name: string }>(query, this.conn, false); + result = result.concat(records); + } + + return result; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/apex/coverage/ApexCodeCoverageAggregateFetcher.ts b/packages/sfpowerscripts-cli/src/core/apex/coverage/ApexCodeCoverageAggregateFetcher.ts new file mode 100644 index 000000000..f1f543365 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apex/coverage/ApexCodeCoverageAggregateFetcher.ts @@ -0,0 +1,42 @@ +import { Connection } from '@salesforce/core'; +import chunkCollection from '../../queryHelper/ChunkCollection'; +import QueryHelper from '../../queryHelper/QueryHelper'; + +export default class ApexCodeCoverageAggregateFetcher { + constructor(private conn: Connection) {} + + /** + * Query ApexCodeCoverageAggregate by list of ApexClassorTriggerId + * @param listOfApexClassOrTriggerId + * @returns + */ + public async fetchACCAById(listOfApexClassOrTriggerId: string[]): Promise<{ + ApexClassOrTriggerId: string; + NumLinesCovered: number; + NumLinesUncovered: number; + Coverage: any; + }[]> { + let result: { + ApexClassOrTriggerId: string; + NumLinesCovered: number; + NumLinesUncovered: number; + Coverage: any; + }[] = []; + + const chunks = chunkCollection(listOfApexClassOrTriggerId); + for (const chunk of chunks) { + const formattedChunk = chunk.map(elem => `'${elem}'`).toString(); + let query = `SELECT ApexClassorTriggerId, NumLinesCovered, NumLinesUncovered, Coverage FROM ApexCodeCoverageAggregate WHERE ApexClassorTriggerId IN (${formattedChunk})`; + + const records = await QueryHelper.query<{ + ApexClassOrTriggerId: string; + NumLinesCovered: number; + NumLinesUncovered: number; + Coverage: any; + }>(query, this.conn, true); + result = result.concat(records); + } + + return result; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/apex/coverage/IndividualClassCoverage.ts b/packages/sfpowerscripts-cli/src/core/apex/coverage/IndividualClassCoverage.ts new file mode 100644 index 000000000..978361887 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apex/coverage/IndividualClassCoverage.ts @@ -0,0 +1,76 @@ +import SFPLogger, { Logger, LoggerLevel } from "@flxblio/sfp-logger" + +export default class IndividualClassCoverage { + public constructor(private codeCoverage: any, private logger: Logger) {} + + public getIndividualClassCoverage(classesToBeValidated?:string[]): ClassCoverage[] { + let individualClassCoverage: { + name: string; + coveredPercent: number; + }[] = []; + + // Return every class in coverage json if test level is not RunAllTestsInPackage + individualClassCoverage = this.codeCoverage.map((cls) => { + return { name: cls.name, coveredPercent: cls.coveredPercent }; + }); + + // Filter individualClassCoverage based on classesToBeValidated + if(classesToBeValidated && classesToBeValidated.length > 0) + individualClassCoverage = individualClassCoverage.filter((cls) => { + return classesToBeValidated.includes(cls.name); + }); + + + return individualClassCoverage; + } + + public validateIndividualClassCoverage( + individualClassCoverage: ClassCoverage[], + coverageThreshold?: number + ): { + result: boolean; + message: string; + classesCovered?: ClassCoverage[]; + classesWithInvalidCoverage?: ClassCoverage[]; + } { + if (coverageThreshold < 75) { + SFPLogger.log('Setting minimum coverage percentage to 75%.', LoggerLevel.INFO, this.logger); + coverageThreshold = 75; + } + + SFPLogger.log( + `Validating individual classes for code coverage greater than ${coverageThreshold} percent`, + LoggerLevel.INFO, + this.logger + ); + let classesWithInvalidCoverage = individualClassCoverage.filter((cls) => { + return cls.coveredPercent < coverageThreshold; + }); + + if (classesWithInvalidCoverage.length > 0) { + return { + result: false, + message: 'There are classes which do not satisfy the individual coverage requirements', + classesCovered: individualClassCoverage, + classesWithInvalidCoverage: classesWithInvalidCoverage, + }; + } else + return { + result: true, + message: 'All classes in this test run meet the required coverage threshold', + classesCovered: individualClassCoverage, + }; + } +} + +export type CoverageOptions = { + isPackageCoverageToBeValidated: boolean; + isIndividualClassCoverageToBeValidated: boolean; + coverageThreshold: number; + classesToBeValidated?: string[]; +}; + +export type ClassCoverage = { + name: string; + coveredPercent: number; +}; diff --git a/packages/sfpowerscripts-cli/src/core/apex/parser/ApexTypeFetcher.ts b/packages/sfpowerscripts-cli/src/core/apex/parser/ApexTypeFetcher.ts new file mode 100644 index 000000000..a4d7eb57e --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apex/parser/ApexTypeFetcher.ts @@ -0,0 +1,134 @@ +import * as fs from 'fs-extra'; +const path = require('path'); +const { globSync } = require('glob'); + +import ApexTypeListener from './listeners/ApexTypeListener'; + +import { + ApexLexer, + ApexParser, + ApexParserListener, + CaseInsensitiveInputStream, + ThrowingErrorListener, + CommonTokenStream, + ParseTreeWalker, +} from 'apex-parser'; +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; +import { ApexClasses } from '../../package/SfpPackage'; + +/** + * Get Apex type of cls files in a search directory. + * Sorts files into classes, test classes and interfaces. + */ +export default class ApexTypeFetcher { + private apexSortedByType: ApexSortedByType = { + class: [], + testClass: [], + interface: [], + parseError: [], + }; + + constructor(private searchDir: string) {} + + public getClassesClassifiedByType(): ApexSortedByType { + let clsFiles: string[]; + if (fs.existsSync(this.searchDir)) { + clsFiles = globSync(`**/*.cls`, { + cwd: this.searchDir, + absolute: true, + }); + } else { + throw new Error(`Search directory does not exist`); + } + + for (let clsFile of clsFiles) { + let clsPayload: string = fs.readFileSync(clsFile, 'utf8'); + let fileDescriptor: FileDescriptor = { + name: path.basename(clsFile, '.cls'), + filepath: clsFile, + }; + + // Parse cls file + let compilationUnitContext; + try { + let lexer = new ApexLexer(new CaseInsensitiveInputStream(clsFile, clsPayload)); + let tokens: CommonTokenStream = new CommonTokenStream(lexer); + + let parser = new ApexParser(tokens); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowingErrorListener()); + + compilationUnitContext = parser.compilationUnit(); + } catch (err) { + SFPLogger.log(`Failed to parse ${clsFile} in ${this.searchDir}`, LoggerLevel.WARN); + SFPLogger.log(err.message, LoggerLevel.WARN); + + fileDescriptor.error = err; + this.apexSortedByType.parseError.push(fileDescriptor); + + continue; + } + + let apexTypeListener: ApexTypeListener = new ApexTypeListener(); + + // Walk parse tree to determine Apex type + ParseTreeWalker.DEFAULT.walk(apexTypeListener as ApexParserListener, compilationUnitContext); + + let apexType = apexTypeListener.getApexType(); + + if (apexType.class) { + this.apexSortedByType.class.push(fileDescriptor); + if (apexType.testClass) { + this.apexSortedByType.testClass.push(fileDescriptor); + } + } else if (apexType.interface) { + this.apexSortedByType.interface.push(fileDescriptor); + } else { + fileDescriptor.error = { message: 'Unknown Apex Type' }; + this.apexSortedByType.parseError.push(fileDescriptor); + } + } + return this.apexSortedByType; + } + + public getTestClasses(): ApexClasses { + let testClassNames: ApexClasses = this.apexSortedByType.testClass.map((fileDescriptor) => fileDescriptor.name); + return testClassNames; + } + + public getClassesOnlyExcludingTestsAndInterfaces(): ApexClasses { + let packageClasses: ApexClasses = this.apexSortedByType.class.map((fileDescriptor) => fileDescriptor.name); + + if (packageClasses != null) { + let testClassesInPackage: ApexClasses = this.apexSortedByType.testClass.map( + (fileDescriptor) => fileDescriptor.name + ); + if (testClassesInPackage != null && testClassesInPackage.length > 0) + packageClasses = packageClasses.filter((item) => !testClassesInPackage.includes(item)); + + let interfacesInPackage: ApexClasses = this.apexSortedByType.testClass.map( + (fileDescriptor) => fileDescriptor.name + ); + if (interfacesInPackage != null && interfacesInPackage.length > 0) + packageClasses = packageClasses.filter((item) => !interfacesInPackage.includes(item)); + + let parseError: ApexClasses = this.apexSortedByType.parseError.map((fileDescriptor) => fileDescriptor.name); + if (parseError != null && parseError.length > 0) + packageClasses = packageClasses.filter((item) => !parseError.includes(item)); + } + return packageClasses; + } +} + +export type ApexSortedByType = { + class: FileDescriptor[]; + testClass: FileDescriptor[]; + interface: FileDescriptor[]; + parseError: FileDescriptor[]; +}; + +export type FileDescriptor = { + name: string; + filepath: string; + error?: any; +}; diff --git a/packages/sfpowerscripts-cli/src/core/apex/parser/listeners/ApexTypeListener.ts b/packages/sfpowerscripts-cli/src/core/apex/parser/listeners/ApexTypeListener.ts new file mode 100644 index 000000000..12e6e2181 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apex/parser/listeners/ApexTypeListener.ts @@ -0,0 +1,38 @@ +import { + ApexParserListener, + AnnotationContext, + InterfaceDeclarationContext, + ClassDeclarationContext, +} from 'apex-parser'; + +export default class ApexTypeListener implements ApexParserListener { + private apexType: ApexType = { + class: false, + testClass: false, + interface: false, + }; + + enterAnnotation(ctx: AnnotationContext): void { + if (ctx.text.toUpperCase().startsWith('@ISTEST')) { + this.apexType.testClass= true; + } + } + + enterInterfaceDeclaration(ctx: InterfaceDeclarationContext): void { + this.apexType.interface = true; + } + + enterClassDeclaration(ctx: ClassDeclarationContext): void { + this.apexType.class = true; + } + + public getApexType(): ApexType { + return this.apexType; + } +} + +interface ApexType { + class: boolean; + testClass: boolean; + interface: boolean; +} diff --git a/packages/sfpowerscripts-cli/src/core/apextest/ApexTestSuite.ts b/packages/sfpowerscripts-cli/src/core/apextest/ApexTestSuite.ts new file mode 100644 index 000000000..ca2dbd0e0 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apextest/ApexTestSuite.ts @@ -0,0 +1,30 @@ + +const fs = require('fs-extra'); +import path from 'path'; +import xml2json from '../utils/xml2json'; +import { globSync } from 'glob'; + +export default class ApexTestSuite { + public constructor(private sourceDir: string, private suiteName: string) {} + + public async getConstituentClasses(): Promise { + let testSuitePaths: string[] = globSync(`**${this.suiteName}.testSuite-meta.xml`, { + cwd: this.sourceDir, + absolute: true, + }); + + console.log('testSuitePaths',testSuitePaths); + + if (!testSuitePaths[0]) throw new Error(`Apex Test Suite ${this.suiteName} not found`); + + let apex_test_suite: any = await xml2json(fs.readFileSync(path.resolve(testSuitePaths[0]))); + + if (Array.isArray(apex_test_suite.ApexTestSuite.testClassName)) { + return apex_test_suite.ApexTestSuite.testClassName; + } else { + let testClassess = new Array(); + testClassess.push(apex_test_suite.ApexTestSuite.testClassName); + return testClassess; + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/apextest/ClearCodeCoverage.ts b/packages/sfpowerscripts-cli/src/core/apextest/ClearCodeCoverage.ts new file mode 100644 index 000000000..bd6fb7712 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apextest/ClearCodeCoverage.ts @@ -0,0 +1,53 @@ +import { Connection, Org } from '@salesforce/core'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import QueryHelper from '../queryHelper/QueryHelper'; +import { chunkArray } from '../utils/ChunkArray'; +const CODECOV_AGGREGATE_QUERY = `SELECT Id FROM ApexCodeCoverageAggregate`; +const APEX_TEST_RESULT_QUERY = `SELECT Id FROM ApexTestResult`; +import { delay } from '../utils/Delay'; + +export default class ClearTestResults { + private conn: Connection; + + public constructor(private org: Org, private logger: Logger) {} + /** + * Clear coverage and test results + */ + public async clear() { + this.conn = this.org.getConnection(); + + SFPLogger.log(`Clearing Coverage Results`, LoggerLevel.DEBUG, this.logger); + let codeCoverageAggregate = await QueryHelper.query(CODECOV_AGGREGATE_QUERY, this.conn, true); + await this.deleteRecords('ApexCodeCoverageAggregate', codeCoverageAggregate); + SFPLogger.log(`Cleared Coverage Results`, LoggerLevel.DEBUG, this.logger); + + SFPLogger.log(`Clearing Test Results`, LoggerLevel.DEBUG, this.logger); + let testResults = await QueryHelper.query(APEX_TEST_RESULT_QUERY, this.conn, true); + await this.deleteRecords('ApexTestResult', testResults); + SFPLogger.log(`Cleared Test Results`, LoggerLevel.DEBUG, this.logger); + + SFPLogger.log(`Cleared Existing Coverage and Test Results`, LoggerLevel.INFO, this.logger); + + //allow org to catchup + await delay(10000); + } + + private async deleteRecords(objectType: string, records: any[]) { + if (records && records.length > 0) { + let idsList: string[] = records.map((elem) => elem.Id); + let errors = []; + for (let idsToDelete of chunkArray(2000, idsList)) { + const deleteResults: any = await this.conn.tooling.destroy(objectType, idsToDelete); + deleteResults.forEach((elem) => { + if (!elem.success) { + errors = errors.concat(elem.errors); + } + }); + } + + if (errors.length > 0) { + throw new Error(JSON.stringify(errors)); + } + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/apextest/ImpactedApexTestClassFetcher.ts b/packages/sfpowerscripts-cli/src/core/apextest/ImpactedApexTestClassFetcher.ts new file mode 100644 index 000000000..a271a79b7 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apextest/ImpactedApexTestClassFetcher.ts @@ -0,0 +1,90 @@ +import * as _ from 'lodash'; +import ApexDepedencyCheckImpl from "@flxblio/apexlink/lib/ApexDepedencyCheckImpl" +import Component from '../dependency/Component'; +import SFPLogger, { COLOR_KEY_MESSAGE, COLOR_WARNING, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import SfpPackage, { PackageType } from '../package/SfpPackage'; +import path from 'path'; + +export default class ImpactedApexTestClassFetcher { + public constructor( + private sfpPackage: SfpPackage, + private changedComponents: Component[], + private logger: Logger, + private loglevel?: LoggerLevel + ) {} + + public async getImpactedTestClasses(): Promise { + + let invalidatedClasses = []; + let invalidatedTestClasses = []; + + try + { + let validatedChangedComponents = this.changedComponents.filter( + (component) => component.package == this.sfpPackage.packageName + ); + + SFPLogger.log(`Computing impacted apex class and associated tests`, LoggerLevel.INFO, this.logger); + SFPLogger.log(`Changed components ${JSON.stringify(validatedChangedComponents)}`, LoggerLevel.INFO, this.logger); + + + + let apexLinkImpl = new ApexDepedencyCheckImpl(this.logger,path.join(this.sfpPackage.workingDirectory, this.sfpPackage.packageDirectory)); + let dependencies = (await apexLinkImpl.execute()).dependencies; + + if(dependencies.length==0) + { + //go for another attempt + SFPLogger.log(`No dependencies found, retrying with apexlink,Retrying again`, LoggerLevel.INFO,this.logger); + apexLinkImpl = new ApexDepedencyCheckImpl(this.logger,this.sfpPackage.workingDirectory); + dependencies = (await apexLinkImpl.execute()).dependencies; + } + + SFPLogger.log(`Dependencies: ${JSON.stringify(dependencies)}`, LoggerLevel.INFO,this.logger); + + //compute invalidated apex classes + for (const changedComponent of validatedChangedComponents) { + //If the component is a permset or profile, add every test class + //There is a change in security model, add all test classes as invalidated + // Temoorarily disabled this check as it is not working as expected + if (this.sfpPackage.packageType != PackageType.Diff && _.includes(['Profile', 'PermissionSet', 'SharingRules'], changedComponent.type)) { + SFPLogger.log( + COLOR_WARNING(`Change in Security Model, pushing all test classes through`), + LoggerLevel.INFO, + this.logger + ); + invalidatedClasses = invalidatedClasses.concat(this.sfpPackage.apexTestClassses); + break; + } + + for (const apexClass of dependencies) { + // push any apex class or test class that is changed, which would then get filtered during subsequent matching with test class + if (apexClass.name == changedComponent.fullName) invalidatedClasses.push(apexClass.name); + + // push any apex class or test class who is dependent on the changed entity + for (const dependsOn of apexClass.dependencies) { + if (changedComponent.fullName == dependsOn) invalidatedClasses.push(apexClass.name); + } + } + } + + SFPLogger.log(`Impacted classes: ${COLOR_KEY_MESSAGE(invalidatedClasses)}`, LoggerLevel.INFO, this.logger); + //Filter all apex classes by means of whats is detected in test classes list + invalidatedTestClasses = _.intersection(invalidatedClasses, this.sfpPackage.apexTestClassses); + SFPLogger.log( + `Impacted test classes: ${COLOR_KEY_MESSAGE(invalidatedTestClasses)}`, + LoggerLevel.INFO, + this.logger + ); + }catch(error) + { + SFPLogger.log( + `Unable to compute impacted test classes, defaulting to all test classes due to error ${error}`, + LoggerLevel.ERROR, + this.logger + ); + invalidatedClasses = this.sfpPackage.apexTestClassses; + } + return invalidatedTestClasses; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/apextest/JSONReporter.ts b/packages/sfpowerscripts-cli/src/core/apextest/JSONReporter.ts new file mode 100644 index 000000000..315f3d47a --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apextest/JSONReporter.ts @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2020, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +import { ApexTestResultData, ApexTestResultOutcome, TestResult } from '@salesforce/apex-node'; + +export type CliJsonFormat = { + summary: object; + tests: CliTestResult[]; + coverage?: CliCoverageResult; +}; + +type CliTestResult = { + Id: string; + QueueItemId: string; + StackTrace: string; + Message: string; + AsyncApexJobId: string; + MethodName: string; + Outcome: ApexTestResultOutcome; + ApexClass: { Id: string; Name: string; NamespacePrefix: string }; + RunTime: number; + FullName: string; +}; + +type ClassCoverage = { + id: string; + name: string; + totalLines: number; + lines: {}; + totalCovered: number; + coveredPercent: number; +}; + +type PerClassCoverage = { + ApexTestClass: { + Id: string; + Name: string; + }; + Coverage?: { coveredLines: number[]; uncoveredLines: number[] }; + TestMethodName: string; + NumLinesCovered: number; + ApexClassOrTrigger: { + Id: string; + Name: string; + }; + NumLinesUncovered: number; +}; + +type CliCoverageResult = { + coverage: ClassCoverage[]; + records: PerClassCoverage[]; + summary: { + totalLines: number; + coveredLines: number; + testRunCoverage: string; + orgWideCoverage: string; + }; +}; + +const skippedProperties = ['skipRate', 'coveredLines', 'totalLines']; +const timeProperties = ['testExecutionTimeInMs', 'testTotalTimeInMs', 'commandTimeInMs']; + +export class JsonReporter { + public format( + result: TestResult + ): { + summary: object; + tests: CliTestResult[]; + coverage?: CliCoverageResult; + } { + return { + summary: this.formatSummary(result), + tests: this.formatTestResults(result.tests), + ...(result.codecoverage + ? { + coverage: this.formatCoverage(result), + } + : {}), + }; + } + + private formatSummary(testResult: TestResult): object { + const summary = {}; + + Object.entries(testResult.summary).forEach(([key, value]) => { + if (skippedProperties.includes(key)) { + return; + } + + if (timeProperties.includes(key)) { + key = key.replace('InMs', ''); + value = `${value} ms`; + } + + Object.assign(summary, { [key]: value }); + }); + + return summary; + } + + private formatTestResults(testResults: ApexTestResultData[]): CliTestResult[] { + return testResults.map((test) => { + return { + Id: test.id, + QueueItemId: test.queueItemId, + StackTrace: test.stackTrace, + Message: test.message, + AsyncApexJobId: test.asyncApexJobId, + MethodName: test.methodName, + Outcome: test.outcome, + ApexClass: { + Id: test.apexClass.id, + Name: test.apexClass.name, + NamespacePrefix: test.apexClass.namespacePrefix, + }, + RunTime: test.runTime, + FullName: test.fullName, + }; + }) as CliTestResult[]; + } + + private formatCoverage(testResult: TestResult): CliCoverageResult { + const formattedCov = { + coverage: [], + records: [], + summary: { + totalLines: testResult.summary.totalLines, + coveredLines: testResult.summary.coveredLines, + orgWideCoverage: testResult.summary.orgWideCoverage, + testRunCoverage: testResult.summary.testRunCoverage, + }, + } as CliCoverageResult; + + if (testResult.codecoverage) { + formattedCov.coverage = testResult.codecoverage.map((cov) => { + const lines: { [key: number]: number } = {}; + cov.coveredLines.forEach((covLine) => (lines[covLine] = 1)); + cov.uncoveredLines.forEach((uncovLine) => (lines[uncovLine] = 0)); + + return { + id: cov.apexId, + name: cov.name, + totalLines: cov.numLinesCovered + cov.numLinesUncovered, + lines, + totalCovered: cov.numLinesCovered, + coveredPercent: parseInt(cov.percentage), + } as ClassCoverage; + }); + + testResult.tests.forEach((test) => { + if (test.perClassCoverage) { + test.perClassCoverage.forEach((perClassCov) => { + formattedCov.records.push({ + ApexTestClass: { Id: test.id, Name: test.apexClass.name }, + ...(perClassCov.coverage ? { Coverage: perClassCov.coverage } : {}), + TestMethodName: test.methodName, + NumLinesCovered: perClassCov.numLinesCovered, + ApexClassOrTrigger: { + Id: perClassCov.apexClassOrTriggerId, + Name: perClassCov.apexClassOrTriggerName, + }, + NumLinesUncovered: perClassCov.numLinesUncovered, + } as PerClassCoverage); + }); + } + }); + } + + return formattedCov; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/apextest/TestOptions.ts b/packages/sfpowerscripts-cli/src/core/apextest/TestOptions.ts new file mode 100644 index 000000000..15faf1fcd --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apextest/TestOptions.ts @@ -0,0 +1,73 @@ +import * as _ from 'lodash'; +import SfpPackage from '../package/SfpPackage'; + +export class TestOptions { + synchronous?: boolean; + wait_time: number; + outputdir: string; + testLevel: TestLevel; +} + +export class RunSpecifiedTestsOption extends TestOptions { + specifiedTests: string; + constructor(wait_time: number, outputdir: string, specifiedTests: string, synchronous?: boolean) { + super(); + this.synchronous = synchronous ? synchronous : false; + this.wait_time = wait_time ? wait_time : 60; + this.outputdir = outputdir; + this.specifiedTests = specifiedTests; + this.testLevel = TestLevel.RunSpecifiedTests; + } +} + +export class RunApexTestSuitesOption extends TestOptions { + suiteNames: string; + constructor(wait_time: number, outputdir: string, suiteNames: string, pkg?: string, synchronous?: boolean) { + super(); + this.synchronous = synchronous ? synchronous : false; + this.wait_time = wait_time ? wait_time : 60; + this.outputdir = outputdir; + this.suiteNames = suiteNames; + this.testLevel = TestLevel.RunApexTestSuite; + } +} + +export class RunLocalTests extends TestOptions { + constructor(wait_time: number, outputdir: string, synchronous?: boolean) { + super(); + this.synchronous = synchronous ? synchronous : false; + this.wait_time = wait_time ? wait_time : 60; + this.outputdir = outputdir; + this.testLevel = TestLevel.RunLocalTests; + } +} + +export class RunAllTestsInOrg extends TestOptions { + constructor(wait_time: number, outputdir: string, synchronous?: boolean) { + super(); + this.synchronous = synchronous ? synchronous : false; + this.wait_time = wait_time ? wait_time : 60; + this.outputdir = outputdir; + this.testLevel = TestLevel.RunAllTestsInOrg; + } +} + +export class RunAllTestsInPackageOptions extends RunSpecifiedTestsOption { + public constructor(private _sfppackage: SfpPackage, wait_time: number, outputdir: string) { + super(wait_time, outputdir, _sfppackage.apexTestClassses.toString(), false); + this.synchronous = _sfppackage.packageDescriptor.testSynchronous == true ? true : false; + } + + public get sfppackage() { + return this._sfppackage; + } +} + +export enum TestLevel { + RunNoTests = 'NoTestRun', + RunSpecifiedTests = 'RunSpecifiedTests', + RunApexTestSuite = 'RunApexTestSuite', + RunLocalTests = 'RunLocalTests', + RunAllTestsInOrg = 'RunAllTestsInOrg', + RunAllTestsInPackage = 'RunAllTestsInPackage', +} diff --git a/packages/sfpowerscripts-cli/src/core/apextest/TestReportDisplayer.ts b/packages/sfpowerscripts-cli/src/core/apextest/TestReportDisplayer.ts new file mode 100644 index 000000000..a66e55dc3 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apextest/TestReportDisplayer.ts @@ -0,0 +1,118 @@ +import { RunAllTestsInPackageOptions, RunApexTestSuitesOption, TestOptions } from './TestOptions'; +import SFPLogger, { COLOR_ERROR, COLOR_SUCCESS, LoggerLevel } from '@flxblio/sfp-logger'; +import { ZERO_BORDER_TABLE } from '../display/TableConstants'; + +const Table = require('cli-table'); + +export class TestReportDisplayer { + constructor(private apexTestReport: any, private testOptions: TestOptions, private fileLogger?: any) {} + + public printTestSummary(packageCoverage?: number): string { + let apexTestReport = { ...this.apexTestReport }; + SFPLogger.log('\n\n\n=== Test Summary', LoggerLevel.INFO, this.fileLogger); + let table = new Table({ + head: ['Name', 'Value'], + chars: ZERO_BORDER_TABLE + }); + + if ( + this.testOptions instanceof RunAllTestsInPackageOptions || + this.testOptions instanceof RunApexTestSuitesOption || + this.testOptions instanceof RunAllTestsInPackageOptions + ) { + delete apexTestReport.summary.testRunCoverage; + delete apexTestReport.summary.orgWideCoverage; + + if (this.testOptions instanceof RunAllTestsInPackageOptions) + apexTestReport.summary.packageCoverage = packageCoverage; + } + + Object.entries(apexTestReport.summary).forEach((keyValuePair) => { + keyValuePair[1] = keyValuePair[1] || ''; + table.push(keyValuePair); + }); + + SFPLogger.log(table.toString(), LoggerLevel.INFO, this.fileLogger); + return table.toString(); + } + + public printTestResults(): string { + SFPLogger.log('=== Test Results', LoggerLevel.INFO, this.fileLogger); + + let table = new Table({ + head: ['Test Name', 'Outcome', 'Message', 'Runtime (ms)'], + chars: ZERO_BORDER_TABLE + }); + + this.apexTestReport.tests.forEach((test) => { + if (test.Outcome === 'Pass') { + table.push([ + COLOR_SUCCESS(test.FullName || ''), + COLOR_SUCCESS(test.Outcome), + COLOR_SUCCESS(test.Message || ''), + COLOR_SUCCESS(test.RunTime || ''), + ]); + } else { + table.push([ + COLOR_ERROR(test.FullName || ''), + COLOR_ERROR(test.Outcome || ''), + COLOR_ERROR(test.Message || ''), + COLOR_ERROR(test.RunTime || ''), + ]); + } + }); + + SFPLogger.log(table.toString(), LoggerLevel.INFO, this.fileLogger); + return table.toString(); + } + + public printCoverageReport( + coverageThreshold: number, + classesCovered?: { name: string; coveredPercent: number }[], + classesWithInvalidCoverage?: { name: string; coveredPercent: number }[] + ): { classesCoveredTable: string; classInvalidCoverageTable?: string } { + SFPLogger.log('\n\n=== Test Coverage', LoggerLevel.INFO, this.fileLogger); + let classesCoveredTable; + if (classesCovered) { + classesCoveredTable = this.printIndividualClassCoverage(classesCovered); + } + if (classesWithInvalidCoverage) { + let classInvalidCoverageTable = this.printClassesWithInvalidCoverage( + classesWithInvalidCoverage, + coverageThreshold + ); + return { classesCoveredTable, classInvalidCoverageTable }; + } else return { classesCoveredTable }; + } + + private printClassesWithInvalidCoverage( + classesWithInvalidCoverage: { name: string; coveredPercent: number }[], + coverageThreshold: number + ): string { + SFPLogger.log( + `The following classes do not satisfy the ${coverageThreshold}% code coverage requirement:`, + LoggerLevel.INFO, + this.fileLogger + ); + + return this.printIndividualClassCoverage(classesWithInvalidCoverage); + } + + private printIndividualClassCoverage(individualClassCoverage: { name: string; coveredPercent: number }[]): string { + let table = new Table({ + head: ['Class', 'Coverage Percent'], + chars: ZERO_BORDER_TABLE + }); + + individualClassCoverage.forEach((cls) => { + if (cls.coveredPercent !== null && cls.coveredPercent < 75) { + table.push([COLOR_ERROR(cls.name || ''), COLOR_ERROR(cls.coveredPercent)]); + } else if (cls.coveredPercent !== null && cls.coveredPercent >= 75) { + table.push([COLOR_SUCCESS(cls.name || ''), COLOR_SUCCESS(cls.coveredPercent)]); + } else table.push([cls.name || '', 'N/A']); + }); + + SFPLogger.log(table.toString(), LoggerLevel.INFO, this.fileLogger); + return table.toString(); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/apextest/TriggerApexTests.ts b/packages/sfpowerscripts-cli/src/core/apextest/TriggerApexTests.ts new file mode 100644 index 000000000..18c45aff5 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/apextest/TriggerApexTests.ts @@ -0,0 +1,767 @@ +const fs = require('fs-extra'); +import path = require('path'); +import { + RunSpecifiedTestsOption, + TestOptions, + RunApexTestSuitesOption, + RunLocalTests, + RunAllTestsInOrg, + RunAllTestsInPackageOptions, +} from './TestOptions'; +import IndividualClassCoverage, { CoverageOptions } from '../apex/coverage/IndividualClassCoverage'; +import { TestReportDisplayer } from './TestReportDisplayer'; +import PackageTestCoverage from '../package/coverage/PackageTestCoverage'; +import SFPLogger, { COLOR_KEY_MESSAGE, Logger, LoggerLevel, COLOR_ERROR } from '@flxblio/sfp-logger'; +import SFPStatsSender from '../stats/SFPStatsSender'; +import { Connection, Org } from '@salesforce/core'; +import { + TestLevel, + TestResult, + TestService, + JUnitReporter, + Progress, + ApexTestProgressValue, + CancellationTokenSource, + ApexTestResultOutcome, + ApexTestResultData, + CodeCoverageResult, +} from '@salesforce/apex-node'; +import { CliJsonFormat, JsonReporter } from './JSONReporter'; +import { Duration } from '@salesforce/kit'; +import ClearCodeCoverage from './ClearCodeCoverage'; +import _ from 'lodash'; +const retry = require('async-retry'); + +export default class TriggerApexTests { + private conn: Connection; + protected cancellationTokenSource = new CancellationTokenSource(); + + public constructor( + private target_org: string, + private testOptions: TestOptions, + private coverageOptions: CoverageOptions, + private project_directory: string, + private fileLogger?: any + ) {} + + public async exec(): Promise<{ + id: string; + result: boolean; + message: string; + }> { + let org = await Org.create({ aliasOrUsername: this.target_org }); + this.conn = org.getConnection(); + + // graceful shutdown + const exitHandler = async (): Promise => { + await this.cancellationTokenSource.asyncCancel(); + process.exit(); + }; + process.on('SIGINT', exitHandler); + process.on('SIGTERM', exitHandler); + + let startTime = Date.now(); + let testExecutionResult: boolean = false; + let testsRan: number; + let commandTime: number; + + try { + const testService = new TestService(this.conn); + + //Clear Code Coverage before triggering tests + try { + let clearCodeCoverage = new ClearCodeCoverage(org, this.fileLogger); + await clearCodeCoverage.clear(); + } catch (error) { + SFPLogger.log( + `Ignoring error in clearing code coverage attributed to ${error}.`, + LoggerLevel.DEBUG, + this.fileLogger + ); + } + + //Translate Tests to test levels used by apex-node + let translatedTestLevel: TestLevel; + //Fetch tests passed in the testOptions + let tests: string = null; + let suites: string = null; + let isCoverageToBeFetched: boolean = + this.coverageOptions.isIndividualClassCoverageToBeValidated || + this.coverageOptions.isPackageCoverageToBeValidated; + + //Translate Test Option + ({ translatedTestLevel, tests, suites } = await this.translateTestOptionToAPIVars(this.testOptions)); + //Trigger tests asynchronously + let testRunResult: TestResult; + try { + testRunResult = (await this.triggerTestAsynchronously( + testService, + translatedTestLevel, + isCoverageToBeFetched, + tests, + suites + )) as TestResult; + } catch (error) { + return { + result: false, + id: null, + message: error.message, + }; + } + + //Fetch Test Results + let testResult = await retry( + async (bail) => { + return await testService.reportAsyncResults( + testRunResult.summary.testRunId, + isCoverageToBeFetched, + this.cancellationTokenSource.token + ); + }, + { retries: 2, minTimeout: 3000 } + ); + + testResult = this.fixBadNamespaceClassFullNames(testResult); + + //Collect Failed Tests only if Parallel + testResult = await this.triggerSecondRunInSerialForParallelFailedTests( + testResult, + testService, + translatedTestLevel, + isCoverageToBeFetched + ); + + //Filter testResult for duplicate test listing + testResult = this.removeDuplicateTestListing(testResult); + + //Write Test Results to file + let jsonOutput = undefined; + try + { + jsonOutput = this.writeTestOutput(testResult); + }catch(error) + { + SFPLogger.log( + `Unable to write test results to file due to ${error}`, + LoggerLevel.DEBUG, + this.fileLogger + ); + return { + result: false, + id: testResult.summary.testRunId, + message: 'Unable to fetch test execution results, Please retry', + }; + } + + //Print tests result to screen + let testReportDisplayer = new TestReportDisplayer(jsonOutput, this.testOptions, this.fileLogger); + testReportDisplayer.printTestResults(); + + commandTime = testResult.summary.commandTimeInMs; + + if (testResult.summary.outcome == 'Failed') { + testExecutionResult = false; + + return { + result: false, + id: testResult.summary.testRunId, + message: 'Test Execution failed', + }; + } else { + if (isCoverageToBeFetched) { + let coverageResults = await this.validateForApexCoverage(jsonOutput.coverage.coverage); + testReportDisplayer.printCoverageReport( + this.coverageOptions.coverageThreshold, + coverageResults.classesCovered, + coverageResults.classesWithInvalidCoverage + ); + + testsRan = testResult.summary.testsRan; + testReportDisplayer.printTestSummary(coverageResults.packageTestCoverage); + + testExecutionResult = coverageResults.result; + SFPStatsSender.logGauge('apextest.testcoverage', coverageResults.packageTestCoverage, { + package: + this.testOptions instanceof RunAllTestsInPackageOptions + ? this.testOptions.sfppackage.packageName + : null, + }); + return { + result: coverageResults.result, + id: testResult.summary.testRunId, + message: coverageResults.message, + }; + } else { + testExecutionResult = true; + SFPStatsSender.logGauge( + 'apextest.testcoverage', + Number.parseInt(testResult.summary.testRunCoverage), + { + package: + this.testOptions instanceof RunAllTestsInPackageOptions + ? this.testOptions.sfppackage.packageName + : null, + } + ); + return { + result: true, + id: testResult.summary.testRunId, + message: `Test execution succesfully completed`, + }; + } + } + } finally { + this.reportMetrics(this.testOptions, { + targetOrg: this.target_org, + startTime, + testExecutionResult, + testsRan, + commandTime, + }); + } + } + + private async translateTestOptionToAPIVars( + testOptions: TestOptions + ): Promise<{ translatedTestLevel: TestLevel; tests: string; suites: string }> { + let translatedTestLevel: TestLevel; + let tests: string; + let suites: string; + if (testOptions instanceof RunAllTestsInPackageOptions) { + ({ translatedTestLevel, tests } = await this.getTranslatedOptionsForAllTestInPackageOptions(testOptions)); + } else if (testOptions instanceof RunSpecifiedTestsOption) { + ({ translatedTestLevel, tests } = await this.getTranslatedOptionsForSpecifiedTests(testOptions)); + } else if (testOptions instanceof RunApexTestSuitesOption) { + translatedTestLevel = TestLevel.RunSpecifiedTests; + suites = (testOptions as RunApexTestSuitesOption).suiteNames; + SFPLogger.log( + `Test Suites to be executed: ${COLOR_KEY_MESSAGE(suites)}`, + LoggerLevel.INFO, + this.fileLogger + ); + } else if (testOptions instanceof RunLocalTests) { + translatedTestLevel = TestLevel.RunLocalTests; + SFPLogger.log( + `Triggering all ${COLOR_KEY_MESSAGE(`local tests`)}in the org`, + LoggerLevel.INFO, + this.fileLogger + ); + } else if (testOptions instanceof RunAllTestsInOrg) { + SFPLogger.log( + `Triggering all ${COLOR_KEY_MESSAGE(`all tests`)}in the org`, + LoggerLevel.INFO, + this.fileLogger + ); + translatedTestLevel = TestLevel.RunAllTestsInOrg; + } + return { translatedTestLevel, tests, suites }; + } + + private removeDuplicateTestListing(testResult: any): any { + let modifiedTestResult = _.cloneDeep(testResult); + + let toEliminateIndices = []; + for (let index = 0; index < modifiedTestResult.tests.length; index++) { + let idx = index; + let duplicateIndices = [index]; + while (idx != -1) { + idx = _.findIndex( + modifiedTestResult.tests, + (elem: any) => { + return elem.methodName == modifiedTestResult.tests[index].methodName + && elem.apexClass.name == modifiedTestResult.tests[index].apexClass.name; + }, + idx + 1 + ); + if (idx != -1) duplicateIndices.push(idx); + } + if (duplicateIndices.length > 1) { + for (const idx of duplicateIndices) { + if (modifiedTestResult.tests[idx].outcome != 'Pass') toEliminateIndices.push(idx); + } + } + } + + modifiedTestResult.tests = modifiedTestResult.tests.filter(function (value, index, arr) { + return !toEliminateIndices.includes(index); + }); + + if (toEliminateIndices.length > 0) modifiedTestResult = this.combineTestResult(modifiedTestResult); + + return modifiedTestResult; + } + + private async getTranslatedOptionsForSpecifiedTests(testOptions: RunSpecifiedTestsOption) { + let translatedTestLevel = TestLevel.RunSpecifiedTests; + let tests = testOptions.specifiedTests; + SFPLogger.log(`Tests to be executed: ${COLOR_KEY_MESSAGE(tests)}`, LoggerLevel.INFO, this.fileLogger); + SFPLogger.log( + `Test Mode: ${COLOR_KEY_MESSAGE(this.testOptions.synchronous == true ? 'serial' : 'parallel')}`, + LoggerLevel.INFO, + this.fileLogger + ); + //Toggle to serial + await this.toggleParallelApexTesting( + this.conn, + this.fileLogger, + this.testOptions.synchronous == true ? true : false + ); + return { translatedTestLevel, tests }; + } + + private async getTranslatedOptionsForAllTestInPackageOptions(testOptions: RunAllTestsInPackageOptions) { + SFPLogger.log( + `Test Mode Descriptor in Package 'testSynchronous': ${ + testOptions.sfppackage.packageDescriptor.testSynchronous + ? testOptions.sfppackage.packageDescriptor.testSynchronous + : false + }`, + LoggerLevel.TRACE, + this.fileLogger + ); + SFPLogger.log( + `Test Mode: ${COLOR_KEY_MESSAGE(testOptions.synchronous == true ? 'serial' : 'parallel')}`, + LoggerLevel.INFO, + this.fileLogger + ); + await this.toggleParallelApexTesting( + this.conn, + this.fileLogger, + testOptions.synchronous == true ? true : false + ); + let translatedTestLevel = TestLevel.RunSpecifiedTests; + let tests = testOptions.specifiedTests; + SFPLogger.log(`Tests to be executed: ${COLOR_KEY_MESSAGE(tests)}`, LoggerLevel.INFO, this.fileLogger); + return { translatedTestLevel, tests }; + } + + private async triggerSecondRunInSerialForParallelFailedTests( + testResult: TestResult, + testService: TestService, + translatedTestLevel: TestLevel, + isCoverageToBeFetched: boolean + ) { + let modifiedTestResult = _.cloneDeep(testResult); + if (!this.testOptions.synchronous) { + let parallelFailedTestClasses: string[] = []; + let testClassesThatDonotContributedCoverage: string[] = []; + + let testToBeTriggered: string[] = []; + for (const test of modifiedTestResult.tests) { + if (test.outcome == ApexTestResultOutcome.Fail) { + //Check for messages + if ( + test.message.includes(`Your request exceeded the time limit for processing`) || + test.message.includes(`UNABLE_TO_LOCK_ROW`) || + test.message.includes(`Internal Salesforce Error`) || + test.message.includes(`LIMIT_EXCEEDED`) || + test.message.includes(`Too many concurrent Apex compilations during resource mitigation`) + ) { + if (!testToBeTriggered.includes(test.apexClass.fullName)) { + parallelFailedTestClasses.push(test.apexClass.fullName); + testToBeTriggered.push(test.apexClass.fullName); + } + } + } + + if (test.outcome == ApexTestResultOutcome.Pass) { + if ( + !test.perClassCoverage && + (this.coverageOptions.isPackageCoverageToBeValidated || + this.coverageOptions.isIndividualClassCoverageToBeValidated) + ) { + if (!testToBeTriggered.includes(test.apexClass.fullName)) { + testClassesThatDonotContributedCoverage.push(test.apexClass.fullName); + if (!testToBeTriggered.includes(test.apexClass.fullName)) + testToBeTriggered.push(test.apexClass.fullName); + } + } + } + } + + if (parallelFailedTestClasses.length > 0) { + SFPLogger.log( + `Failed Tests while triggered in parallel: ${COLOR_KEY_MESSAGE( + parallelFailedTestClasses.toString() + )}`, + LoggerLevel.INFO, + this.fileLogger + ); + } + + if (testClassesThatDonotContributedCoverage.length > 0) { + SFPLogger.log( + `Test Classes that were not able to contribute coverage: ${COLOR_KEY_MESSAGE( + testClassesThatDonotContributedCoverage.toString() + )}`, + LoggerLevel.INFO, + this.fileLogger + ); + } + + if (testToBeTriggered.length > 0) { + SFPLogger.log( + `Triggering tests synchronously: ${COLOR_KEY_MESSAGE(testToBeTriggered.toString())}`, + LoggerLevel.INFO, + this.fileLogger + ); + //Trigger Second Test Run + //Convert to sequential + await this.toggleParallelApexTesting(this.conn, this.fileLogger, true); + + //Trigger tests asynchronously + let secondRuntestRunResult: TestResult; + secondRuntestRunResult = await retry( + async (bail) => { + return (await this.triggerTestAsynchronously( + testService, + translatedTestLevel, + isCoverageToBeFetched, + testToBeTriggered.toString(), + null + )) as TestResult; + }, + { retries: 2, minTimeout: 3000 } + ); + + secondRuntestRunResult = this.fixBadNamespaceClassFullNames(secondRuntestRunResult); + + //Fetch Test Results + const secondTestResult = this.fixBadNamespaceClassFullNames( + await testService.reportAsyncResults( + secondRuntestRunResult.summary.testRunId, + true, + this.cancellationTokenSource.token + ) + ); + + this.writeTestOutput(secondTestResult); + + //Merge original test results with second run + const mergedTestResults: ApexTestResultData[] = modifiedTestResult.tests; + for (const testObject of secondTestResult.tests){ + const index = mergedTestResults.findIndex((test) => test.fullName === testObject.fullName); + if (index !== -1) { + mergedTestResults[index] = testObject; + }else{ + mergedTestResults.push(testObject); + } + } + modifiedTestResult.tests = mergedTestResults; + + //Merge original code coverage with second run + if (isCoverageToBeFetched) { + const mergedCodecoverage: CodeCoverageResult[] = modifiedTestResult.codecoverage; + for (const codeCoverageObject of secondTestResult.codecoverage){ + + const index = mergedCodecoverage.findIndex((codeCoverage) => codeCoverage.name === codeCoverageObject.name); + if (index !== -1) { + mergedCodecoverage[index] = codeCoverageObject; + }else{ + mergedCodecoverage.push(codeCoverageObject); + } + } + modifiedTestResult.codecoverage = mergedCodecoverage; + } + + //Now redo the math + modifiedTestResult = this.combineTestResult(modifiedTestResult, secondRuntestRunResult); + } + } + + return modifiedTestResult; + } + + private fixBadNamespaceClassFullNames(testResult: any): any { + let modifiedTestResult = _.cloneDeep(testResult); + + try + { + modifiedTestResult.tests = modifiedTestResult.tests.map((test) => { + return { + ...test, + ...{ + fullName: test.fullName?.replace('__', '.'), + apexClass: { + ...test.apexClass, + ...{ + fullName: test.apexClass?.fullName?.replace('__', '.'), + }, + }, + }, + }; + }); + }catch(error) + { + SFPLogger.log( + `Unable to fix bad namespace class full names due to ${error}`, + LoggerLevel.DEBUG, + this.fileLogger + ); + modifiedTestResult = _.cloneDeep(testResult); + } + + return modifiedTestResult; + } + + private combineTestResult(testResult: TestResult, testResultSecondRun?: TestResult) { + testResult.summary.failing = 0; + testResult.summary.passing = 0; + testResult.summary.skipped = 0; + + for (const test of testResult.tests) { + if (test.outcome === ApexTestResultOutcome.Pass) testResult.summary.passing++; + else if (test.outcome === ApexTestResultOutcome.Fail) testResult.summary.failing++; + else if (test.outcome === ApexTestResultOutcome.Skip) testResult.summary.skipped++; + } + + if (testResult.summary.failing > 0) testResult.summary.outcome = 'Failed'; + else testResult.summary.outcome = 'Passed'; + + testResult.summary.passRate = (testResult.summary.passing / testResult.summary.testsRan) * 100 + '%'; + testResult.summary.failRate = (testResult.summary.failing / testResult.summary.testsRan) * 100 + '%'; + testResult.summary.commandTimeInMs = + testResult.summary.commandTimeInMs + testResultSecondRun?.summary.commandTimeInMs; + testResult.summary.testExecutionTimeInMs = + testResult.summary.testExecutionTimeInMs + testResultSecondRun?.summary.testExecutionTimeInMs; + testResult.summary.testTotalTimeInMs = + testResult.summary.testTotalTimeInMs + testResultSecondRun?.summary.testTotalTimeInMs; + + delete testResult.summary.testRunCoverage; + delete testResult.summary.orgWideCoverage; + delete testResult.summary.totalLines; + delete testResult.summary.coveredLines; + + if (testResultSecondRun) + testResult.summary.testRunId = testResult.summary.testRunId.concat( + '_', + testResultSecondRun.summary.testRunId + ); + return testResult; + } + + /** + * Trigger tests asynchronously + * @param {TestService} testService + * @param {TestLevel} testLevel + * @param {string} tests? + * @param {string} suites? + */ + private async triggerTestAsynchronously( + testService: TestService, + testLevel: TestLevel, + isCoverageToBeFetched: boolean, + tests?: string, + suites?: string + ) { + const payload = await testService.buildAsyncPayload(testLevel, null, tests, suites); + + let result = await testService.runTestAsynchronous( + payload, + isCoverageToBeFetched, + false, + new ProgressReporter(this.fileLogger), + this.cancellationTokenSource.token + ); + + if (this.cancellationTokenSource.token.isCancellationRequested) { + throw new Error(`A previous run is being cancelled.. Please try after some time`); + } + + return result; + } + + private async validateForApexCoverage( + coverageReport: any + ): Promise<{ + result: boolean; + message?: string; + packageTestCoverage?: number; + classesCovered?: { + name: string; + coveredPercent: number; + }[]; + classesWithInvalidCoverage?: { + name: string; + coveredPercent: number; + }[]; + }> { + if (this.testOptions instanceof RunAllTestsInPackageOptions) { + let packageTestCoverage: PackageTestCoverage = new PackageTestCoverage( + this.testOptions.sfppackage, + coverageReport, + this.fileLogger, + this.conn + ); + + return packageTestCoverage.validateTestCoverage(this.coverageOptions.coverageThreshold); + } else { + if (this.coverageOptions.isIndividualClassCoverageToBeValidated) { + let coverageValidator: IndividualClassCoverage = new IndividualClassCoverage( + coverageReport, + this.fileLogger + ); + return coverageValidator.validateIndividualClassCoverage( + coverageValidator.getIndividualClassCoverage(this.coverageOptions.classesToBeValidated), + this.coverageOptions.coverageThreshold + ); + } else { + let coverageValidator: IndividualClassCoverage = new IndividualClassCoverage( + coverageReport, + this.fileLogger + ); + return coverageValidator.validateIndividualClassCoverage( + coverageValidator.getIndividualClassCoverage() + ); + } + } + } + + private writeJUnit(testResult: TestResult) { + SFPLogger.log( + `Junit Report file available at ${path.join( + this.testOptions.outputdir, + `test-result-${testResult.summary.testRunId}-junit.xml` + )}` + ); + let reportAsJUnitReport = new JUnitReporter().format(testResult); + fs.writeFileSync( + path.join(this.testOptions.outputdir, `test-result-${testResult.summary.testRunId}-junit.xml`), + reportAsJUnitReport + ); + } + + private writeTestOutput(testResult: TestResult): CliJsonFormat { + const jsonOutput = this.formatResultInJson(testResult); + + //write output files + fs.ensureDirSync(this.testOptions.outputdir); + + //Write files + fs.writeJSONSync( + path.join(this.testOptions.outputdir, `test-result-${testResult.summary.testRunId}.json`), + testResult, + { spaces: 4 } + ); + + if (jsonOutput.coverage) + fs.writeJSONSync( + path.join(this.testOptions.outputdir, `test-result-${testResult.summary.testRunId}-coverage.json`), + jsonOutput.coverage?.coverage, + { spaces: 4 } + ); + + //Write Junit Result no matter what + this.writeJUnit(testResult); + + return jsonOutput; + } + + private formatResultInJson(result: TestResult): CliJsonFormat { + try { + const reporter = new JsonReporter(); + return reporter.format(result); + } catch (error) { + return null; + } + } + + //Enable Synchronus Compile on Deploy + private async toggleParallelApexTesting(conn: Connection, logger: Logger, toEnable: boolean) { + try { + SFPLogger.log(`Set enableDisableParallelApexTesting:${toEnable}`, LoggerLevel.TRACE, logger); + let apexSettingMetadata = { fullName: 'ApexSettings', enableDisableParallelApexTesting: toEnable }; + let result = await conn.metadata.upsert('ApexSettings', apexSettingMetadata); + if (result.success) { + SFPLogger.log(`Successfully updated apex testing setting`, LoggerLevel.INFO, logger); + } + } catch (error) { + SFPLogger.log( + `Skipping toggling of enableDisableParallelApexTesting due to ${error}..`, + LoggerLevel.INFO, + logger + ); + } + } + + private reportMetrics( + testOptions: TestOptions, + testMetrics: { + targetOrg: string; + startTime: number; + testExecutionResult: boolean; + testsRan: number; + commandTime?: number; + } + ) { + let elapsedTime = Date.now() - testMetrics.startTime; + + if (testMetrics.testExecutionResult) + SFPStatsSender.logGauge('apextest.tests.ran', testMetrics.testsRan, { + test_result: String(testMetrics.testExecutionResult), + package: testOptions instanceof RunAllTestsInPackageOptions ? testOptions.sfppackage.packageName : null, + type: testOptions.testLevel, + target_org: testMetrics.targetOrg, + }); + + SFPStatsSender.logGauge('apextest.testtotal.time', elapsedTime, { + test_result: String(testMetrics.testExecutionResult), + package: testOptions instanceof RunAllTestsInPackageOptions ? testOptions.sfppackage.packageName : null, + type: testOptions['testlevel'], + target_org: testMetrics.targetOrg, + }); + + if (testMetrics.commandTime) + SFPStatsSender.logGauge('apextest.command.time', testMetrics.commandTime, { + test_result: String(testMetrics.testExecutionResult), + package: testOptions instanceof RunAllTestsInPackageOptions ? testOptions.sfppackage.packageName : null, + type: testOptions.testLevel, + target_org: testMetrics.targetOrg, + }); + + SFPStatsSender.logCount('apextests.triggered', { + test_result: String(testMetrics.testExecutionResult), + package: testOptions instanceof RunAllTestsInPackageOptions ? testOptions.sfppackage.packageName : null, + type: testOptions.testLevel, + target_org: testMetrics.targetOrg, + }); + } +} +export class ProgressReporter implements Progress { + private lastExecutedTime; + constructor(private logger: Logger) { + this.lastExecutedTime = Date.now(); + } + + report(value: ApexTestProgressValue): void { + try { + let count = {}; + //Limit printing an update to 30 seconds + if (Date.now() - this.lastExecutedTime > Duration.seconds(30).milliseconds) { + if (value.type == 'TestQueueProgress') { + for (const elem of value.value.records) { + if (elem?.Status) { + if (!count[elem.Status]) { + count[elem.Status] = 1; + } else count[elem.Status]++; + } + } + let statusString = ''; + + //Compute total + let total: number = 0; + for (const [key, value] of Object.entries(count)) { + total += value as number; + } + statusString = `Completed:${count['Completed'] ? count['Completed'] : 0}/${total} Queued(${ + count['Queued'] ? count['Queued'] : 0 + }) Failed(${COLOR_ERROR(count['Failed'] ? count['Failed'] : 0)}) `; + SFPLogger.log(`Test Status: ` + COLOR_KEY_MESSAGE(statusString), LoggerLevel.INFO, this.logger); + this.lastExecutedTime = Date.now(); + } + } + } catch (error) { + //Ignore any results during reporting + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/artifacts/ArtifactFetcher.ts b/packages/sfpowerscripts-cli/src/core/artifacts/ArtifactFetcher.ts new file mode 100644 index 000000000..fd0c94644 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/artifacts/ArtifactFetcher.ts @@ -0,0 +1,224 @@ +import path = require('path'); +import * as fs from 'fs-extra'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { globSync } from 'glob'; +import AdmZip = require('adm-zip'); +import semver = require('semver'); +import tar = require('tar'); + +export default class ArtifactFetcher { + /** + * Decider for which artifact retrieval method to use + * Returns empty array if no artifacts are found + * @param artifactDirectory + * @param sfdx_package + */ + public static fetchArtifacts(artifactDirectory: string, sfdx_package?: string, logger?: Logger): Artifact[] { + let result: Artifact[] = []; + + if (!fs.existsSync(artifactDirectory)) { + throw new Error(`Artifact directory ${path.resolve(artifactDirectory)} does not exist`); + } + + let artifacts: string[] = this.findArtifacts(artifactDirectory, sfdx_package); + + SFPLogger.log(`Artifacts: ${JSON.stringify(artifacts)}`, LoggerLevel.TRACE, logger); + + for (let artifact of artifacts) { + let artifactFilePaths: Artifact; + if (path.extname(artifact) === '.zip') { + artifactFilePaths = ArtifactFetcher.fetchArtifactFilePathsFromZipFile(artifact); + } else if (path.extname(artifact) === '.tgz') { + artifactFilePaths = ArtifactFetcher.fetchArtifactFilePathsFromTarball(artifact); + } else { + throw new Error(`Unhandled artifact format ${artifact}, neither tar or zip file`); + } + result.push(artifactFilePaths); + } + + return result; + } + + /** + * Helper method for retrieving the ArtifactFilePaths of an artifact folder + * @param packageMetadataFilePath + */ + private static fetchArtifactFilePathsFromFolder(packageMetadataFilePath: string): Artifact { + let sourceDirectory = path.join(path.dirname(packageMetadataFilePath), `source`); + + let changelogFilePath = path.join(path.dirname(packageMetadataFilePath), `changelog.json`); + + let artifactFilePaths: Artifact = { + packageMetadataFilePath: packageMetadataFilePath, + sourceDirectoryPath: sourceDirectory, + changelogFilePath: changelogFilePath, + }; + + ArtifactFetcher.existsArtifactFilepaths(artifactFilePaths); + + return artifactFilePaths; + } + + /** + * Helper method for retrieving ArtifactFilePaths of an artifact zip + * @param artifact + */ + private static fetchArtifactFilePathsFromZipFile(artifact: string): Artifact { + let unzippedArtifactsDirectory: string = `.sfp/unzippedArtifacts/${this.makefolderid(8)}`; + + fs.mkdirpSync(unzippedArtifactsDirectory); + let zip = new AdmZip(artifact); + + // Overwrite existing files + zip.extractAllTo(unzippedArtifactsDirectory, true); + + let artifactName: string = path.basename(artifact).match(/.*sfp_artifact/)?.[0]; + if (artifactName == null) { + throw new Error(`Failed to fetch artifact file paths for ${artifact}`); + } + + let packageMetadataFilePath = path.join(unzippedArtifactsDirectory, artifactName, 'artifact_metadata.json'); + + let sourceDirectory = path.join(unzippedArtifactsDirectory, artifactName, `source`); + + let changelogFilePath = path.join(unzippedArtifactsDirectory, artifactName, `changelog.json`); + + let artifactFilePaths: Artifact = { + packageMetadataFilePath: packageMetadataFilePath, + sourceDirectoryPath: sourceDirectory, + changelogFilePath: changelogFilePath, + }; + + ArtifactFetcher.existsArtifactFilepaths(artifactFilePaths); + + return artifactFilePaths; + } + + /** + * Helper method for retrieving ArtifactFilePaths of a tarball + * @param artifact + */ + private static fetchArtifactFilePathsFromTarball(artifact: string): Artifact { + let unzippedArtifactsDirectory: string = `.sfp/unzippedArtifacts/${this.makefolderid(8)}`; + fs.mkdirpSync(unzippedArtifactsDirectory); + + tar.x({ + file: artifact, + cwd: unzippedArtifactsDirectory, + sync: true, + }); + + let packageMetadataFilePath = path.join(unzippedArtifactsDirectory, 'package', 'artifact_metadata.json'); + + let sourceDirectory = path.join(unzippedArtifactsDirectory, 'package', `source`); + + let changelogFilePath = path.join(unzippedArtifactsDirectory, 'package', `changelog.json`); + + let artifactFilePaths: Artifact = { + packageMetadataFilePath: packageMetadataFilePath, + sourceDirectoryPath: sourceDirectory, + changelogFilePath: changelogFilePath, + }; + + ArtifactFetcher.existsArtifactFilepaths(artifactFilePaths); + + return artifactFilePaths; + } + + /** + * Find zip and tarball artifacts + * Artifact format/s: + * sfp_artifact_.zip, + * [sfdx_package]_sfp_artifact_[version].zip, + * [sfdx_package]_sfp_artifact_[version].tgz + */ + public static findArtifacts(artifactDirectory: string, sfdx_package?: string): string[] { + let pattern: string; + if (sfdx_package) { + pattern = `**/*${sfdx_package}_sfp_artifact*.@(zip|tgz)`; + } else { + pattern = `**/*sfp_artifact*.@(zip|tgz)`; + } + + let artifacts: string[] = globSync(pattern, { + cwd: artifactDirectory, + absolute: true, + }); + + if (sfdx_package && artifacts.length > 1) { + SFPLogger.log(`Found more than one artifact for ${sfdx_package}`, LoggerLevel.INFO); + let latestArtifact: string = ArtifactFetcher.getLatestArtifact(artifacts); + SFPLogger.log(`Using latest artifact ${latestArtifact}`, LoggerLevel.INFO); + return [latestArtifact]; + } else return artifacts; + } + + /** + * Get the artifact with the latest semantic version + * @param artifacts + */ + private static getLatestArtifact(artifacts: string[]) { + // Consider zip & tarball artifacts only + artifacts = artifacts.filter((artifact) => { + let ext: string = path.extname(artifact); + return ext === '.zip' || ext === '.tgz'; + }); + + let pattern = new RegExp('(?:^.*)(?:_sfp_artifact[_-])(?.*)(?:\\.zip|\\.tgz)$'); + let versions: string[] = artifacts.map((artifact) => { + let match: RegExpMatchArray = path.basename(artifact).match(pattern); + let version = match?.groups.version; + + if (version) return version; + else throw new Error('Corrupted artifact detected with no version number'); + }); + + // Pick artifact with latest semantic version + let sortedVersions: string[] = semver.sort(versions); + let latestVersion: string = sortedVersions.pop(); + + return artifacts.find((artifact) => artifact.includes(latestVersion)); + } + + /** + * Verify that artifact filepaths exist on the file system + * @param artifactFilePaths + */ + private static existsArtifactFilepaths(artifactFilePaths: Artifact): void { + Object.values(artifactFilePaths).forEach((filepath) => { + if (!fs.existsSync(filepath)) throw new Error(`Artifact filepath ${filepath} does not exist`); + }); + } + + /** + * Decider for task outcome if the artifact cannot be found + * @param artifacts_filepaths + * @param isToSkipOnMissingArtifact + */ + public static missingArtifactDecider(artifacts: Artifact[], isToSkipOnMissingArtifact: boolean): boolean { + if (artifacts.length === 0 && !isToSkipOnMissingArtifact) { + throw new Error(`Artifact not found, Please check the inputs`); + } else if (artifacts.length === 0 && isToSkipOnMissingArtifact) { + SFPLogger.log( + `Skipping task as artifact is missing, and 'Skip If no artifact is found' ${isToSkipOnMissingArtifact}` + ); + return true; + } + } + + private static makefolderid(length): string { + var result = ''; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charactersLength = characters.length; + for (var i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; + } +} + +export interface Artifact { + packageMetadataFilePath: string; + sourceDirectoryPath?: string; + changelogFilePath?: string; +} diff --git a/packages/sfpowerscripts-cli/src/core/artifacts/generators/ArtifactGenerator.ts b/packages/sfpowerscripts-cli/src/core/artifacts/generators/ArtifactGenerator.ts new file mode 100644 index 000000000..6ce94ec2d --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/artifacts/generators/ArtifactGenerator.ts @@ -0,0 +1,105 @@ +import path = require('path'); +import * as fs from 'fs-extra'; +import GeneratePackageChangelog from '../../changelog/GeneratePackageChangelog'; +import { Changelog } from '../../changelog/interfaces/GenericChangelogInterfaces'; +import * as rimraf from 'rimraf'; +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; +import AdmZip = require('adm-zip'); +import SfpPackage from '../../package/SfpPackage'; + +export default class ArtifactGenerator { + //Generates the universal artifact used by the CLI and AZP + public static async generateArtifact( + sfpPackage: SfpPackage, + project_directory: string, + artifact_directory: string + ): Promise { + try { + // Artifact folder consisting of artifact metadata, changelog & source + let artifactFolder: string = `${sfpPackage.packageName}_sfp_artifact`; + + // Absolute filepath of artifact + let artifactFilepath: string; + + if (artifact_directory != null) { + artifactFilepath = path.resolve(artifact_directory, artifactFolder); + } else { + artifactFilepath = path.resolve(artifactFolder); + } + + fs.mkdirpSync(artifactFilepath); + + let sourcePackage: string = path.join(artifactFilepath, `source`); + fs.mkdirpSync(sourcePackage); + + //Clean up temp directory + if (fs.existsSync(path.join(sfpPackage.workingDirectory, '.sfp'))) + rimraf.sync(path.join(sfpPackage.workingDirectory, '.sfp')); + if (fs.existsSync(path.join(sfpPackage.workingDirectory, '.sfdx'))) + rimraf.sync(path.join(sfpPackage.workingDirectory, '.sfdx')); + + fs.copySync(sfpPackage.workingDirectory, sourcePackage); + rimraf.sync(sfpPackage.workingDirectory); + + //Modify Source Directory to the new source directory inside the artifact + sfpPackage.sourceDir = `source`; + + let artifactMetadataFilePath: string = path.join(artifactFilepath, `artifact_metadata.json`); + + fs.writeFileSync(artifactMetadataFilePath, JSON.stringify(sfpPackage, null, 4)); + + // Generate package changelog + // Doesnt need a from version number, as it always generate from start + let generatePackageChangelog: GeneratePackageChangelog = new GeneratePackageChangelog( + sfpPackage.packageName, + undefined, + sfpPackage.sourceVersion, + project_directory + ); + + let packageChangelog: Changelog = await generatePackageChangelog.exec(); + + let changelogFilepath: string = path.join(artifactFilepath, `changelog.json`); + + fs.writeFileSync(changelogFilepath, JSON.stringify(packageChangelog, null, 4)); + + SFPLogger.log('Artifact Copy Completed', LoggerLevel.DEBUG); + + let zip = new AdmZip(); + zip.addLocalFolder(artifactFilepath, artifactFolder); + SFPLogger.log(`Zipping ${artifactFolder}`, LoggerLevel.DEBUG); + + let packageVersionNumber: string = ArtifactGenerator.substituteBuildNumberWithPreRelease( + sfpPackage.versionNumber + ); + + let zipArtifactFilepath: string = artifactFilepath + `_` + packageVersionNumber + `.zip`; + zip.writeZip(zipArtifactFilepath); + + SFPLogger.log( + `Artifact Generation Completed for ${sfpPackage.packageType} to ${zipArtifactFilepath}`, + LoggerLevel.INFO + ); + + // Cleanup unzipped artifact + rimraf.sync(artifactFilepath); + + return zipArtifactFilepath; + } catch (error) { + throw new Error('Unable to create artifact' + error); + } + } + + private static 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; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/changelog/GeneratePackageChangelog.ts b/packages/sfpowerscripts-cli/src/core/changelog/GeneratePackageChangelog.ts new file mode 100644 index 000000000..d4c04424d --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/changelog/GeneratePackageChangelog.ts @@ -0,0 +1,76 @@ +import ProjectConfig from '../project/ProjectConfig'; +import simplegit, { SimpleGit, LogOptions } from 'simple-git'; +import { Changelog } from './interfaces/GenericChangelogInterfaces'; +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; + +/** + * A class for generating a changelog between two commits + * for a single package + */ +export default class GeneratePackageChangelog { + constructor( + private readonly sfdx_package: string, + private readonly revFrom: string, + private readonly revTo: string, + private readonly project_directory: string + ) {} + + public async exec(): Promise { + let git: SimpleGit; + if (this.project_directory != null) { + git = simplegit(this.project_directory); + } else { + git = simplegit(); + } + + let packageDescriptor; + try { + packageDescriptor = ProjectConfig.getSFDXPackageDescriptor(this.project_directory, this.sfdx_package); + } catch (err) { + SFPLogger.log(`Unable to find descriptor for package ${this.sfdx_package}`, LoggerLevel.WARN); + SFPLogger.log(err.message, LoggerLevel.WARN); + } + + let revFrom: string; + if (this.revFrom) { + revFrom = await git.revparse(['--short', `${this.revFrom}^{}`]); + } + + + let revTo: string = await git.revparse(['--short', `${this.revTo}^{}`]); + + let options: LogOptions = { + file: packageDescriptor ? `${packageDescriptor['path']}*` : packageDescriptor, + }; + if(revFrom) + { + options.from = revFrom; + options.to = revTo; + } + + const gitLogResult = await git.log(options); + + let changelog: Changelog = { + name: undefined, + from: undefined, + to: undefined, + commits: [], + }; + + changelog['name'] = this.sfdx_package; + changelog['from'] = revFrom; + changelog['to'] = revTo; + + for (let commit of gitLogResult.all) { + changelog['commits'].push({ + commitId: commit.hash.slice(0, 8), + date: commit.date, + author: commit.author_name, + message: commit.message, + body: commit.body, + }); + } + + return changelog; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/changelog/interfaces/GenericChangelogInterfaces.ts b/packages/sfpowerscripts-cli/src/core/changelog/interfaces/GenericChangelogInterfaces.ts new file mode 100644 index 000000000..d9929d0ad --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/changelog/interfaces/GenericChangelogInterfaces.ts @@ -0,0 +1,30 @@ +export interface Changelog { + /** + * Name of the package + */ + name: string; + + /** + * Backwards-compatibility for delta package + */ + from: string; + + /** + * Commit Id from which package was created + * May not necessarily be the first element in commits + */ + to: string; + + /** + * Commits that modified the package + */ + commits: Commit[]; +} + +export interface Commit { + commitId: string; + date: string; + author: string; + message: string; + body: string; +} diff --git a/packages/sfpowerscripts-cli/src/core/dependency/ChangedComponentsFetcher.ts b/packages/sfpowerscripts-cli/src/core/dependency/ChangedComponentsFetcher.ts new file mode 100644 index 000000000..253b64313 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/dependency/ChangedComponentsFetcher.ts @@ -0,0 +1,93 @@ +import Git from '../git/Git'; +import IgnoreFiles from '../ignore/IgnoreFiles'; +import ProjectConfig from '../project/ProjectConfig'; +import MetadataFiles from '../metadata/MetadataFiles'; +import Component from './Component'; +import * as fs from 'fs-extra'; +import path = require('path'); +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; + +export default class ChangedComponentsFetcher { + constructor(private baseBranch: string) {} + + async fetch(): Promise { + const components: Component[] = []; + + let git: Git = await Git.initiateRepo(); + + let projectConfig = ProjectConfig.getSFDXProjectConfig(null); + + if (!this.baseBranch.includes('origin')) { + // for user convenience, use full ref name to avoid errors involving missing local refs + this.baseBranch = `remotes/origin/${this.baseBranch}`; + } + + let diff: string[] = await git.diff([this.baseBranch, `HEAD`, `--no-renames`, `--name-only`]); + + // Filter diff to package directories + diff = diff.filter((filepath) => + projectConfig.packageDirectories.find((pkg) => + // TODO: make comparison more robust + filepath.includes(pkg.path) + ) + ); + + // Apply root forceignore to the diff + let ignoreFiles: IgnoreFiles = new IgnoreFiles(fs.readFileSync('.forceignore', 'utf8')); + diff = ignoreFiles.filter(diff); + + let componentSuccesses = this.getComponentSuccessesFromReports(); + + if (diff.length > 0) { + for (const filepath of diff) { + const fullApiName = MetadataFiles.getFullApiName(filepath); + + // find package that file belongs to + const indexOfPackage = projectConfig.packageDirectories.findIndex((pkg) => filepath.includes(pkg.path)); + + const packageName = projectConfig.packageDirectories[indexOfPackage].package; + + const componentSuccess = componentSuccesses.find( + (component) => component.fullName === fullApiName && component.id + ); + + if (componentSuccess) { + const component: Component = { + id: componentSuccess.id, + fullName: componentSuccess.fullName, + type: componentSuccess.componentType, + files: [filepath], + package: packageName, + packageType: ProjectConfig.getPackageType(projectConfig, packageName), + indexOfPackage: indexOfPackage, + }; + + components.push(component); + } else { + SFPLogger.log(`Unable to find ID for ${fullApiName} in deployment reports`, LoggerLevel.DEBUG); + // Ignore file if it's not an identifiable component + continue; + } + } + } + + return components; + } + + /** + * Aggregates component successes from MDAPI deploy reports + */ + private getComponentSuccessesFromReports(): any[] { + let componentSuccesses: any[] = []; + + const reportsDir: string = '.sfp/mdapiDeployReports'; + if (fs.existsSync(reportsDir)) { + let reports = fs.readdirSync(reportsDir); + reports.forEach((report) => { + let data = JSON.parse(fs.readFileSync(path.join(reportsDir, report), 'utf8')); + componentSuccesses = componentSuccesses.concat(data.result.details.componentSuccesses); + }); + } + return componentSuccesses; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/dependency/Component.ts b/packages/sfpowerscripts-cli/src/core/dependency/Component.ts new file mode 100644 index 000000000..7fe64298b --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/dependency/Component.ts @@ -0,0 +1,16 @@ +import { PackageType } from "../package/SfpPackage"; + +/** + * Component details and package it belongs to + */ +export default interface Component { + id: string; + fullName: string; + type: string; + files?: string[]; + package?: string; + packageType?: PackageType; + indexOfPackage?: number; + namespace?: string; + dependencies?: Component[]; +} diff --git a/packages/sfpowerscripts-cli/src/core/dependency/DependencyViolation.ts b/packages/sfpowerscripts-cli/src/core/dependency/DependencyViolation.ts new file mode 100644 index 000000000..80493b56e --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/dependency/DependencyViolation.ts @@ -0,0 +1,7 @@ +import Component from './Component'; + +export default interface DependencyViolation { + component: Component; + dependency: any; + description: string; +} diff --git a/packages/sfpowerscripts-cli/src/core/dependency/Entrypoint.ts b/packages/sfpowerscripts-cli/src/core/dependency/Entrypoint.ts new file mode 100644 index 000000000..008ec7b59 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/dependency/Entrypoint.ts @@ -0,0 +1,20 @@ +import Component from './Component'; + +/** + * Used by sfdc-soup API calls + */ +export default interface Entrypoint { + name: string; + type: string; + id: string; +} + +export function component2entrypoint(components: Component[]): Entrypoint[] { + return components.map((component) => { + return { + name: component.fullName, + type: component.type, + id: component.id, + } as Entrypoint; + }); +} diff --git a/packages/sfpowerscripts-cli/src/core/deployers/DeploySourceToOrgImpl.ts b/packages/sfpowerscripts-cli/src/core/deployers/DeploySourceToOrgImpl.ts new file mode 100644 index 000000000..ea9f2c3d4 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/deployers/DeploySourceToOrgImpl.ts @@ -0,0 +1,230 @@ +import SFPLogger, { + COLOR_ERROR, + COLOR_HEADER, + COLOR_KEY_MESSAGE, + COLOR_SUCCESS, + Logger, + LoggerLevel, +} from '@flxblio/sfp-logger'; + +import { Duration } from '@salesforce/kit'; +import DeploymentExecutor, { DeploySourceResult } from './DeploymentExecutor'; +import { + ComponentSet, + DeployMessage, + DeployResult, + MetadataApiDeployOptions, + RequestStatus, +} from '@salesforce/source-deploy-retrieve'; +import * as fs from 'fs-extra'; +import path from 'path'; +import SFPOrg from '../org/SFPOrg'; +import getFormattedTime from '../utils/GetFormattedTime'; +import { TestLevel } from '../apextest/TestOptions'; +import { SfProject } from '@salesforce/core'; +import { SourceTracking } from '@salesforce/source-tracking'; + +export default class DeploySourceToOrgImpl implements DeploymentExecutor { + public constructor( + private org: SFPOrg, + private projectDir: string, + private componentSet: ComponentSet, + private deploymentOptions: DeploymentOptions, + private logger?: Logger + ) {} + + public async exec(): Promise { + let deploySourceResult = {} as DeploySourceResult; + + if (this.deploymentOptions.apiVersion) this.componentSet.sourceApiVersion = this.deploymentOptions.apiVersion; + + //Get Deploy ID + let result = await this.deploy(this.componentSet); + + this.writeResultToReport(result); + + if (this.deploymentOptions.sourceTracking) { + await this.handleSourceTracking(this.org, this.logger, this.projectDir, result); + } + + //Handle Responses + if (result.response.status == RequestStatus.Succeeded) { + deploySourceResult.message = `Successfully deployed`; + deploySourceResult.result = result.response.success; + deploySourceResult.deploy_id = result.response.id; + } else { + if (result.response.status == RequestStatus.Canceled) { + deploySourceResult.message = `The deployment request ${result.response.id} was cancelled by ${result.response.canceledByName}`; + } else { + deploySourceResult.message = this.handlErrorMesasge(result); + } + deploySourceResult.response = result.response; + deploySourceResult.result = false; + deploySourceResult.deploy_id = result.response.id; + } + return deploySourceResult; + } + + private handlErrorMesasge(result: DeployResult): string { + if (result.response.numberComponentErrors == 0) { + return 'Unable to fetch report, Check your org for details'; + } else if (result.response.numberComponentErrors > 0) { + return this.constructComponentErrorMessage(result.response.details.componentFailures, this.logger); + } else if (result.response.details.runTestResult) { + return 'Unable to deploy due to unsatisfactory code coverage and/or test failures'; + } else { + return 'Unable to fetch report, Check your org for details'; + } + } + + private constructComponentErrorMessage(componentFailures: DeployMessage | DeployMessage[], logger: Logger) { + let errorMessage = `Unable to deploy due to failure in some components, check log for details`; + + if (componentFailures === null || componentFailures === undefined) return; + + if (componentFailures instanceof Array) { + //Search for other scenarios and if background Job is being executed, override the error message + for (let failure of componentFailures) { + let scenario = classifyErrorScenarios(failure); + if (scenario == `BackgroundJob`) { + errorMessage = `Unable to deploy due to an ongoing background job from a previous package`; + break; + } + } + } else { + let failure = componentFailures; + let scenario = classifyErrorScenarios(failure); + if (scenario == `BackgroundJob`) { + errorMessage = `Unable to deploy due to an ongoing background job from a previous package`; + } + } + + function classifyErrorScenarios(failure: DeployMessage) { + let scenario = `Component Error`; + //Override if background job is being executed + if (failure.problem.includes(`background job is being executed`)) { + scenario = `BackgroundJob`; + } + return scenario; + } + return errorMessage; + } + + private writeResultToReport(result: DeployResult) { + let deploymentReports = `.sfp/mdapiDeployReports`; + fs.mkdirpSync(deploymentReports); + fs.writeFileSync( + path.join(deploymentReports, `${result.response.id}.json`), + JSON.stringify(this.formatResultAsJSON(result)) + ); + } + + private async buildDeploymentOptions(org: SFPOrg): Promise { + let metdataDeployOptions: MetadataApiDeployOptions = { + usernameOrConnection: org.getConnection(), + apiOptions: {}, + }; + + if (this.deploymentOptions.apiVersion) metdataDeployOptions.apiVersion = this.deploymentOptions.apiVersion; + + if (this.deploymentOptions.testLevel == TestLevel.RunLocalTests) { + metdataDeployOptions.apiOptions.testLevel = TestLevel.RunLocalTests; + } else if (this.deploymentOptions.testLevel == TestLevel.RunSpecifiedTests) { + metdataDeployOptions.apiOptions.testLevel = TestLevel.RunSpecifiedTests; + metdataDeployOptions.apiOptions.runTests = this.deploymentOptions.specifiedTests.split(`,`); + } else { + metdataDeployOptions.apiOptions.testLevel = TestLevel.RunNoTests; + } + + if (this.deploymentOptions.ignoreWarnings) { + metdataDeployOptions.apiOptions.ignoreWarnings = true; + } + + metdataDeployOptions.apiOptions.rollbackOnError = this.deploymentOptions.rollBackOnError; + + return metdataDeployOptions; + } + + private async deploy(componentSet: ComponentSet) { + let deploymentOptions = await this.buildDeploymentOptions(this.org); + const deploy = await componentSet.deploy(deploymentOptions); + + let startTime = Date.now(); + SFPLogger.log(`Deploying to ${this.org.getUsername()} with id:${deploy.id}`, LoggerLevel.INFO, this.logger); + // Attach a listener to check the deploy status on each poll + deploy.onUpdate((response) => { + const { status, numberComponentsDeployed, numberComponentsTotal } = response; + const progress = `${numberComponentsDeployed}/${numberComponentsTotal}`; + const message = `Status: ${COLOR_KEY_MESSAGE(status)} Progress: ${COLOR_KEY_MESSAGE(progress)}`; + SFPLogger.log(message, LoggerLevel.INFO, this.logger); + }); + + deploy.onFinish((response) => { + let deploymentDuration = Date.now() - startTime; + if (response.response.success) { + SFPLogger.log( + COLOR_SUCCESS( + `Succesfully Deployed ${COLOR_HEADER( + response.response.numberComponentsDeployed + )} components in ${getFormattedTime(deploymentDuration)}` + ), + LoggerLevel.INFO, + this.logger + ); + } else + SFPLogger.log( + COLOR_ERROR(`Failed to deploy after ${getFormattedTime(deploymentDuration)}`), + LoggerLevel.INFO, + this.logger + ); + }); + + // Wait for polling to finish and get the DeployResult object + const hoursInWaitTime = Number(this.deploymentOptions.waitTime) / 60; + const result = await deploy.pollStatus({ frequency: Duration.seconds(30), timeout: Duration.hours(hoursInWaitTime) }); + return result; + } + + //For compatibility with cli output + private formatResultAsJSON(result) { + const response = result?.response ? result.response : {}; + return { + result: { + ...response, + details: { + componentSuccesses: response?.details?.componentSuccesses, + componentFailures: response?.details?.componentFailures, + runTestResult: response?.details?.runTestResult, + }, + }, + }; + } + + private async handleSourceTracking(org: SFPOrg, logger: Logger, projectDir: string, result: DeployResult) { + if (result.response.success) { + try { + const project = await SfProject.resolve(this.projectDir); + const tracking = await SourceTracking.create({ + org: org, + project: project, + }); + await tracking.ensureRemoteTracking(); + tracking.updateTrackingFromDeploy(result); + } catch (error) { + SFPLogger.log(`Unable to update source tracking due to \n ${error}`, LoggerLevel.WARN, logger); + } + } + } +} + +export class DeploymentOptions { + ignoreWarnings: boolean; + waitTime: string; + checkOnly?: boolean; + apiVersion?: string; + testLevel?: TestLevel; + apexTestSuite?: string; + specifiedTests?: string; + sourceTracking?: boolean; + rollBackOnError?: boolean; +} diff --git a/packages/sfpowerscripts-cli/src/core/deployers/DeploymentExecutor.ts b/packages/sfpowerscripts-cli/src/core/deployers/DeploymentExecutor.ts new file mode 100644 index 000000000..1e1165abf --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/deployers/DeploymentExecutor.ts @@ -0,0 +1,17 @@ +import { MetadataApiDeployStatus } from "@salesforce/source-deploy-retrieve"; + +export default interface DeploymentExecutor { + exec(): Promise; +} + +export interface DeploySourceResult { + deploy_id: string; + result: boolean; + message: string; + response?:MetadataApiDeployStatus +} + +export enum DeploymentType { + SOURCE_PUSH, + MDAPI_DEPLOY, +} diff --git a/packages/sfpowerscripts-cli/src/core/deployers/DeploymentSettingsService.ts b/packages/sfpowerscripts-cli/src/core/deployers/DeploymentSettingsService.ts new file mode 100644 index 000000000..2f8c0b0f6 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/deployers/DeploymentSettingsService.ts @@ -0,0 +1,64 @@ +import SFPLogger, { COLOR_KEY_MESSAGE, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { Connection } from '@salesforce/core'; +import { IpRange, SecuritySettings } from 'jsforce/lib/api/metadata'; + +export default class DeploymentSettingsService { + constructor(private conn: Connection) {} + + //Enable Synchronus Compile on Deploy + public async enableSynchronousCompileOnDeploy(logger: Logger) { + try { + let apexSettingMetadata = { fullName: 'ApexSettings', enableCompileOnDeploy: true }; + let result = await this.conn.metadata.upsert('ApexSettings', apexSettingMetadata); + if (result.success) { + SFPLogger.log( + `${COLOR_KEY_MESSAGE( + 'Enabled Synchronous Compile on Org succesfully as this is the last package in queue' + )}`, + LoggerLevel.INFO, + logger + ); + } + } catch (error) { + SFPLogger.log( + `Skipping Synchronous Compile on Org succesfully due to ${error}..`, + LoggerLevel.INFO, + logger + ); + } + } + + public async relaxAllIPRanges(logger: Logger, ipRangesAsStringArray?: string[]) { + let ipRanges: IpRange[] = []; + if (!ipRangesAsStringArray) { + ipRanges = this.getFullRange(); + } else { + ipRanges = []; + //transform to ipRange Array + for (const ipRange of ipRangesAsStringArray) { + ipRanges.push({ start: ipRange, end: ipRange }); + } + } + let securitySettingsMetadata: SecuritySettings = { + fullName: 'SecuritySettings', + networkAccess: { ipRanges: ipRanges }, + }; + try { + let result = await this.conn.metadata.upsert('SecuritySettings', securitySettingsMetadata); + if (result.success) { + SFPLogger.log(`${COLOR_KEY_MESSAGE('Relaxed all ipRanges in the org')}`, LoggerLevel.INFO, logger); + } + } catch (error) { + SFPLogger.log(`Unable to relax IP range in org due to ${error.message}`, LoggerLevel.ERROR, logger); + throw error; + } + } + + private getFullRange(): IpRange[] { + let ipRanges = []; + for (let i = 0; i < 255; i += 2) { + ipRanges.push({ start: `${i}.0.0.0`, end: `${i + 1}.255.255.255` }); + } + return ipRanges; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/display/DependencyViolationDisplayer.ts b/packages/sfpowerscripts-cli/src/core/display/DependencyViolationDisplayer.ts new file mode 100644 index 000000000..09c6e54ca --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/display/DependencyViolationDisplayer.ts @@ -0,0 +1,31 @@ +const Table = require('cli-table'); +import DependencyViolation from '../dependency/DependencyViolation'; +import SFPLogger from '@flxblio/sfp-logger'; +import { ZERO_BORDER_TABLE } from './TableConstants'; + +export default class DependencyViolationDisplayer { + public static printDependencyViolations(dependencyViolations: DependencyViolation[]) { + if (!dependencyViolations || dependencyViolations.length === 0) return; + + const table = new Table({ + head: ['API Name', 'Type', 'Package', 'Dependency', 'Dependency Type', 'Dependency Package', 'Problem'], + chars: ZERO_BORDER_TABLE + }); + + SFPLogger.log('The following components resulted in failures:'); + + dependencyViolations.forEach((violation) => { + table.push([ + violation.component.fullName, + violation.component.type, + violation.component.package, + violation.dependency.fullName, + violation.dependency.type, + violation.dependency.package, + violation.description, + ]); + }); + + SFPLogger.log(table.toString()); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/display/DeployErrorDisplayer.ts b/packages/sfpowerscripts-cli/src/core/display/DeployErrorDisplayer.ts new file mode 100644 index 000000000..8a0ea62d3 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/display/DeployErrorDisplayer.ts @@ -0,0 +1,103 @@ +const Table = require('cli-table'); +import { CodeCoverageWarnings, DeployMessage, Failures, MetadataApiDeployStatus } from '@salesforce/source-deploy-retrieve'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { ZERO_BORDER_TABLE } from './TableConstants'; + +export default class DeployErrorDisplayer { + private static printMetadataFailedToDeploy(componentFailures: DeployMessage | DeployMessage[], logger: Logger) { + if (componentFailures === null || componentFailures === undefined) return; + + let table = new Table({ + head: ['Metadata Type', 'API Name', 'Problem Type', 'Problem'], + chars: ZERO_BORDER_TABLE + }); + + let pushComponentFailureIntoTable = (componentFailure) => { + let item = [ + componentFailure.componentType, + componentFailure.fullName, + componentFailure.problemType, + componentFailure.problem, + ]; + table.push(item); + }; + + if (componentFailures instanceof Array) { + for (let failure of componentFailures) { + pushComponentFailureIntoTable(failure); + } + } else { + let failure = componentFailures; + pushComponentFailureIntoTable(failure); + } + SFPLogger.log('The following components resulted in failures:', LoggerLevel.ERROR, logger); + SFPLogger.log(table.toString(), LoggerLevel.ERROR, logger); + } + + public static displayErrors(response: MetadataApiDeployStatus, logger: Logger) { + SFPLogger.log(`Gathering Final Deployment Status`, null, logger); + + if (response.numberComponentErrors == 0) { + return 'Unable to fetch report, Check your org for details'; + } else if (response.numberComponentErrors > 0) { + this.printMetadataFailedToDeploy(response.details.componentFailures, logger); + return response.errorMessage; + } else if (response.details.runTestResult) { + if (response.details.runTestResult.codeCoverageWarnings) { + this.displayCodeCoverageWarnings(response.details.runTestResult.codeCoverageWarnings, logger); + } + + if (response.details.runTestResult.failures) { + this.displayTestFailures(response.details.runTestResult.failures, logger); + } + return 'Unable to deploy due to unsatisfactory code coverage and/or test failures'; + } else { + return 'Unable to fetch report, Check your org for details'; + } + } + + private static displayCodeCoverageWarnings( + codeCoverageWarnings: CodeCoverageWarnings | CodeCoverageWarnings[], + logger: Logger + ) { + let table = new Table({ + head: ['Name', 'Message'], + }); + + if (Array.isArray(codeCoverageWarnings)) { + codeCoverageWarnings.forEach((coverageWarningElement) => { + table.push([coverageWarningElement['name'], coverageWarningElement.message]); + }); + } else { + table.push([codeCoverageWarnings['name'], codeCoverageWarnings.message]); + } + + if (table.length > 1) { + SFPLogger.log( + 'Unable to deploy due to unsatisfactory code coverage, Check the following classes:', + LoggerLevel.WARN, + logger + ); + SFPLogger.log(table.toString(), LoggerLevel.WARN, logger); + } + } + + private static displayTestFailures(testFailures: Failures | Failures[], logger: Logger) { + let table = new Table({ + head: ['Test Name', 'Method Name', 'Message'], + chars: ZERO_BORDER_TABLE + }); + + if (Array.isArray(testFailures)) { + testFailures.forEach((elem) => { + table.push([elem.name, elem.methodName, elem.message]); + }); + } else { + table.push([testFailures.name, testFailures.methodName, testFailures.message]); + } + if (table.length > 1) { + SFPLogger.log('Unable to deploy due to test failures:', LoggerLevel.WARN, logger); + SFPLogger.log(table.toString(), LoggerLevel.WARN, logger); + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/display/DeploymentOptionDisplayer.ts b/packages/sfpowerscripts-cli/src/core/display/DeploymentOptionDisplayer.ts new file mode 100644 index 000000000..e484e0724 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/display/DeploymentOptionDisplayer.ts @@ -0,0 +1,51 @@ +import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { TestLevel } from '../apextest/TestOptions'; +import { DeploymentOptions } from '../deployers/DeploySourceToOrgImpl'; + +export default class DeploymentOptionDisplayer { + public static printDeploymentOptions(deploymentOptions: DeploymentOptions, logger?: Logger) { + SFPLogger.log( + `${COLOR_HEADER( + `=================================================================================================` + )}`, + LoggerLevel.INFO, + logger + ); + SFPLogger.log(`${COLOR_HEADER(`Deployment Options`)}`, LoggerLevel.INFO, logger); + SFPLogger.log( + `${COLOR_HEADER( + `=================================================================================================` + )}`, + LoggerLevel.INFO, + logger + ); + SFPLogger.log(`TestLevel: ${COLOR_KEY_MESSAGE(deploymentOptions.testLevel)}`, LoggerLevel.INFO, logger); + if (deploymentOptions.testLevel == TestLevel.RunSpecifiedTests) + SFPLogger.log( + `Tests to be triggered: ${COLOR_KEY_MESSAGE(deploymentOptions.specifiedTests)}`, + LoggerLevel.INFO, + logger + ); + + SFPLogger.log( + `Ignore Warnings: ${COLOR_KEY_MESSAGE(deploymentOptions.ignoreWarnings)}`, + LoggerLevel.INFO, + logger + ); + + SFPLogger.log( + `Roll Back on Error: ${COLOR_KEY_MESSAGE(deploymentOptions.rollBackOnError)}`, + LoggerLevel.INFO, + logger + ); + + SFPLogger.log(`API Version: ${COLOR_KEY_MESSAGE(deploymentOptions.apiVersion)}`, LoggerLevel.INFO, logger); + SFPLogger.log( + `${COLOR_HEADER( + `=================================================================================================` + )}`, + LoggerLevel.INFO, + logger + ); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/display/ExternalDependencyDisplayer.ts b/packages/sfpowerscripts-cli/src/core/display/ExternalDependencyDisplayer.ts new file mode 100644 index 000000000..3227b8695 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/display/ExternalDependencyDisplayer.ts @@ -0,0 +1,30 @@ +import SFPLogger, { COLOR_KEY_MESSAGE, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { EOL } from 'os'; +import Package2Detail from '../package/Package2Detail'; +import { ZERO_BORDER_TABLE } from './TableConstants'; +const Table = require('cli-table'); + +export default class ExternalDependencyDisplayer { + public constructor(private externalPackage2s: Package2Detail[], private logger: Logger) {} + + public display() { + if (this.externalPackage2s.length > 0) { + let table = new Table({ + head: ['Order', 'Package', 'Version', 'Subscriber Version Id'], + chars: ZERO_BORDER_TABLE + }); + let i = 0; + for (const externalPackage of this.externalPackage2s) { + table.push([ + i++, + externalPackage.name, + externalPackage.versionNumber ? externalPackage.versionNumber : 'N/A', + externalPackage.subscriberPackageVersionId ? externalPackage.subscriberPackageVersionId:'N/A, Could be part of current operation', + ]); + } + SFPLogger.log(EOL, LoggerLevel.INFO, this.logger); + SFPLogger.log(COLOR_KEY_MESSAGE(`Resolved external package dependencies:`), LoggerLevel.INFO, this.logger); + SFPLogger.log(table.toString(), LoggerLevel.INFO, this.logger); + } + } +} \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/src/core/display/InstalledArtifactsDisplayer.ts b/packages/sfpowerscripts-cli/src/core/display/InstalledArtifactsDisplayer.ts new file mode 100644 index 000000000..d0ef5f96f --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/display/InstalledArtifactsDisplayer.ts @@ -0,0 +1,22 @@ +const Table = require('cli-table'); +import SFPLogger, { Logger, LoggerLevel, COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; +import { ZERO_BORDER_TABLE } from './TableConstants'; + +export default class InstalledArtifactsDisplayer { + public static printInstalledArtifacts(artifacts: any, logger: Logger) { + if (!artifacts) return; + else if(artifacts.length==0) return; + + let table = new Table({ + head: ['Artifact', 'Version', 'Commit Id'], + chars: ZERO_BORDER_TABLE + }); + + artifacts.forEach((artifact) => { + table.push([artifact.Name, artifact.Version__c, artifact.CommitId__c?artifact.CommitId__c:""]); + }); + + SFPLogger.log(COLOR_KEY_MESSAGE('Artifacts installed in org:'), LoggerLevel.INFO, logger); + SFPLogger.log(table.toString(), LoggerLevel.INFO, logger); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/display/InstalledPackagesDisplayer.ts b/packages/sfpowerscripts-cli/src/core/display/InstalledPackagesDisplayer.ts new file mode 100644 index 000000000..32460d356 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/display/InstalledPackagesDisplayer.ts @@ -0,0 +1,22 @@ +const Table = require('cli-table'); +import SFPLogger, { Logger, LoggerLevel, COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; +import Package2Detail from '../package/Package2Detail'; +import { ZERO_BORDER_TABLE } from './TableConstants'; + +export default class InstalledPackagesDisplayer { + public static printInstalledPackages(packages: Package2Detail[], logger: Logger) { + if (packages == null) return; + + let table = new Table({ + head: ['Package', 'Version', 'Type', 'isOrgDependent'], + chars: ZERO_BORDER_TABLE + }); + + packages.forEach((pkg) => { + table.push([pkg.name, pkg.versionNumber, pkg.type, pkg.isOrgDependent]); + }); + + SFPLogger.log(COLOR_KEY_MESSAGE('Packages installed in org:'), LoggerLevel.INFO, logger); + SFPLogger.log(table.toString(), LoggerLevel.INFO, logger); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/display/PackageComponentPrinter.ts b/packages/sfpowerscripts-cli/src/core/display/PackageComponentPrinter.ts new file mode 100644 index 000000000..ca2806cc4 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/display/PackageComponentPrinter.ts @@ -0,0 +1,27 @@ +const Table = require('cli-table'); +import { LazyCollection, SourceComponent } from '@salesforce/source-deploy-retrieve'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { ZERO_BORDER_TABLE } from './TableConstants'; + +export default class PackageComponentPrinter { + public static printComponentTable(components: LazyCollection, logger: Logger) { + //If Manifest is null, just return + if (components === null || components === undefined) return; + + let table = new Table({ + head: ['Metadata Type', 'API Name'], + chars: ZERO_BORDER_TABLE + }); + + let componentArray = components.toArray(); + componentArray.sort((a, b) => a.type.name.localeCompare(b.type.name)); + + for (const component of componentArray) { + let item = [component.type.name, component.fullName]; + table.push(item); + } + + SFPLogger.log('The following metadata will be deployed:', LoggerLevel.INFO, logger); + SFPLogger.log(table.toString(), LoggerLevel.INFO, logger); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/display/PackageDependencyDisplayer.ts b/packages/sfpowerscripts-cli/src/core/display/PackageDependencyDisplayer.ts new file mode 100644 index 000000000..e2540aeb9 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/display/PackageDependencyDisplayer.ts @@ -0,0 +1,35 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { ZERO_BORDER_TABLE } from './TableConstants'; +const Table = require('cli-table'); + +export default class PackageDependencyDisplayer { + public static printPackageDependencies( + dependencies: { package: string; versionNumber?: string }[], + projectConfig: any, + logger: Logger + ) { + if (Array.isArray(dependencies)) { + SFPLogger.log('Package Dependencies:', LoggerLevel.INFO, logger); + const table = new Table({ + head: ['Order','Package', 'Version'], + chars: ZERO_BORDER_TABLE, + style: { 'padding-left': 3 }, + }); + let order=1; + for (const dependency of dependencies) { + let versionNumber = 'N/A'; + + if (!dependency.versionNumber) + versionNumber = projectConfig.packageAliases[dependency.package] + ? projectConfig.packageAliases[dependency.package] + : 'N/A'; + else versionNumber = dependency.versionNumber; + + const row = [order,dependency.package, versionNumber]; + table.push(row); + order++; + } + SFPLogger.log(table.toString(), LoggerLevel.INFO, logger); + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/display/PackageMetadataPrinter.ts b/packages/sfpowerscripts-cli/src/core/display/PackageMetadataPrinter.ts new file mode 100644 index 000000000..7703c9cc2 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/display/PackageMetadataPrinter.ts @@ -0,0 +1,38 @@ +const Table = require('cli-table'); +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { ZERO_BORDER_TABLE } from './TableConstants'; + +export default class PackageMetadataPrinter { + public static printMetadataToDeploy(payload: any, logger: Logger) { + //If Manifest is null, just return + if (payload === null || payload === undefined) return; + + let table = new Table({ + head: ['Metadata Type', 'API Name'], + chars: ZERO_BORDER_TABLE + }); + + let pushTypeMembersIntoTable = (type) => { + if (type['members'] instanceof Array) { + for (let member of type['members']) { + let item = [type.name, member]; + table.push(item); + } + } else { + let item = [type.name, type.members]; + table.push(item); + } + }; + + if (payload['Package']['types'] instanceof Array) { + for (let type of payload['Package']['types']) { + pushTypeMembersIntoTable(type); + } + } else { + let type = payload['Package']['types']; + pushTypeMembersIntoTable(type); + } + SFPLogger.log('The following metadata will be deployed:', LoggerLevel.INFO, logger); + SFPLogger.log(table.toString(), LoggerLevel.INFO, logger); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/display/PushErrorDisplayer.ts b/packages/sfpowerscripts-cli/src/core/display/PushErrorDisplayer.ts new file mode 100644 index 000000000..3cfc6cf16 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/display/PushErrorDisplayer.ts @@ -0,0 +1,75 @@ +const Table = require('cli-table'); +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { ZERO_BORDER_TABLE } from './TableConstants'; + +export default class PushErrorDisplayer { + public static printMetadataFailedToPush(error: any, packageLogger: Logger) { + if (error == null) return; + + let table; + let pushComponentFailureIntoTable; + if (error.name === 'sourceConflictDetected') { + table = new Table({ + head: ['State', 'API Name', 'Metadata Type', 'File Path'], + chars: ZERO_BORDER_TABLE + }); + + pushComponentFailureIntoTable = (componentFailure) => { + let item = [ + componentFailure.state, + componentFailure.fullName, + componentFailure.type, + componentFailure.filePath, + ]; + + // Replace "undefined" values with "NA". cli-table breaks for undefined cells + item.forEach((elem, idx, item) => { + if (elem === undefined) { + item[idx] = 'NA'; + } + }); + + table.push(item); + }; + } else if (error.name === 'DeployFailed') { + table = new Table({ + head: ['Metadata Type', 'API Name', 'Problem Type', 'FilePath','Problem'], + }); + + pushComponentFailureIntoTable = (componentFailure) => { + let item = [ + componentFailure.type, + componentFailure.fullName, + componentFailure.problemType, + componentFailure.error, + componentFailure.filePath, + ]; + + // Replace "undefined" values with "NA". cli-table breaks for undefined cells + item.forEach((elem, idx, item) => { + if (elem === undefined) { + item[idx] = 'NA'; + } + }); + + table.push(item); + }; + } else { + SFPLogger.log('Unknown error type. Failed to print table.', LoggerLevel.ERROR, packageLogger); + return; + } + + if (error.data instanceof Array) { + for (let componentFailure of error.data) { + pushComponentFailureIntoTable(componentFailure); + } + } else { + let failure = error.data; + pushComponentFailureIntoTable(failure); + } + + SFPLogger.log('The following components resulted in failures:', LoggerLevel.ERROR, packageLogger); + + SFPLogger.log(table.toString(), LoggerLevel.ERROR, packageLogger); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/display/TableConstants.ts b/packages/sfpowerscripts-cli/src/core/display/TableConstants.ts new file mode 100644 index 000000000..4caeefcdc --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/display/TableConstants.ts @@ -0,0 +1,38 @@ + +export const ZERO_BORDER_TABLE = { + top: ' ', + 'top-mid': ' ', + 'top-left': ' ', + 'top-right': ' ', + bottom: ' ', + 'bottom-mid': ' ', + 'bottom-left': ' ', + 'bottom-right': ' ', + left: '', + 'left-mid': '', + mid: '', + 'mid-mid': '', + right: '', + 'right-mid': '', + middle: ' ', +}; + + + +export const COLON_MIDDLE_BORDER_TABLE = { + top: '', + 'top-mid': '', + 'top-left': '', + 'top-right': '', + bottom: '', + 'bottom-mid': '', + 'bottom-left': '', + 'bottom-right': '', + left: '', + 'left-mid': '', + mid: '', + 'mid-mid': '', + right: '', + 'right-mid': '', + middle: ':', +}; diff --git a/packages/sfpowerscripts-cli/src/core/git/Git.ts b/packages/sfpowerscripts-cli/src/core/git/Git.ts new file mode 100644 index 000000000..97089b71f --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/git/Git.ts @@ -0,0 +1,246 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import simplegit, { SimpleGit } from 'simple-git'; +import fs = require('fs-extra'); +import GitIdentity from './GitIdentity'; +const tmp = require('tmp'); + +//Git Abstraction +export default class Git { + private _git: SimpleGit; + private repositoryLocation: string; + private tempRepoLocation: any; + private _isATemporaryRepo: boolean = false; + + private constructor(private projectDir?: string, private logger?: Logger) { + if (this.projectDir) { + this._git = simplegit(this.projectDir); + this.repositoryLocation = this.projectDir; + } else { + this._git = simplegit(); + this.repositoryLocation = process.cwd(); + } + } + + async fetch() { + return this._git.fetch('origin'); + } + + async getHeadCommit(): Promise { + return this._git.revparse(['HEAD']); + } + + async show(options: string[]): Promise { + return this._git.show(options); + } + + async tag(options: string[]): Promise { + let tagResult = await this._git.tag(options); + + let temp: string[] = tagResult.split('\n'); + temp.pop(); + + return temp; + } + + async diff(options: string[]): Promise { + let diffResult = await this._git.diff(options); + + let temp: string[] = diffResult.split('\n'); + temp.pop(); + + return temp; + } + + async log(options: string[]): Promise { + let gitLogResult = await this._git.log(options); + + return gitLogResult['all'][0]['hash'].split('\n'); + } + + public async getRemoteOriginUrl(overrideOriginURL?: string): Promise { + let remoteOriginURL; + if (!overrideOriginURL) { + remoteOriginURL = (await this._git.getConfig('remote.origin.url')).value; + if (!remoteOriginURL) { + remoteOriginURL = (await this._git.getConfig('remote.origin.url')).value; + } + SFPLogger.log(`Fetched Remote URL ${remoteOriginURL}`, LoggerLevel.DEBUG); + } else remoteOriginURL = overrideOriginURL; + + if (!remoteOriginURL) throw new Error('Remote origin must be set in repository'); + + return remoteOriginURL; + } + + public async commitFile(pathToFiles: string[], message = `[skip ci] Autogenerated commit by sfp`) { + try { + await new GitIdentity(this._git).setUsernameAndEmail(); + await this._git.add(pathToFiles); + await this._git.commit(message); + SFPLogger.log(`Committed File ${pathToFiles}`); + } catch (error) { + SFPLogger.log( + `Unable to commit file, probably due to no change or something else,Please try manually`, + LoggerLevel.ERROR + ); + throw error; + } + } + + async pushTags(tags?: string[]) { + if (!tags) await this._git.pushTags(); + else { + for (let tag of tags) { + await this._git.push('origin', tag); + } + } + } + + async deleteTags(tags?: string[]) { + if (tags) await this._git.push('origin', '--delete', tags); + } + + async addAnnotatedTag(tagName: string, annotation: string, commitId?: string) { + try { + await new GitIdentity(this._git).setUsernameAndEmail(); + if (!commitId) { + await this._git.addAnnotatedTag(tagName, annotation); + } else { + const commands = ['tag', tagName, commitId, '-m', annotation]; + await this._git.raw(commands); + } + } catch (error) { + SFPLogger.log( + `Unable to commit file, probably due to no change or something else,Please try manually`, + LoggerLevel.ERROR + ); + throw error; + } + } + + public async isBranchExists(branch: string): Promise { + const listOfBranches = await this._git.branch(['-la']); + + return listOfBranches.all.find((elem) => elem.endsWith(branch)) ? true : false; + } + + static async initiateRepoAtTempLocation(logger: Logger, commitRef?: string, branch?: string): Promise { + let locationOfCopiedDirectory = tmp.dirSync({ unsafeCleanup: true }); + + SFPLogger.log(`Copying the repository to ${locationOfCopiedDirectory.name}`, LoggerLevel.INFO, logger); + let repoDir = locationOfCopiedDirectory.name; + + // Copy source directory to temp dir + fs.copySync(process.cwd(), repoDir); + + //Initiate git on new repo on using the abstracted object + let git = new Git(repoDir, logger); + git._isATemporaryRepo = true; + git.tempRepoLocation = locationOfCopiedDirectory; + + await git.addSafeConfig(repoDir); + await git.getRemoteOriginUrl(); + await git.fetch(); + if (branch) { + await git.createBranch(branch); + } + if (commitRef) { + await git.checkout(commitRef, true); + } + + SFPLogger.log( + `Successfully created temporary repository at ${repoDir} with commit ${commitRef ? commitRef : 'HEAD'}`, + LoggerLevel.INFO, + logger + ); + return git; + } + + static async initiateRepo(logger?: Logger, projectDir?: string) { + let git = new Git(projectDir, logger); + if (projectDir) await git.addSafeConfig(projectDir); + else { + await git.addSafeConfig(process.cwd()); + } + await git.getRemoteOriginUrl(); + return git; + } + + public getRepositoryPath() { + return this.repositoryLocation; + } + + async deleteTempoRepoIfAny() { + if (this.tempRepoLocation) this.tempRepoLocation.removeCallback(); + } + + async addSafeConfig(repoDir: string) { + try + { + //add workaround for safe directory (https://github.com/actions/runner/issues/2033) + await this._git.addConfig('safe.directory', repoDir, false, 'global'); + }catch(error) + { + //ignore error + SFPLogger.log(`Unable to set safe.directory`,LoggerLevel.TRACE) + } + } + + async pushToRemote(branch: string, isForce: boolean) { + if (!branch) branch = (await this._git.branch()).current; + SFPLogger.log(`Pushing ${branch}`, LoggerLevel.INFO, this.logger); + if (process.env.sfp_OVERRIDE_ORIGIN_URL) { + await this._git.removeRemote('origin'); + await this._git.addRemote('origin', process.env.sfp_OVERRIDE_ORIGIN_URL); + } + + if (isForce) { + await this._git.push('origin', branch, [`--force`]); + } else { + await this._git.push('origin', branch); + } + } + + isATemporaryRepo(): boolean { + return this._isATemporaryRepo; + } + + async getCurrentCommitId() { + return this._git.revparse(['HEAD']); + } + + async checkout(commitRef: string, isForce?: boolean) { + if (isForce) { + return this._git.checkout(commitRef, [`--force`]); + } else return this._git.checkout(commitRef, {}); + } + + async checkoutPath(commitRef: string, path: string, isForce?: boolean) { + if (isForce) { + return this._git.checkout(commitRef, [path, `--force`]); + } else return this._git.checkout(commitRef, [path]); + } + + async stageChangedFiles(path: string): Promise { + try { + await this._git.add(path); + return true; + } catch (error) { + SFPLogger.log(`Nothing to add, ignoring`, LoggerLevel.INFO, this.logger); + return false; + } + } + async createBranch(branch: string) { + if (await this.isBranchExists(branch)) { + await this._git.checkout(branch, ['-f']); + try { + // For ease-of-use when running locally and local branch exists + await this._git.merge([`refs/remotes/origin/${branch}`]); + } catch (error) { + SFPLogger.log(`Unable to find remote`, LoggerLevel.TRACE, this.logger); + } + } else { + await this._git.checkout(['-b', branch]); + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/git/GitDiffUtil.ts b/packages/sfpowerscripts-cli/src/core/git/GitDiffUtil.ts new file mode 100644 index 000000000..ce904bf9b --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/git/GitDiffUtil.ts @@ -0,0 +1,175 @@ +import * as path from 'path'; +import * as fs from 'fs-extra'; +import * as _ from 'lodash'; + +import { LoggerLevel } from '@salesforce/core'; +import simplegit, { SimpleGit } from 'simple-git'; +import SFPLogger, { Logger } from '@flxblio/sfp-logger'; +const SEP = /\/|\\/; + +export interface DiffFileStatus { + revisionFrom: string; + revisionTo: string; + path: string; + renamedPath?: string; +} + +export interface DiffFile { + deleted: DiffFileStatus[]; + addedEdited: DiffFileStatus[]; +} + +const git: SimpleGit = simplegit(); + +export default class GitDiffUtils { + private gitTreeRevisionTo: { + revision: string; + path: string; + }[]; + + public async isFileIncludesContent(diffFile: DiffFileStatus, content: string): Promise { + let fileAsString = await git.show(['--raw', diffFile.revisionFrom]); + let result = fileAsString.includes(content); + return result; + } + + public async fetchFileListRevisionTo(revisionTo: string, logger: Logger) { + SFPLogger.log('Fetching file list from target revision ' + revisionTo, LoggerLevel.TRACE, logger); + this.gitTreeRevisionTo = []; + let revisionTree = await git.raw(['ls-tree', '-r', revisionTo]); + const sepRegex = /\n|\r/; + let lines = revisionTree.split(sepRegex); + for (let i = 0; i < lines.length; i++) { + if (lines[i] === '') continue; + let fields = lines[i].split(/\t/); + let pathStr = fields[1]; + let revisionSha = fields[0].split(/\s/)[2]; + let fileMetadata = { + revision: revisionSha, + path: path.join('.', pathStr), + }; + this.gitTreeRevisionTo.push(fileMetadata); + } + return this.gitTreeRevisionTo; + } + + public async copyFile(filePath: string, outputFolder: string, logger: Logger) { + SFPLogger.log(`Copying file ${filePath} from git to ${outputFolder}`, LoggerLevel.TRACE, logger); + if (fs.existsSync(path.join(outputFolder, filePath))) { + SFPLogger.log(`File ${filePath} already in output folder. `, LoggerLevel.TRACE, logger); + return; + } + + let gitFiles: { + revision: string; + path: string; + }[] = []; + this.gitTreeRevisionTo.forEach((file) => { + if (file.path === filePath) { + gitFiles.push(file); + } + }); + + if(gitFiles.length==0) + throw new Error(`Unable to find the required file ${filePath} in Git.., Did you really commit the file?`) + + let copyOutputFolder = outputFolder; + for (let i = 0; i < gitFiles.length; i++) { + outputFolder = copyOutputFolder; + let gitFile = gitFiles[i]; + + SFPLogger.log( + `Associated file ${i}: ${gitFile.path} Revision: ${gitFile.revision}`, + LoggerLevel.TRACE, + logger + ); + + let outputPath = path.join(outputFolder, gitFile.path); + + let filePathParts = gitFile.path.split(SEP); + + if (fs.existsSync(outputFolder) == false) { + fs.mkdirSync(outputFolder); + } + // Create folder structure + for (let i = 0; i < filePathParts.length - 1; i++) { + let folder = filePathParts[i].replace('"', ''); + outputFolder = path.join(outputFolder, folder); + if (fs.existsSync(outputFolder) == false) { + fs.mkdirSync(outputFolder); + } + } + let fileContent = await git.binaryCatFile(['-p', gitFile.revision]); + fs.writeFileSync(outputPath, fileContent); + } + } + + public async copyFolder(folderPath: string, outputFolder: string, logger: Logger) { + SFPLogger.log(`Copying folder ${folderPath} from git to ${outputFolder}`, LoggerLevel.TRACE, logger); + if (fs.existsSync(path.join(outputFolder, folderPath))) { + SFPLogger.log(`Folder ${folderPath} already in output folder. `, LoggerLevel.TRACE, logger); + return; + } + + this.gitTreeRevisionTo.forEach((file) => { + let fileToCompare = file.path; + if (fileToCompare.startsWith(folderPath)) { + this.copyFile(fileToCompare, outputFolder, logger); + } + }); + } + + public getChangedOrAdded(list1: any[], list2: any[], key: string) { + let result: any = { + addedEdited: [], + deleted: [], + }; + + //Ensure array + if (!_.isNil(list1) && !Array.isArray(list1)) { + list1 = [list1]; + } + if (!_.isNil(list2) && !Array.isArray(list2)) { + list2 = [list2]; + } + + if (_.isNil(list1) && !_.isNil(list2) && list2.length > 0) { + result.addedEdited.push(...list2); + } + + if (_.isNil(list2) && !_.isNil(list1) && list1.length > 0) { + result.deleted.push(...list1); + } + + if (!_.isNil(list1) && !_.isNil(list2)) { + list1.forEach((elem1) => { + let found = false; + for (let i = 0; i < list2.length; i++) { + let elem2 = list2[i]; + if (elem1[key] === elem2[key]) { + //check if edited + if (!_.isEqual(elem1, elem2)) { + result.addedEdited.push(elem2); + } + found = true; + break; + } + } + if (!found) { + result.deleted.push(elem1); + } + }); + + //Check for added elements + + let addedElement = _.differenceWith(list2, list1, function (element1: any, element2: any) { + return element1[key] === element2[key]; + }); + + if (!_.isNil(addedElement)) { + result.addedEdited.push(...addedElement); + } + } + return result; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/git/GitIdentity.ts b/packages/sfpowerscripts-cli/src/core/git/GitIdentity.ts new file mode 100644 index 000000000..ecc806d82 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/git/GitIdentity.ts @@ -0,0 +1,34 @@ +import { SimpleGit } from 'simple-git/promise'; + +export default class GitIdentity { + constructor(private git: SimpleGit) {} + + async setUsernameAndEmail(): Promise { + await this.setUsername(); + await this.setEmail(); + } + + private async setUsername(): Promise { + let username: string; + + if (process.env.sfp_GIT_USERNAME) { + username = process.env.sfp_GIT_USERNAME; + } else { + username = 'sfp'; + } + + await this.git.addConfig('user.name', username); + } + + private async setEmail(): Promise { + let email: string; + + if (process.env.sfp_GIT_EMAIL) { + email = process.env.sfp_GIT_EMAIL; + } else { + email = 'sfp@flxblio.io'; + } + + await this.git.addConfig('user.email', email); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/git/GitTags.ts b/packages/sfpowerscripts-cli/src/core/git/GitTags.ts new file mode 100644 index 000000000..846585451 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/git/GitTags.ts @@ -0,0 +1,151 @@ +import Git from './Git'; +import child_process = require('child_process'); + +export default class GitTags { + constructor(private git: Git, private sfdx_package: string) {} + + /*** + * Returns list of sorted tags, belonging to package, that are reachable from HEAD and + * follow the first parent on merge commits. + * If there are no tags, returns empty array + * @param sfdx_package + */ + async listTagsOnBranch(): Promise { + let tags: string[] = await this.git.tag([ + `-l`, + `${this.sfdx_package}_v*`, + `--sort=creatordate`, + `--merged`, + ]); + + if (tags.length > 0) return this.filterTagsAgainstBranch(tags); + else return tags; + } + + private async filterTagsAgainstBranch(tags: string[]): Promise { + // Get full-length commit ID's on the current branch, following the first parent on merge commits + let commits: string[] = await this.git.log([`--pretty=format:%H`, `--first-parent`]); + + // Get the tags' associated commit ID + // Dereference (-d) tags into object IDs + //TODO: Remove this direct usage + let gitShowRefTagsBuffer = child_process.execSync(`git show-ref --tags -d | grep "${this.sfdx_package}_v*"`, { + maxBuffer: 5 * 1024 * 1024, + stdio: 'pipe', + cwd: this.git.getRepositoryPath() + }); + + let gitShowRefTags = gitShowRefTagsBuffer.toString(); + + let refTags: string[] = gitShowRefTags.split('\n'); + refTags.pop(); // Remove last empty element + + // Filter ref tags, only including tags that point to the branch + // By checking whether all 40 digits in the tag commit ID matches an ID in the branch's commit log + let refTagsPointingToBranch: string[] = refTags.filter((refTag) => commits.includes(refTag.substring(0, 40))); + + // Only match the name of the tags pointing to the branch + refTagsPointingToBranch = refTagsPointingToBranch.map( + (refTagPointingToBranch) => refTagPointingToBranch.match(/(?:refs\/tags\/)(.*)((?:-ALIGN)|(?:\^{}))/)[1] + ); + + // Filter the sorted tags - only including tags that point to the branch + let tagsPointingToBranch: string[] = tags.filter((tag) => refTagsPointingToBranch.includes(tag)); + + return tagsPointingToBranch; + } + + public async getVersionFromLatestTag(): Promise { + let version: string; + + let tags = await this.listTagsOnBranch(); + let latestTag = tags.pop(); + if (latestTag) { + let match: RegExpMatchArray = latestTag.match( + /^.*_v(?[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+|\.LATEST|\.NEXT)?(\-ALIGN)?)$/ + ); + if (match) version = this.substituteBuildNumberWithPreRelease(match.groups.version); + else throw new Error(`Failed to find valid tag for ${this.sfdx_package}`); + } else throw new Error(`Failed to find latest tag for ${this.sfdx_package}`); + + return version; + } + + private substituteBuildNumberWithPreRelease(packageVersionNumber: string) { + let segments = packageVersionNumber.split('.'); + //Strip ALIGN + if (segments.length == 4 && segments[3].includes('ALIGN')) { + segments[3] = segments[3].substring(0, segments[3].indexOf('-')); + } + + if (segments.length === 4) { + packageVersionNumber = segments.reduce((version, segment, segmentsIdx) => { + if (segmentsIdx === 3) return version + '-' + segment; + else return version + '.' + segment; + }); + } + + return packageVersionNumber; + } + + + public async limitTags(limit: number): Promise{ + let rawTags = await this.listTagsOnBranch(); + + if (rawTags.length <= limit) { + return []; + } + + const tags:string [] = rawTags.slice(0, Math.abs(limit) * -1); + return tags; + } + + + public async filteredOldTags(daysToKeep: number, limit?: number): Promise { + const currentTimestamp = Math.floor(Date.now() / 1000); + + let rawTags: string[]; + if (limit) { + rawTags = await this.limitTags(limit); + } else { + rawTags = await this.listTagsOnBranch(); + } + + if (rawTags.length < 0) { + return []; + } + + let tags: string[] = await this.getTagsWithTimestamps(rawTags); + + const filteredTags = tags + .map(tagStr => { + const [name, timestampStr] = tagStr.split(' '); + const timestamp = parseInt(timestampStr, 10); + return { name, timestamp }; + }) + .filter(tag => { + const daysSinceTag = (currentTimestamp - tag.timestamp) / 86400; + return tag.name && daysSinceTag > daysToKeep; + }); + + return filteredTags.map(tag => tag.name); + } + + private async getTagsWithTimestamps(tags: string[]): Promise { + const timestampPromises: Promise[] = []; + + // Create an array of promises that will get the tagger date for each tag + tags.forEach((tag: string) => { + timestampPromises.push( + this.git.log(['--format=%at', `refs/tags/${tag}`]) + .then((output: string[]) => parseInt(output[0].trim(), 10)) + ); + }); + + // Wait for all promises to resolve and format the output + const timestamps: number[] = await Promise.all(timestampPromises); + const tagsWithTimestamp = tags.map((tag: string, index: number) => `${tag} ${timestamps[index]}`); + return tagsWithTimestamp + } + +} diff --git a/packages/sfpowerscripts-cli/src/core/ignore/IgnoreFiles.ts b/packages/sfpowerscripts-cli/src/core/ignore/IgnoreFiles.ts new file mode 100644 index 000000000..9a48420f9 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/ignore/IgnoreFiles.ts @@ -0,0 +1,13 @@ +import ignore, { Ignore } from 'ignore'; + +export default class IgnoreFiles { + private _ignore: Ignore; + + constructor(pattern: string) { + this._ignore = ignore().add(pattern); + } + + filter(pathnames: string[]): string[] { + return this._ignore.filter(pathnames); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/limits/LimitsFetcher.ts b/packages/sfpowerscripts-cli/src/core/limits/LimitsFetcher.ts new file mode 100644 index 000000000..da4e69186 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/limits/LimitsFetcher.ts @@ -0,0 +1,30 @@ +import { Connection } from '@salesforce/core'; +const retry = require('async-retry'); + +export default class LimitsFetcher { + constructor(private conn: Connection) {} + + public async getApiLimits() { + const limits: { name: string; max: number; remaining: number }[] = []; + const endpoint = `${this.conn.instanceUrl}/services/data/v${this.conn.version}/limits`; + + const result = await retry( + async (bail) => { + return this.conn.request<{ + [p: string]: { Max: number; Remaining: number }; + }>(endpoint); + }, + { retries: 3, minTimeout: 2000 } + ); + + Object.keys(result).forEach((limitName) => { + limits.push({ + name: limitName, + max: result[limitName].Max, + remaining: result[limitName].Remaining, + }); + }); + + return limits; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/metadata/CustomFieldFetcher.ts b/packages/sfpowerscripts-cli/src/core/metadata/CustomFieldFetcher.ts new file mode 100644 index 000000000..063dbbbd3 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/metadata/CustomFieldFetcher.ts @@ -0,0 +1,45 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import SFPOrg from '../org/SFPOrg'; +const fs = require('fs-extra'); +import { XMLParser } from 'fast-xml-parser'; +import MetadataFetcher from './MetadataFetcher'; +import { + ComponentSet, + MetadataConverter, + MetadataResolver, + ZipTreeContainer, +} from '@salesforce/source-deploy-retrieve'; +import path from 'path'; +import { makeRandomId } from '../utils/RandomId'; + +export default class CustomFieldFetcher extends MetadataFetcher { + constructor(logger: Logger) { + super(logger); + } + + public async getCustomFields(org: SFPOrg, fields: string[]) { + SFPLogger.log(`Fetching Custom Fields from Org`, LoggerLevel.INFO, this.logger); + let retriveLocation = await this.fetchPackageFromOrg(org, { + types: { name: 'CustomField', members: fields.length > 1 ? fields : fields[0] }, + }); + + const zipTree = await ZipTreeContainer.create(fs.readFileSync(retriveLocation.zipLocation)); + const zipResolver = new MetadataResolver(undefined, zipTree); + const zipComponents = zipResolver.getComponentsFromPath('.'); + let packageName = makeRandomId(6); + await new MetadataConverter().convert(zipComponents, 'source', { + type: 'directory', + outputDirectory: path.join(retriveLocation.unzippedLocation, 'source'), + packageName: packageName + }); + + //Write a force ignore file as its required for component set resolution + fs.writeFileSync(path.resolve(retriveLocation.unzippedLocation, 'source', '.forceignore'), '# .forceignore v2'); + + let sourceBackedComponents = ComponentSet.fromSource(path.resolve(retriveLocation.unzippedLocation, 'source')); + + return {components:sourceBackedComponents,location:path.join(retriveLocation.unzippedLocation, 'source',packageName)} + } + + +} diff --git a/packages/sfpowerscripts-cli/src/core/metadata/MetadataFetcher.ts b/packages/sfpowerscripts-cli/src/core/metadata/MetadataFetcher.ts new file mode 100644 index 000000000..ef41f6061 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/metadata/MetadataFetcher.ts @@ -0,0 +1,71 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import SFPOrg from '../org/SFPOrg'; +import { delay } from '../utils/Delay'; +const fs = require('fs-extra'); +import AdmZip = require('adm-zip'); +import { Connection } from '@salesforce/core'; +import { RetrieveResult } from 'jsforce/lib/api/metadata'; +import { makeRandomId } from '../utils/RandomId'; + +export default class MetadataFetcher { + constructor(protected logger: Logger) {} + + + protected async fetchPackageFromOrg(org: SFPOrg, members: any) { + let connection = org.getConnection(); + const apiversion = await org.getConnection().retrieveMaxApiVersion(); + + let retrieveRequest = { + apiVersion: Number(apiversion), + }; + + retrieveRequest['singlePackage'] = true; + retrieveRequest['unpackaged'] = members; + connection.metadata.pollTimeout = 60; + let retrievedId = await connection.metadata.retrieve(retrieveRequest); + SFPLogger.log(`Fetching metadata from ${connection.getUsername()}`, LoggerLevel.DEBUG, this.logger); + + let metadata_retrieve_result = await this.checkRetrievalStatus(connection, retrievedId.id); + if (!metadata_retrieve_result.zipFile) + SFPLogger.log('Unable to find the requested metadata', LoggerLevel.ERROR, this.logger); + + let retriveLocation = `.sfp/retrieved/${retrievedId.id}`; + //Extract Security + let zipFileName = `${retriveLocation}/unpackaged_${makeRandomId(8)}.zip`; + fs.mkdirpSync(retriveLocation); + fs.writeFileSync(zipFileName, metadata_retrieve_result.zipFile, { + encoding: 'base64', + }); + this.extract(retriveLocation, zipFileName); + // fs.unlinkSync(zipFileName); + return {zipLocation:zipFileName,unzippedLocation:retriveLocation}; + } + + private async checkRetrievalStatus( + conn: Connection, + retrievedId: string, + isToBeLoggedToConsole: boolean = true + ): Promise { + let metadata_result:RetrieveResult; + + while (true) { + metadata_result = await conn.metadata.checkRetrieveStatus(retrievedId); + + if (metadata_result.done === false) { + if (isToBeLoggedToConsole) SFPLogger.log(`Polling for Retrieval Status`, LoggerLevel.INFO, this.logger); + await delay(5000); + } else { + //this.ux.logJson(metadata_result); + break; + } + } + return metadata_result; + } + + + private extract(unzippedDirectory: string, zipFile: string) { + let zip = new AdmZip(zipFile); + // Overwrite existing files + zip.extractAllTo(unzippedDirectory, true); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/metadata/MetadataFiles.ts b/packages/sfpowerscripts-cli/src/core/metadata/MetadataFiles.ts new file mode 100644 index 000000000..cffbea0d6 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/metadata/MetadataFiles.ts @@ -0,0 +1,343 @@ +//TODO: Replace with SDR +import * as path from 'path'; +import { MetadataInfo, METADATA_INFO, MetadataDescribe, SOURCE_EXTENSION_REGEX } from './MetadataInfo'; +import FileUtils from '../utils/Fileutils'; +import * as _ from 'lodash'; +import ignore from 'ignore'; +import * as fs from 'fs-extra'; +import ProjectConfig from '../project/ProjectConfig'; +import { globSync } from 'glob'; + +const SEP = /\/|\\/; + +export default class MetadataFiles { + public static sourceOnly: boolean = false; + forceignore: any; + public constructor() { + if (fs.existsSync('.forceignore')) { + this.forceignore = ignore().add(fs.readFileSync('.forceignore', 'utf8').toString()); + } else { + this.forceignore = ignore(); + } + } + static getFullApiName(fileName: string): string { + let fullName = ''; + let metadateType = MetadataInfo.getMetadataName(fileName); + let splitFilepath = fileName.split(SEP); + let isObjectChild = METADATA_INFO.CustomObject.childXmlNames.includes(metadateType); + if (isObjectChild) { + let objectName = splitFilepath[splitFilepath.length - 3]; + let fieldName = splitFilepath[splitFilepath.length - 1].split('.')[0]; + fullName = objectName.concat('.' + fieldName); + } else { + fullName = splitFilepath[splitFilepath.length - 1].split('.')[0]; + } + return fullName; + } + static getFullApiNameWithExtension(fileName: string): string { + let fullName = ''; + let metadateType = MetadataInfo.getMetadataName(fileName); + let splitFilepath = fileName.split(SEP); + let isObjectChild = METADATA_INFO.CustomObject.childXmlNames.includes(metadateType); + if (isObjectChild) { + let objectName = splitFilepath[splitFilepath.length - 3]; + let fieldName = splitFilepath[splitFilepath.length - 1]; + fullName = objectName.concat('.' + fieldName); + } else { + fullName = splitFilepath[splitFilepath.length - 1]; + } + return fullName; + } + + public static isCustomMetadata(filepath: string, name: string): boolean { + let result = true; + let splitFilepath = filepath.split(SEP); + let componentName = splitFilepath[splitFilepath.length - 1]; + componentName = componentName.substring(0, componentName.indexOf('.')); + if (name === METADATA_INFO.CustomField.xmlName || name === METADATA_INFO.CustomObject.xmlName) { + //Custom Field or Custom Object + result = componentName.endsWith('__c') || componentName.endsWith('__mdt'); + } + return result; + } + public static getMemberNameFromFilepath(filepath: string, name: string): string { + let member: string; + let splitFilepath = filepath.split(SEP); + let lastIndex = splitFilepath.length - 1; + let isObjectChild = METADATA_INFO.CustomObject.childXmlNames.includes(name); + let metadataDescribe: MetadataDescribe = METADATA_INFO[name]; + if (isObjectChild) { + let objectName = splitFilepath[lastIndex - 2]; + let fieldName = splitFilepath[lastIndex].split('.')[0]; + member = objectName.concat('.' + fieldName); + } else if (metadataDescribe.inFolder) { + let baseName = metadataDescribe.directoryName; + let baseIndex = filepath.indexOf(baseName) + baseName.length; + let cmpPath = filepath.substring(baseIndex + 1); // add 1 to remove the path seperator + cmpPath = cmpPath.substring(0, cmpPath.indexOf('.')); + member = cmpPath.replace(SEP, '/'); + } else { + if (SOURCE_EXTENSION_REGEX.test(splitFilepath[lastIndex])) { + member = splitFilepath[lastIndex].replace(SOURCE_EXTENSION_REGEX, ''); + } else { + const auraRegExp = new RegExp('aura'); + const lwcRegExp = new RegExp('lwc'); + const staticResourceRegExp = new RegExp('staticresources'); + const experienceBundleRegExp = new RegExp('experiences'); + if (auraRegExp.test(filepath) || lwcRegExp.test(filepath)) { + member = splitFilepath[lastIndex - 1]; + } else if (staticResourceRegExp.test(filepath)) { + //Return the fileName + let baseName = 'staticresources'; + let baseIndex = filepath.indexOf(baseName) + baseName.length; + let cmpPath = filepath.substring(baseIndex + 1); // add 1 to remove the path seperator + member = cmpPath.split(SEP)[0]; + let extension = path.parse(member).ext; + + member = member.replace(new RegExp(extension + '$'), ''); + } else if (experienceBundleRegExp.test(filepath)) { + //Return the fileName + let baseName = 'experiences'; + let baseIndex = filepath.indexOf(baseName) + baseName.length; + let cmpPath = filepath.substring(baseIndex + 1); // add 1 to remove the path seperator + member = cmpPath.split(SEP)[0]; + let extension = path.parse(member).ext; + + member = member.replace(new RegExp(extension + '$'), ''); + } else { + let extension = path.parse(splitFilepath[lastIndex]).ext; + member = splitFilepath[lastIndex].replace(new RegExp(extension + '$'), ''); + } + } + } + return member; + } + + public loadComponents(srcFolder: string, checkIgnore = true): void { + var metadataFiles: string[] = FileUtils.getAllFilesSync(srcFolder); + let keys = Object.keys(METADATA_INFO); + if (Array.isArray(metadataFiles) && metadataFiles.length > 0) { + metadataFiles.forEach((metadataFile) => { + let found = false; + + for (let i = 0; i < keys.length; i++) { + let match = false; + if (metadataFile.endsWith(METADATA_INFO[keys[i]].sourceExtension)) { + match = true; + } else if ( + METADATA_INFO[keys[i]].inFolder && + metadataFile.endsWith(METADATA_INFO[keys[i]].folderExtension) + ) { + match = true; + } + if (match) { + if (_.isNil(METADATA_INFO[keys[i]].files)) { + METADATA_INFO[keys[i]].files = []; + METADATA_INFO[keys[i]].components = []; + } + if (!checkIgnore || (checkIgnore && this.accepts(metadataFile))) { + METADATA_INFO[keys[i]].files.push(metadataFile); + + let name = FileUtils.getFileNameWithoutExtension( + metadataFile, + METADATA_INFO[keys[i]].sourceExtension + ); + + if (METADATA_INFO[keys[i]].isChildComponent) { + let fileParts = metadataFile.split(SEP); + let parentName = fileParts[fileParts.length - 3]; + name = parentName + '.' + name; + } + + METADATA_INFO[keys[i]].components.push(name); + } + found = true; + break; + } + } + + if (!found) { + const auraRegExp = new RegExp('aura'); + if (auraRegExp.test(metadataFile) && SOURCE_EXTENSION_REGEX.test(metadataFile)) { + if (_.isNil(METADATA_INFO.AuraDefinitionBundle.files)) { + METADATA_INFO.AuraDefinitionBundle.files = []; + METADATA_INFO.AuraDefinitionBundle.components = []; + } + if (!checkIgnore || (checkIgnore && this.accepts(metadataFile))) { + METADATA_INFO.AuraDefinitionBundle.files.push(metadataFile); + + let name = FileUtils.getFileNameWithoutExtension(metadataFile); + METADATA_INFO.AuraDefinitionBundle.components.push(name); + } + } + } + }); + } else { + keys.forEach((key) => { + if (_.isNil(METADATA_INFO[key].files)) { + METADATA_INFO[key].files = []; + METADATA_INFO[key].components = []; + } + }); + } + } + //Check if a component is accepted by forceignore. + public accepts(filePath: string) { + return !this.forceignore.ignores(path.relative(process.cwd(), filePath)); + } + + public async isInModuleFolder(filePath: string) { + const packageDirectories = ProjectConfig.getSFDXProjectConfig(null).packageDirectories.map((elem) => elem.path); + if (!packageDirectories || packageDirectories.length == 0) { + return false; + } + const moduleFolder = packageDirectories.find((packageFolder) => { + let packageFolderNormalized = path.relative('', packageFolder); + return filePath.startsWith(packageFolderNormalized); + }); + return moduleFolder !== undefined; + } + + /** + * Copy a file to an outpu directory. If the filePath is a Metadata file Path, + * All the metadata requirement are also copied. For example MyApexClass.cls-meta.xml will also copy MyApexClass.cls. + * Enforcing the .forceignore to ignire file ignored in the project. + * @param filePath + * @param outputFolder + */ + public static copyFile(filePath: string, outputFolder: string) { + console.log(`Copying file ${filePath} from file system to ${outputFolder}`); + const LWC_IGNORE_FILES = ['jsconfig.json', '.eslintrc.json']; + const pairStatResources = METADATA_INFO.StaticResource.directoryName; + const pairStatResourcesRegExp = new RegExp(pairStatResources); + const pairAuaraRegExp = new RegExp(METADATA_INFO.AuraDefinitionBundle.directoryName); + + let copyOutputFolder = outputFolder; + + if (!fs.existsSync(filePath)) { + return; + } + + let exists = fs.existsSync(path.join(outputFolder, filePath)); + if (exists) { + return; + } + + if (filePath.startsWith('.')) { + let parts = path.parse(filePath); + if (parts.dir === '') { + fs.copyFileSync(filePath, path.join(outputFolder, filePath)); + return; + } + } + + let fileName = path.parse(filePath).base; + //exclude lwc ignored files + if (LWC_IGNORE_FILES.includes(fileName)) { + return; + } + + let filePathParts = filePath.split(SEP); + + if (fs.existsSync(outputFolder) == false) { + fs.mkdirSync(outputFolder); + } + // Create folder structure + for (let i = 0; i < filePathParts.length - 1; i++) { + let folder = filePathParts[i].replace('"', ''); + outputFolder = path.join(outputFolder, folder); + if (fs.existsSync(outputFolder) == false) { + fs.mkdirSync(outputFolder); + } + } + + // Copy all file with same base name + let associatedFilePattern = ''; + if (SOURCE_EXTENSION_REGEX.test(filePath)) { + associatedFilePattern = filePath.replace(SOURCE_EXTENSION_REGEX, '.*'); + } else { + let extension = path.parse(filePath).ext; + associatedFilePattern = filePath.replace(extension, '.*'); + } + let files = globSync(associatedFilePattern); + for (let i = 0; i < files.length; i++) { + if (fs.lstatSync(files[i]).isDirectory() == false) { + let oneFilePath = path.join('.', files[i]); + let oneFilePathParts = oneFilePath.split(SEP); + fileName = oneFilePathParts[oneFilePathParts.length - 1]; + let outputPath = path.join(outputFolder, fileName); + fs.copyFileSync(files[i], outputPath); + } + } + + // Hadle ObjectTranslations + // If a file fieldTranslation is copied, make sure the ObjectTranslation File is also copied + if (filePath.endsWith('Translation-meta.xml') && filePath.indexOf('globalValueSet') < 0) { + let parentFolder = filePathParts[filePathParts.length - 2]; + let objectTranslation = parentFolder + METADATA_INFO.CustomObjectTranslation.sourceExtension; + let outputPath = path.join(outputFolder, objectTranslation); + let sourceFile = filePath.replace(fileName, objectTranslation); + if (fs.existsSync(sourceFile) == true) { + fs.copyFileSync(sourceFile, outputPath); + } + } + + //FOR STATIC RESOURCES - WHERE THE CORRESPONDING DIRECTORY + THE ROOT META FILE HAS TO BE INCLUDED + if (pairStatResourcesRegExp.test(filePath)) { + outputFolder = path.join('.', copyOutputFolder); + let srcFolder = '.'; + let staticRecourceRoot = ''; + let resourceFile = ''; + for (let i = 0; i < filePathParts.length; i++) { + outputFolder = path.join(outputFolder, filePathParts[i]); + srcFolder = path.join(srcFolder, filePathParts[i]); + if (filePathParts[i] === METADATA_INFO.StaticResource.directoryName) { + let fileOrDirname = filePathParts[i + 1]; + let fileOrDirnameParts = fileOrDirname.split('.'); + srcFolder = path.join(srcFolder, fileOrDirnameParts[0]); + outputFolder = path.join(outputFolder, fileOrDirnameParts[0]); + resourceFile = srcFolder + METADATA_INFO.StaticResource.sourceExtension; + METADATA_INFO.StaticResource.sourceExtension; + staticRecourceRoot = outputFolder + METADATA_INFO.StaticResource.sourceExtension; + if (fs.existsSync(srcFolder)) { + if (fs.existsSync(outputFolder) == false) { + fs.mkdirSync(outputFolder); + } + } + break; + } + } + if (fs.existsSync(srcFolder)) { + FileUtils.copyRecursiveSync(srcFolder, outputFolder); + } + if (fs.existsSync(resourceFile)) { + fs.copyFileSync(resourceFile, staticRecourceRoot); + } + } + //FOR AURA components and LWC components + if (pairAuaraRegExp.test(filePath)) { + outputFolder = path.join('.', copyOutputFolder); + let srcFolder = '.'; + for (let i = 0; i < filePathParts.length; i++) { + outputFolder = path.join(outputFolder, filePathParts[i]); + srcFolder = path.join(srcFolder, filePathParts[i]); + if (filePathParts[i] === 'aura' || filePathParts[i] === 'lwc') { + let fileOrDirname = filePathParts[i + 1]; + let fileOrDirnameParts = fileOrDirname.split('.'); + srcFolder = path.join(srcFolder, fileOrDirnameParts[0]); + outputFolder = path.join(outputFolder, fileOrDirnameParts[0]); + + if (fs.existsSync(srcFolder)) { + if (fs.existsSync(outputFolder) == false) { + fs.mkdirSync(outputFolder); + } + } + break; + } + } + if (fs.existsSync(srcFolder)) { + FileUtils.copyRecursiveSync(srcFolder, outputFolder); + } + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/metadata/MetadataInfo.ts b/packages/sfpowerscripts-cli/src/core/metadata/MetadataInfo.ts new file mode 100644 index 000000000..b3a888cce --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/metadata/MetadataInfo.ts @@ -0,0 +1,214 @@ +//TODO: Replace with SDR +import * as _ from 'lodash'; +import * as path from 'path'; +import * as fs from 'fs-extra'; + +export const SOURCE_EXTENSION_REGEX = /\.[a-zA-Z]+-meta\.xml/; +const SPLITED_TYPES = { + CustomField: { + suffix: 'field', + folder: 'fields', + }, + BusinessProcess: { + suffix: 'businessProcess', + folder: 'businessProcesses', + }, + CompactLayout: { + suffix: 'compactLayout', + folder: 'compactLayouts', + }, + FieldSet: { + suffix: 'fieldSet', + folder: 'fieldSets', + }, + RecordType: { + suffix: 'recordType', + folder: 'recordTypes', + }, + ListView: { + suffix: 'listView', + folder: 'listViews', + }, + SharingReason: { + suffix: 'sharingReason', + folder: 'sharingReasons', + }, + ValidationRule: { + suffix: 'validationRule', + folder: 'validationRules', + }, + WebLink: { + suffix: 'webLink', + folder: 'webLinks', + }, +}; + +export interface MetadataDescribe { + directoryName?: string; + inFolder?: boolean; + metaFile?: boolean; + suffix?: string; + xmlName?: string; + sourceExtension?: string; + childXmlNames?: string[]; + folderExtension?: string; + files?: string[]; + components?: string[]; + isChildComponent?: boolean; +} + +export interface MetadataInfo { + CustomApplication?: MetadataDescribe; + ApexClass?: MetadataDescribe; + ApexPage?: MetadataDescribe; + CustomField?: MetadataDescribe; + CustomObject?: MetadataDescribe; + CustomPermission?: MetadataDescribe; + ExternalDataSource?: MetadataDescribe; + ExperienceBundle?: MetadataDescribe; + Flow?: MetadataDescribe; + RecordType?: MetadataDescribe; + ListView?: MetadataDescribe; + WebLink?: MetadataDescribe; + ValidationRule?: MetadataDescribe; + CompactLayout?: MetadataDescribe; + BujsinessProcess?: MetadataDescribe; + CustomTab?: MetadataDescribe; + Layout?: MetadataDescribe; + Profile?: MetadataDescribe; + Translations?: MetadataDescribe; + CustomLabel?: MetadataDescribe; + CustomLabels?: MetadataDescribe; + GlobalValueSet?: MetadataDescribe; + CustomMetadata?: MetadataDescribe; + Document?: MetadataDescribe; + Queue?: MetadataDescribe; + Group?: MetadataDescribe; + Role?: MetadataDescribe; + Report?: MetadataDescribe; + Dashboard?: MetadataDescribe; + EmailTemplate?: MetadataDescribe; + CustomSite?: MetadataDescribe; + PermissionSet?: MetadataDescribe; + StaticResource?: MetadataDescribe; + CustomObjectTranslation?: MetadataDescribe; + AuraDefinitionBundle?: MetadataDescribe; + Workflow?: MetadataDescribe; + SharingRules?: MetadataDescribe; + LightningComponentBundle?: MetadataDescribe; +} + +export class MetadataInfo { + static loadMetadataInfo(): MetadataInfo { + let metadataInfo: MetadataInfo = {}; + let resourcePath = path.join(__dirname, '..', '..', '..', 'resources', 'metadatainfo.json'); + const fileData = fs.readFileSync(resourcePath, 'utf8'); + let metadataInfoJSON = JSON.parse(fileData); + metadataInfoJSON.metadataObjects.forEach((metadata) => { + let metadataDescribe = metadata as MetadataDescribe; + if (_.isNil(metadata.suffix)) { + if (metadata.xmlName === 'AuraDefinitionBundle') { + metadata.suffix = 'cmp'; + metadataDescribe.suffix = 'cmp'; + } else if (metadata.xmlName == 'LightningComponentBundle') { + metadata.suffix = 'js'; + metadataDescribe.suffix = 'js'; + } + } + metadataDescribe.sourceExtension = `.${metadata.suffix}-meta.xml`; + if (metadata.inFolder) { + let folderExtensionPrefix = metadata.suffix; + if (_.isNil(metadata.suffix)) { + folderExtensionPrefix = metadata.xmlName.charAt(0).toLowerCase + metadata.xmlName.slice(1); + } + metadataDescribe.folderExtension = `.${folderExtensionPrefix}Folder-meta.xml`; + } + + //Generate Describe of cheildItems if exists + if (!_.isNil(metadata.childXmlNames)) { + metadata.childXmlNames.forEach((element) => { + let splitedElement = SPLITED_TYPES[element]; + if (!_.isNil(splitedElement)) { + let childDescribe: MetadataDescribe = {}; + childDescribe.directoryName = SPLITED_TYPES[element].folder; + childDescribe.suffix = SPLITED_TYPES[element].suffix; + childDescribe.xmlName = element; + childDescribe.inFolder = false; + childDescribe.metaFile = false; + childDescribe.isChildComponent = true; + childDescribe.sourceExtension = `.${SPLITED_TYPES[element].suffix}-meta.xml`; + metadataInfo[childDescribe.xmlName] = childDescribe; + } + }); + } + metadataInfo[metadataDescribe.xmlName] = metadataDescribe; + }); + return metadataInfo; + } + + static getMetadataName(metadataFile: string, validateSourceExtension = true): string { + let matcher = metadataFile.match(SOURCE_EXTENSION_REGEX); + let extension = ''; + if (matcher) { + extension = matcher[0]; + } else { + extension = path.parse(metadataFile).ext; + } + //SfPowerKit.ux.log(extension); + let metadataName = ''; + + const auraRegExp = new RegExp('aura'); + const lwcRegExp = new RegExp('lwc'); + const staticResourceRegExp = new RegExp('staticresources'); + const experienceBundleRegExp = new RegExp('experiences'); + const documentRegExp = new RegExp('documents'); + if (auraRegExp.test(metadataFile) && (SOURCE_EXTENSION_REGEX.test(metadataFile) || !validateSourceExtension)) { + metadataName = METADATA_INFO.AuraDefinitionBundle.xmlName; + } else if ( + lwcRegExp.test(metadataFile) && + (SOURCE_EXTENSION_REGEX.test(metadataFile) || !validateSourceExtension) + ) { + metadataName = METADATA_INFO.LightningComponentBundle.xmlName; + } else if ( + staticResourceRegExp.test(metadataFile) && + (SOURCE_EXTENSION_REGEX.test(metadataFile) || !validateSourceExtension) + ) { + metadataName = METADATA_INFO.StaticResource.xmlName; + } else if ( + experienceBundleRegExp.test(metadataFile) && + (SOURCE_EXTENSION_REGEX.test(metadataFile) || !validateSourceExtension) + ) { + metadataName = METADATA_INFO.ExperienceBundle.xmlName; + } else if ( + documentRegExp.test(metadataFile) && + (SOURCE_EXTENSION_REGEX.test(metadataFile) || !validateSourceExtension) + ) { + metadataName = METADATA_INFO.Document.xmlName; + } else { + let keys = Object.keys(METADATA_INFO); + for (let i = 0; i < keys.length; i++) { + let metaDescribe = METADATA_INFO[keys[i]]; + if ( + metaDescribe.sourceExtension === extension || + ('.' + metaDescribe.suffix === extension && !validateSourceExtension) || + metaDescribe.folderExtension === extension + ) { + metadataName = metaDescribe.xmlName; + break; + } + } + } + return metadataName; + } +} + +export const METADATA_INFO = MetadataInfo.loadMetadataInfo(); +export const UNSPLITED_METADATA = [ + METADATA_INFO.Workflow, + METADATA_INFO.SharingRules, + METADATA_INFO.CustomLabels, + METADATA_INFO.Profile, + METADATA_INFO.PermissionSet, +]; + +export const PROFILE_PERMISSIONSET_EXTENSION = [METADATA_INFO.Profile, METADATA_INFO.PermissionSet]; diff --git a/packages/sfpowerscripts-cli/src/core/metadata/SettingsFetcher.ts b/packages/sfpowerscripts-cli/src/core/metadata/SettingsFetcher.ts new file mode 100644 index 000000000..971717dbe --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/metadata/SettingsFetcher.ts @@ -0,0 +1,22 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import SFPOrg from '../org/SFPOrg'; +const fs = require('fs-extra'); +import { XMLParser } from 'fast-xml-parser'; +import MetadataFetcher from './MetadataFetcher'; + +export default class SettingsFetcher extends MetadataFetcher { + constructor(logger: Logger) { + super(logger); + } + + public async getSetttingMetadata(org: SFPOrg, setting: string) { + SFPLogger.log(`Fetching ${setting}Settings from Org`, LoggerLevel.INFO, this.logger); + let retriveLocation = (await this.fetchPackageFromOrg(org, { + types: { name: 'Settings', members: setting }, + })).unzippedLocation; + let resultFile = `${retriveLocation}/settings/${setting}.settings`; + const parser = new XMLParser(); + let parsedSettings = parser.parse(fs.readFileSync(resultFile).toString())[`${setting}Settings`]; + return parsedSettings; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/org/OrgDetailsFetcher.ts b/packages/sfpowerscripts-cli/src/core/org/OrgDetailsFetcher.ts new file mode 100644 index 000000000..7ded33399 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/org/OrgDetailsFetcher.ts @@ -0,0 +1,120 @@ +import extractDomainFromUrl from '../utils/extractDomainFromUrl'; +import { convertAliasToUsername } from '../utils/AliasList'; +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; +import ScratchOrgInfoFetcher from './ScratchOrgInfoFetcher'; +import OrganizationFetcher from './OrganizationFetcher'; +import { AuthInfo, Connection, Org, trimTo15 } from '@salesforce/core'; + +export default class OrgDetailsFetcher { + private static usernamesToOrgDetails: { [P: string]: OrgDetails } = {}; + + constructor(private username: string) {} + + public async getOrgDetails(): Promise { + //Convert alias to username + this.username = await convertAliasToUsername(this.username); + + if (OrgDetailsFetcher.usernamesToOrgDetails[this.username]) + return OrgDetailsFetcher.usernamesToOrgDetails[this.username]; + + const authInfo = await AuthInfo.create({ username: this.username }); + + let authInfoFields = authInfo.getFields(); + + + let sfdxAuthUrl: string; + try { + sfdxAuthUrl = authInfo.getSfdxAuthUrl(); + } catch (error) { + SFPLogger.log(`Unable to get SFDX Auth URL: ${error.message}`, LoggerLevel.TRACE, null); + } + + const isScratchOrg = authInfoFields.devHubUsername; + let scratchOrgInfo = isScratchOrg + ? await this.getScratchOrgDetails(authInfoFields.orgId, authInfo) + : ({} as ScratchOrgDetails); + + const organization = await this.getOrganization(authInfo); + + OrgDetailsFetcher.usernamesToOrgDetails[this.username] = { + sfdxAuthUrl: sfdxAuthUrl, + instanceUrl: authInfoFields.instanceUrl, + ...authInfoFields, + ...scratchOrgInfo, + ...organization, + }; + + return OrgDetailsFetcher.usernamesToOrgDetails[this.username]; + } + + public async getOrgDomainUrl(): Promise { + await this.getOrgDetails(); + + if (OrgDetailsFetcher.usernamesToOrgDetails[this.username]) { + let domain = extractDomainFromUrl(OrgDetailsFetcher.usernamesToOrgDetails[this.username].instanceUrl); + if (domain) return domain; + else return ''; + } else { + return ''; + } + } + + private async getScratchOrgDetails(orgId: string, authInfo: AuthInfo): Promise { + const hubOrg: Org = await ( + await Org.create({ + connection: await Connection.create({ + authInfo: authInfo, + }), + }) + ).getDevHubOrg(); + + let scratchOrgInfo = ( + await new ScratchOrgInfoFetcher(hubOrg).getScratchOrgInfoByOrgId([trimTo15(orgId)]) + )[0]; + + if (scratchOrgInfo) { + return { + isScratchOrg:true, + status: scratchOrgInfo.Status, + }; + } else { + throw new Error( + `No information for scratch org with ID ${trimTo15( + orgId + )} found in Dev Hub ${hubOrg.getUsername()}` + ); + } + } + + private async getOrganization(authInfo: AuthInfo) { + const connection = await Connection.create({ + authInfo: authInfo, + }); + + const results = await new OrganizationFetcher(connection).fetch(); + + if (results[0]) { + return { + isSandbox: results[0].IsSandbox, + organizationType: results[0].OrganizationType, + }; + } else { + throw new Error(`No Organization records found for ${connection.getUsername()}`); + } + } +} + +export interface OrgDetails extends ScratchOrgDetails, Organization { + sfdxAuthUrl: string; + instanceUrl:string; +} + +export interface ScratchOrgDetails { + isScratchOrg:boolean; + status: string; +} + +export interface Organization { + isSandbox: boolean; + organizationType: string; +} diff --git a/packages/sfpowerscripts-cli/src/core/org/OrganizationFetcher.ts b/packages/sfpowerscripts-cli/src/core/org/OrganizationFetcher.ts new file mode 100644 index 000000000..b453b9851 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/org/OrganizationFetcher.ts @@ -0,0 +1,12 @@ +import { Connection } from '@salesforce/core'; +import QueryHelper from '../queryHelper/QueryHelper'; + +export default class OrganizationFetcher { + constructor(private conn: Connection) {} + + public fetch() { + const query = 'SELECT OrganizationType, IsSandbox FROM Organization LIMIT 1'; + + return QueryHelper.query<{ OrganizationType: string; IsSandbox: boolean }>(query, this.conn, false); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/org/SFPOrg.ts b/packages/sfpowerscripts-cli/src/core/org/SFPOrg.ts new file mode 100644 index 000000000..d1fc38c18 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/org/SFPOrg.ts @@ -0,0 +1,271 @@ +import { Org } from '@salesforce/core'; +import SFPLogger, { COLOR_KEY_MESSAGE, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import Package2Detail from '../package/Package2Detail'; +import SfpPackage from '../package/SfpPackage'; +import QueryHelper from '../queryHelper/QueryHelper'; +import { convertUsernameToAlias } from '../utils/AliasList'; +import ObjectCRUDHelper from '../utils/ObjectCRUDHelper'; +import InstalledPackagesQueryExecutor from './packageQuery/InstalledPackagesQueryExecutor'; + +export default class SFPOrg extends Org { + /** + * Get list of all artifacts in an org + */ + public async getInstalledArtifacts(orderBy: string = `CreatedDate`,logger?:Logger) { + let records=[] + try { + records = await QueryHelper.query( + `SELECT Id, Name, CommitId__c, Version__c, Tag__c FROM sfpArtifact2__c ORDER BY ${orderBy} ASC`, + this.getConnection(), + false + ); + return records; + } catch (error) { + SFPLogger.log( + 'Unable to fetch any sfp artifacts in the org\n' + + '1. sfp package is not installed in the org\n' + + '2. The required prerequisite object is not deployed to this org\n', + LoggerLevel.WARN, + logger + ); + } + return records; + } + /** + * Check whether an artifact is installed in a Org + * @param {Logger} logger + * @param {SfpPackage} sfpPackage + */ + public async isArtifactInstalledInOrg( + logger: Logger, + sfpPackage: SfpPackage + ): Promise<{ isInstalled: boolean; versionNumber?: string }> { + let result: { isInstalled: boolean; versionNumber?: string } = { + isInstalled: false, + }; + try { + SFPLogger.log(`Querying for version of ${sfpPackage.packageName} in the Org.`, LoggerLevel.TRACE, logger); + result.isInstalled = false; + let installedArtifacts = await this.getInstalledArtifacts(); + let packageName = sfpPackage.packageName; + for (const artifact of installedArtifacts) { + if (artifact.Name === packageName) { + result.versionNumber = artifact.Version__c; + if (artifact.Version__c === sfpPackage.package_version_number) { + result.isInstalled = true; + return result; + } + } + } + } catch (error) { + SFPLogger.log( + 'Unable to fetch any sfp artifacts in the org\n' + + '1. sfp package is not installed in the org\n' + + '2. The required prerequisite object is not deployed to this org\n', + LoggerLevel.WARN, + logger + ); + } + return result; + } + /** + * Updates or Create information about an artifact in the org + * @param {Logger} logger + * @param {SfpPackage} sfpPackage + */ + public async updateArtifactInOrg(logger: Logger, sfpPackage: SfpPackage): Promise { + let artifactId = await this.getArtifactRecordId(sfpPackage); + + SFPLogger.log( + COLOR_KEY_MESSAGE( + `Existing artifact record id for ${sfpPackage.packageName} in Org for ${ + sfpPackage.package_version_number + }: ${artifactId ? artifactId : 'N/A'}` + ), + LoggerLevel.INFO, + logger + ); + + let packageName = sfpPackage.package_name; + + if (artifactId == null) { + artifactId = await ObjectCRUDHelper.createRecord( + this.getConnection(), + 'sfpArtifact2__c', + { + Name: packageName, + Tag__c: sfpPackage.tag, + Version__c: sfpPackage.package_version_number, + CommitId__c: sfpPackage.sourceVersion, + } + ); + } else { + artifactId = await ObjectCRUDHelper.updateRecord( + this.getConnection(), + 'sfpArtifact2__c', + { + Id: artifactId, + Name: packageName, + Tag__c: sfpPackage.tag, + Version__c: sfpPackage.package_version_number, + CommitId__c: sfpPackage.sourceVersion, + } + ); + } + + SFPLogger.log( + COLOR_KEY_MESSAGE( + `Updated Org with new Artifact ${packageName} ${sfpPackage.package_version_number} ${ + artifactId ? artifactId : '' + }` + ), + LoggerLevel.INFO, + logger + ); + return artifactId; + } + + private async getArtifactRecordId(sfpPackage: SfpPackage): Promise { + let installedArtifacts = await this.getInstalledArtifacts(); + + let packageName = sfpPackage.packageName; + for (const artifact of installedArtifacts) { + if (artifact.Name === packageName) { + return artifact.Id; + } + } + return null; + } + /** + * Retrieves all packages(recognized by Salesforce) installed in the org + */ + public async getAllInstalled2GPPackages(): Promise { + const installedPackages: Package2Detail[] = []; + + let records = await InstalledPackagesQueryExecutor.exec(this.getConnection()); + + records.forEach((record) => { + let packageVersionNumber = `${record.SubscriberPackageVersion.MajorVersion}.${record.SubscriberPackageVersion.MinorVersion}.${record.SubscriberPackageVersion.PatchVersion}.${record.SubscriberPackageVersion.BuildNumber}`; + + let packageDetails: Package2Detail = { + name: record.SubscriberPackage.Name, + package2Id: record.SubscriberPackageId, + namespacePrefix: record.SubscriberPackage.NamespacePrefix, + subscriberPackageVersionId: record.SubscriberPackageVersion.Id, + versionNumber: packageVersionNumber, + type: record.SubscriberPackageVersion.Package2ContainerOptions, + isOrgDependent: record.SubscriberPackageVersion.IsOrgDependent, + }; + + installedPackages.push(packageDetails); + }); + + return installedPackages; + } + + /** + * Retrives all managed packages in the org + */ + public async getAllInstalledManagedPackages(): Promise { + const installedPackages = await this.getAllInstalled2GPPackages(); + return installedPackages.filter((installedPackage) => installedPackage.type === 'Managed'); + } + /** + * List all the packages created in DevHub, will throw an error, if its not a DevHub + */ + public async listAllPackages() { + if (await this.determineIfDevHubOrg(true)) { + let records = await QueryHelper.query(packageQuery, this.getConnection(), true); + records.forEach((record) => { + record.IsOrgDependent = + record.ContainerOptions === 'Managed' ? 'N/A' : record.IsOrgDependent === true ? 'Yes' : 'No'; + }); + + return records; + } else throw new Error('Package Type Information can only be fetched from a DevHub'); + } + + public async getAlias(): Promise { + return await convertUsernameToAlias(this.getUsername()); + } + + /** + * Return all artifacts including sfp as well as external unlocked/managed + */ + public async getAllInstalledArtifacts():Promise { + let artifacts = await this.getInstalledArtifacts(`Name`); + let installedArtifacts: InstalledArtifact[]=[]; + let installed2GPPackages = await this.getAllInstalled2GPPackages(); + + artifacts.forEach((artifact) => { + let installedArtifact: InstalledArtifact = { + name: artifact.Name, + version: artifact.Version__c, + commitId:artifact.CommitId__c, + isInstalledBysfp: true, + }; + let packageFound = installed2GPPackages.find((elem) => elem.name == artifact.Name); + if (packageFound) { + installedArtifact.subscriberVersion = packageFound.subscriberPackageVersionId; + if (packageFound.isOrgDependent) installedArtifact.type = `OrgDependendent`; + else installedArtifact.type = `Unlocked`; + } else { + installedArtifact.subscriberVersion = `N/A`; + installedArtifact.type = `Source/Data`; + } + installedArtifacts.push(installedArtifact); + }); + + installed2GPPackages.forEach((installed2GPPackage) => { + let packageFound = installedArtifacts.find((elem) => elem.name == installed2GPPackage.name); + if (!packageFound) { + let installedArtifact: InstalledArtifact = { + name: installed2GPPackage.name, + version: installed2GPPackage.versionNumber, + commitId: `N/A`, + }; + if (installed2GPPackage.isOrgDependent) installedArtifact.type = `OrgDependendent`; + else if (installed2GPPackage.type == `Managed`) installedArtifact.type = `Managed`; + else installedArtifact.type = `Unlocked`; + + installedArtifact.subscriberVersion = installed2GPPackage.subscriberPackageVersionId; + installedArtifact.isInstalledBysfp = false; + installedArtifacts.push(installedArtifact); + } + }); + return installedArtifacts; + } +} + +const packageQuery = + 'SELECT Id,Name, Description, NamespacePrefix, ContainerOptions, IsOrgDependent ' + + 'FROM Package2 ' + + 'WHERE IsDeprecated != true ' + + 'ORDER BY NamespacePrefix, Name'; + + +export interface InstalledArtifact { + name: string; + version: string; + commitId?: string; + subscriberVersion?: string; + type?: string; + isInstalledBysfp?: boolean; +} + +export interface sfpArtifact2__c { + Id?: string; + Name: string; + Tag__c: string; + Version__c: string; + CommitId__c: string; +} + +export interface PackageTypeInfo { + Id: string; + Name: string; + Description: string; + NamespacePrefix: string; + ContainerOptions: string; + IsOrgDependent: boolean | string; +} diff --git a/packages/sfpowerscripts-cli/src/core/org/ScratchOrgInfoFetcher.ts b/packages/sfpowerscripts-cli/src/core/org/ScratchOrgInfoFetcher.ts new file mode 100644 index 000000000..c238af473 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/org/ScratchOrgInfoFetcher.ts @@ -0,0 +1,30 @@ +import { Org, trimTo15 } from '@salesforce/core'; +import QueryHelper from '../queryHelper/QueryHelper'; + +export default class ScratchOrgInfoFetcher { + constructor(private hubOrg: Org) {} + + public async getScratchOrgInfoByOrgId(orgId: string[]) { + const conn = this.hubOrg.getConnection(); + + let collection = orgId + .map((id) => { + return `'${trimTo15(id)}'`; + }) + .toString(); + + let query = ` + SELECT Id, ScratchOrg, Status + FROM ScratchOrgInfo + WHERE ScratchOrg IN (${collection}) + `; + + return QueryHelper.query(query, conn, false); + } +} + +export interface ScratchOrgInfo { + Id: string; + ScratchOrg: string; + Status: 'New' | 'Deleted' | 'Active' | 'Error'; +} diff --git a/packages/sfpowerscripts-cli/src/core/org/packageQuery/InstalledPackagesQueryExecutor.ts b/packages/sfpowerscripts-cli/src/core/org/packageQuery/InstalledPackagesQueryExecutor.ts new file mode 100644 index 000000000..2d124718c --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/org/packageQuery/InstalledPackagesQueryExecutor.ts @@ -0,0 +1,14 @@ +import { Connection } from '@salesforce/core'; +import QueryHelper from '../../queryHelper/QueryHelper'; + +export default class InstalledPackagesQueryExecutor { + static async exec(conn: Connection) { + const installedPackagesQuery = + 'SELECT Id, SubscriberPackageId, SubscriberPackage.NamespacePrefix, SubscriberPackage.Name, ' + + 'SubscriberPackageVersion.Id, SubscriberPackageVersion.Name, SubscriberPackageVersion.MajorVersion, SubscriberPackageVersion.MinorVersion, ' + + 'SubscriberPackageVersion.PatchVersion, SubscriberPackageVersion.BuildNumber, SubscriberPackageVersion.Package2ContainerOptions, SubscriberPackageVersion.IsOrgDependent FROM InstalledSubscriberPackage ' + + 'ORDER BY SubscriberPackageId'; + + return QueryHelper.query(installedPackagesQuery, conn, true); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/Package2Detail.ts b/packages/sfpowerscripts-cli/src/core/package/Package2Detail.ts new file mode 100644 index 000000000..55c55deed --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/Package2Detail.ts @@ -0,0 +1,10 @@ +export default interface Package2Detail { + name: string; + package2Id?: string; + namespacePrefix?: string; + subscriberPackageVersionId?: string; + versionNumber?: string; + type?: string; + isOrgDependent?: boolean; + key?: string; +} diff --git a/packages/sfpowerscripts-cli/src/core/package/SfpPackage.ts b/packages/sfpowerscripts-cli/src/core/package/SfpPackage.ts new file mode 100644 index 000000000..9c4833062 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/SfpPackage.ts @@ -0,0 +1,143 @@ +import _ from 'lodash'; +import { ApexSortedByType } from '../apex/parser/ApexTypeFetcher'; + +export type ApexClasses = Array; + +class PackageInfo { + id?: string; + package_name: string; + package_version_number?: string; + package_version_id?: string; + package_type?: string; + test_coverage?: number; + has_passed_coverage_check?: boolean; + repository_url?: string; + sourceVersion?: string; + branch?: string; + apextestsuite?: string; + isApexFound?: boolean; + assignPermSetsPreDeployment?: string[]; + assignPermSetsPostDeployment?: string[]; + apexTestClassses?: string[]; + isPickListsFound?: boolean; + isTriggerAllTests?: boolean; + isProfilesFound?: boolean; + isPermissionSetGroupFound?: boolean; + isPromoted?: boolean; + tag?: string; + isDependencyValidated?: boolean; + destructiveChanges?: any; + destructiveChangesPath?: string; + payload?: any; + metadataCount?: number; + sourceDir?: string; + dependencies?: any; + reconcileProfiles?: boolean; + isPayLoadContainTypesSupportedByProfiles?: boolean; + creation_details?: { creation_time?: number; timestamp?: number }; + deployments?: { target_org: string; sub_directory?: string; installation_time?: number; timestamp?: number }[]; + apiVersion?: string; + postDeploymentScript?: string; + preDeploymentScript?: string; + apexClassWithOutTestClasses?: ApexClasses; + triggers?: ApexClasses; + configFilePath?: string; + packageDescriptor?: any; + commitSHAFrom?:string; + commitSHATo?:string; + packageDirectory?: string; + apexClassesSortedByTypes?: ApexSortedByType; + projectConfig?: any; + changelogFilePath?: string; +} + +export default class SfpPackage extends PackageInfo { + public projectDirectory: string; + public workingDirectory: string; + public mdapiDir: string; + public destructiveChangesPath: string; + public resolvedPackageDirectory: string; + + public version: string = '5'; + + //Just a few helpers to resolve api differene + public get packageName(): string { + return this.package_name; + } + + public get versionNumber(): string { + return this.package_version_number; + } + + public set versionNumber(versionNumber:string) + { + this.package_version_number = versionNumber; + } + + public get packageType(): string { + return this.package_type.toLocaleLowerCase(); + } + + public set packageType(packageType: string) { + this.package_type = packageType; + } + /** + * Do not use this constructor directly, use SfPPackageBuilder + * to build a package + * + */ + public constructor() { + super(); + } + + toJSON(): PackageInfo { + let castToPackageMetadata = _.cloneDeep(this); + delete castToPackageMetadata.workingDirectory; + delete castToPackageMetadata.mdapiDir; + delete castToPackageMetadata.projectConfig; + delete castToPackageMetadata.packageDescriptor; + delete castToPackageMetadata.projectDirectory; + delete castToPackageMetadata.resolvedPackageDirectory; + delete castToPackageMetadata.isTriggerAllTests; + return castToPackageMetadata; + } +} + + +export enum PackageType { + Unlocked = "unlocked", + Source = "source", + Data = "data", + Diff = "diff" +} + +export interface DiffPackageMetadata { + + + sourceVersionFrom?: string; + sourceVersionTo?: string; + isProfilesFound?: boolean; + apexTestClassses?: string[]; + isApexFound?: boolean; + isPicklistFound?: boolean; + isPermissionSetGroupFound?: boolean; + isPermissionSetFound?: boolean; + payload?: any; + metadataCount?: number; + profilesToReconcile?: number; + destructiveChanges?: any; + sourceDir?: string; + invalidatedTestClasses?: ApexClasses; + isPayLoadContainTypesSupportedByProfiles?:boolean; +} +export interface SfpPackageParams { + overridePackageTypeWith?: string; + branch?: string; + packageVersionNumber?: string; + repositoryUrl?: string; + sourceVersion?: string; + configFilePath?: string; + pathToReplacementForceIgnore?: string; + revisionFrom?: string; + revisionTo?: string; +} diff --git a/packages/sfpowerscripts-cli/src/core/package/SfpPackageBuilder.ts b/packages/sfpowerscripts-cli/src/core/package/SfpPackageBuilder.ts new file mode 100644 index 000000000..e394099a1 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/SfpPackageBuilder.ts @@ -0,0 +1,278 @@ +import ApexTypeFetcher, { ApexSortedByType } from '../apex/parser/ApexTypeFetcher'; +import ProjectConfig from '../project/ProjectConfig'; +import SfpPackageContentGenerator from './generators/SfpPackageContentGenerator'; +import SourceToMDAPIConvertor from './packageFormatConvertors/SourceToMDAPIConvertor'; +import PackageManifest from './components/PackageManifest'; +import MetadataCount from './components/MetadataCount'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import * as fs from 'fs-extra'; +import path from 'path'; +import { Artifact } from '../artifacts/ArtifactFetcher'; +import SfpPackage, { DiffPackageMetadata, PackageType, SfpPackageParams } from './SfpPackage'; +import PropertyFetcher from './propertyFetchers/PropertyFetcher'; +import AssignPermissionSetFetcher from './propertyFetchers/AssignPermissionSetFetcher'; +import DestructiveManifestPathFetcher from './propertyFetchers/DestructiveManifestPathFetcher'; +import ReconcilePropertyFetcher from './propertyFetchers/ReconcileProfilePropertyFetcher'; +import CreateUnlockedPackageImpl from './packageCreators/CreateUnlockedPackageImpl'; +import CreateSourcePackageImpl from './packageCreators/CreateSourcePackageImpl'; +import CreateDataPackageImpl from './packageCreators/CreateDataPackageImpl'; +import lodash = require('lodash'); +import { EOL } from 'os'; +import PackageVersionUpdater from './version/PackageVersionUpdater'; +import { AnalyzerRegistry } from './analyser/AnalyzerRegistry'; +import { ComponentSet } from '@salesforce/source-deploy-retrieve'; +import CreateDiffPackageImp from './packageCreators/CreateDiffPackageImpl'; +import { COLOR_WARNING } from '@flxblio/sfp-logger'; + +export default class SfpPackageBuilder { + public static async buildPackageFromProjectDirectory( + logger: Logger, + projectDirectory: string, + sfdx_package: string, + params?: SfpPackageParams, + packageCreationParams?: PackageCreationParams, + projectConfig?: any + ) { + if (!projectConfig) { + projectConfig = ProjectConfig.getSFDXProjectConfig(projectDirectory); + } else { + // Clone the projectConfig to prevent mutation + projectConfig = lodash.cloneDeep(projectConfig); + } + + let propertyFetchers: PropertyFetcher[] = [ + new AssignPermissionSetFetcher(), + new DestructiveManifestPathFetcher(), + new ReconcilePropertyFetcher(), + ]; + + let startTime = Date.now; + let sfpPackage: SfpPackage = new SfpPackage(); + sfpPackage.package_name = sfdx_package; + sfpPackage.projectConfig = projectConfig; + sfpPackage.apiVersion = sfpPackage.projectConfig.sourceApiVersion; + sfpPackage.packageDescriptor = ProjectConfig.getPackageDescriptorFromConfig( + sfdx_package, + sfpPackage.projectConfig + ); + sfpPackage.projectDirectory = projectDirectory?projectDirectory:''; + sfpPackage.packageDirectory = sfpPackage.packageDescriptor.path; + //Set Default Version Number + sfpPackage.versionNumber = sfpPackage.packageDescriptor.versionNumber; + + //set additional options + sfpPackage.sourceVersion = params?.sourceVersion; + sfpPackage.branch = params?.branch; + sfpPackage.repository_url = params?.repositoryUrl; + if (params?.configFilePath == null) sfpPackage.configFilePath = 'config/project-scratch-def.json'; + else sfpPackage.configFilePath = params?.configFilePath; + + for (const propertyFetcher of propertyFetchers) { + await propertyFetcher.getsfpProperties(sfpPackage, logger); + } + + //Get Package Type + sfpPackage.package_type = ProjectConfig.getPackageType(projectConfig, sfdx_package); + + sfpPackage = SfpPackageBuilder.handleVersionNumber(params, sfpPackage, packageCreationParams); + + // Requires destructiveChangesPath which is set by the property fetcher + sfpPackage.workingDirectory = await SfpPackageContentGenerator.generateSfpPackageDirectory( + logger, + sfpPackage.projectDirectory, + sfpPackage.projectConfig, + sfpPackage.packageName, + sfpPackage.packageDescriptor.path, + sfpPackage.versionNumber, + sfpPackage.destructiveChangesPath, + sfpPackage.configFilePath, + params?.pathToReplacementForceIgnore + ); + + sfpPackage.resolvedPackageDirectory = path.join(sfpPackage.workingDirectory, sfpPackage.packageDescriptor.path); + + //Don't proceed further if packageType is Data + if (sfpPackage.package_type != PackageType.Data) { + let sourceToMdapiConvertor = new SourceToMDAPIConvertor( + sfpPackage.workingDirectory, + sfpPackage.packageDescriptor.path, + ProjectConfig.getSFDXProjectConfig(sfpPackage.workingDirectory).sourceApiVersion, + logger + ); + sfpPackage.mdapiDir = (await sourceToMdapiConvertor.convert()).packagePath; + const packageManifest: PackageManifest = await PackageManifest.create(sfpPackage.mdapiDir); + + sfpPackage.payload = packageManifest.manifestJson; + sfpPackage.triggers = packageManifest.fetchTriggers(); + sfpPackage.isApexFound = packageManifest.isApexInPackage(); + sfpPackage.isProfilesFound = packageManifest.isProfilesInPackage(); + sfpPackage.isPermissionSetGroupFound = packageManifest.isPermissionSetGroupsFoundInPackage(); + sfpPackage.isPayLoadContainTypesSupportedByProfiles = packageManifest.isPayLoadContainTypesSupportedByProfiles(); + + let apexFetcher: ApexTypeFetcher = new ApexTypeFetcher(sfpPackage.mdapiDir); + sfpPackage.apexClassesSortedByTypes = apexFetcher.getClassesClassifiedByType(); + sfpPackage.apexTestClassses = apexFetcher.getTestClasses(); + sfpPackage.metadataCount = await MetadataCount.getMetadataCount( + sfpPackage.workingDirectory, + sfpPackage.packageDescriptor.path + ); + sfpPackage.apexClassWithOutTestClasses = apexFetcher.getClassesOnlyExcludingTestsAndInterfaces(); + + sfpPackage.isTriggerAllTests = this.isAllTestsToBeTriggered(sfpPackage, logger); + + //Load component Set + let componentSet = ComponentSet.fromSource( + path.resolve(sfpPackage.workingDirectory, sfpPackage.projectDirectory, sfpPackage.packageDirectory) + ); + + //Run through all analyzers + let analyzers = AnalyzerRegistry.getAnalyzers(); + for (const analyzer of analyzers) { + if (analyzer.isEnabled(sfpPackage, logger)) sfpPackage = await analyzer.analyze(sfpPackage,componentSet, logger); + } + } + + //Create the actual package + let createPackage; + + if (!packageCreationParams) packageCreationParams = { breakBuildIfEmpty: true }; + + let packageType = sfpPackage.package_type; + if (params?.overridePackageTypeWith) packageType = params?.overridePackageTypeWith.toLocaleLowerCase(); + + //Get Implementors + switch (packageType) { + case PackageType.Unlocked: + createPackage = new CreateUnlockedPackageImpl( + sfpPackage.workingDirectory, + sfpPackage, + packageCreationParams, + logger, + params + ); + break; + case PackageType.Source: + createPackage = new CreateSourcePackageImpl( + sfpPackage.workingDirectory, + sfpPackage, + packageCreationParams, + logger, + params + ); + break; + case PackageType.Data: + createPackage = new CreateDataPackageImpl( + sfpPackage.workingDirectory, + sfpPackage, + packageCreationParams, + logger, + params + ); + break; + case PackageType.Diff: + packageCreationParams.revisionFrom = params.revisionFrom; + packageCreationParams.revisionTo = params.revisionTo; + createPackage = new CreateDiffPackageImp( + sfpPackage.workingDirectory, + sfpPackage, + packageCreationParams, + logger, + params + ); + break; + } + + return createPackage.exec(); + } + + /* + * Handle version Numbers of package + * If VersionNumber is explcitly passed, use that + * else allow autosubstitute using buildNumber for Source and Data if available + */ + private static handleVersionNumber( + params: SfpPackageParams, + sfpPackage: SfpPackage, + packageCreationParams: PackageCreationParams + ) { + if (params?.packageVersionNumber) { + sfpPackage.versionNumber = params.packageVersionNumber; + } else if (packageCreationParams?.buildNumber) { + if (sfpPackage.packageType != PackageType.Unlocked) { + let versionUpdater: PackageVersionUpdater = new PackageVersionUpdater(); + sfpPackage.versionNumber = versionUpdater.substituteBuildNumber( + sfpPackage, + packageCreationParams.buildNumber + ); + } + } else { + sfpPackage.versionNumber = sfpPackage.packageDescriptor.versionNumber; + } + return sfpPackage; + } + + public static async buildPackageFromArtifact(artifact: Artifact, logger: Logger): Promise { + //Read artifact metadata + let sfpPackage = new SfpPackage(); + Object.assign(sfpPackage, fs.readJSONSync(artifact.packageMetadataFilePath, { encoding: 'utf8' })); + sfpPackage.sourceDir = artifact.sourceDirectoryPath; + sfpPackage.changelogFilePath = artifact.changelogFilePath; + + sfpPackage.projectConfig = ProjectConfig.getSFDXProjectConfig(artifact.sourceDirectoryPath); + sfpPackage.packageDescriptor = ProjectConfig.getSFDXPackageDescriptor( + artifact.sourceDirectoryPath, + sfpPackage.package_name + ); + sfpPackage.projectDirectory = artifact.sourceDirectoryPath; + sfpPackage.packageDirectory = sfpPackage.packageDescriptor.path; + sfpPackage.isTriggerAllTests = this.isAllTestsToBeTriggered(sfpPackage, logger); + + return sfpPackage; + } + + + + private static isAllTestsToBeTriggered(sfpPackage: SfpPackage, logger: Logger) { + if ( + this.isOptimizedDeploymentForSourcePackage(sfpPackage) == false || + (sfpPackage.packageType == PackageType.Source && + sfpPackage.isApexFound == true && + sfpPackage.apexTestClassses == null) + ) { + SFPLogger.printHeaderLine('WARNING! NON OPTIMAL DEPLOYMENT',COLOR_WARNING,LoggerLevel.INFO,logger); + SFPLogger.log( + `This package has apex classes/triggers, In order to deploy optimally, each class need to have a minimum` + + `75% test coverage,We are unable to find any test classes in the given package, hence will be deploying` + + `via triggering all local tests,This definitely is not optimal approach on large orgs` + + `Please consider adding test classes for the classes in the package`, + LoggerLevel.INFO, + logger + ); + SFPLogger.printHeaderLine('',COLOR_WARNING,LoggerLevel.INFO,logger); + return true; + } else return false; + } + + // Allow individual packages to use non optimized path + private static isOptimizedDeploymentForSourcePackage(pkgDescriptor: any): boolean { + if (pkgDescriptor['isOptimizedDeployment'] == null) return true; + else return pkgDescriptor['isOptimizedDeployment']; + } +} + +// Options while creating package +export class PackageCreationParams { + breakBuildIfEmpty: boolean = true; + devHub?: string; + installationkeybypass?: boolean; + installationkey?: string; + waitTime?: string; + isCoverageEnabled?: boolean; + isSkipValidation?: boolean; + isComputeDiffPackage?: boolean; + baseBranch?: string; + buildNumber?: string; + useSelectiveBuildOnly?: boolean; + revisionFrom?:string; + revisionTo?:string; +} diff --git a/packages/sfpowerscripts-cli/src/core/package/SfpPackageInquirer.ts b/packages/sfpowerscripts-cli/src/core/package/SfpPackageInquirer.ts new file mode 100644 index 000000000..ba0e6a4d3 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/SfpPackageInquirer.ts @@ -0,0 +1,178 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import * as fs from 'fs-extra'; +import path = require('path'); +import lodash = require('lodash'); +import { URL } from 'url'; +import SfpPackage from './SfpPackage'; + +/** + * Methods for getting information about artifacts + */ +export default class SfpPackageInquirer { + private _latestPackageManifestFromArtifacts: any; + private _pathToLatestPackageManifestFromArtifacts: string; + private _prunedLatestPackageManifestFromArtifacts: any; + + get pathToLatestPackageManifestFromArtifacts() { + return this._pathToLatestPackageManifestFromArtifacts; + } + get prunedLatestPackageManifestFromArtifacts() { + return this._prunedLatestPackageManifestFromArtifacts; + } + + constructor(private readonly sfpPackages: SfpPackage[], private packageLogger?: Logger) {} + + public getLatestProjectConfig() { + let latestPackageManifest = this.getLatestPackageManifestFromArtifacts(this.sfpPackages); + + if (latestPackageManifest) { + this._latestPackageManifestFromArtifacts = latestPackageManifest.latestPackageManifest; + this._pathToLatestPackageManifestFromArtifacts = latestPackageManifest.pathToLatestPackageManifest; + + this._prunedLatestPackageManifestFromArtifacts = this.pruneLatestPackageManifest( + latestPackageManifest.latestPackageManifest, + this.sfpPackages + ); + } + return this._latestPackageManifestFromArtifacts; + } + + /** + * Gets latest package manifest from artifacts + * Returns null if unable to find latest package manifest + */ + private getLatestPackageManifestFromArtifacts( + sfpPackages: SfpPackage[] + ): { + latestPackageManifest: any; + pathToLatestPackageManifest: string; + } { + let latestPackageManifest: any; + let pathToLatestPackageManifest: string; + + this.validateArtifactsSourceRepository(); + + let latestSfpPackage: SfpPackage; + for (let sfpPackage of sfpPackages) { + if ( + latestSfpPackage == null || + latestSfpPackage.creation_details.timestamp < sfpPackage.creation_details.timestamp + ) { + latestSfpPackage = sfpPackage; + + let pathToPackageManifest = path.join(sfpPackage.sourceDir, 'manifests', 'sfdx-project.json.ori'); + if (fs.existsSync(pathToPackageManifest)) { + latestPackageManifest = JSON.parse(fs.readFileSync(pathToPackageManifest, 'utf8')); + + pathToLatestPackageManifest = pathToPackageManifest; + } + } + } + + if (latestPackageManifest) { + SFPLogger.log( + `Found latest package manifest in ${latestSfpPackage.packageName} artifact`, + LoggerLevel.INFO, + this.packageLogger + ); + + return { latestPackageManifest, pathToLatestPackageManifest }; + } else return null; + } + + /** + * Verify that artifacts are from the same source repository + */ + public validateArtifactsSourceRepository(): void { + let remoteURL: RemoteURL; + + for (let sfpPackage of this.sfpPackages) { + let currentRemoteURL: RemoteURL; + + let isHttp: boolean = sfpPackage.repository_url.match(/^https?:\/\//) ? true : false; + if (isHttp) { + const url = new URL(sfpPackage.repository_url); + currentRemoteURL = { + ref: url.toString(), + hostName: url.hostname, + pathName: url.pathname, + }; + } else { + // Handle SSH URL separately, as it is not supported by URL module + currentRemoteURL = { + ref: sfpPackage.repository_url, + hostName: null, + pathName: null, + }; + } + + if (remoteURL == null) { + remoteURL = currentRemoteURL; + continue; + } + + let isValid: boolean; + if (isHttp) { + if ( + currentRemoteURL.hostName === remoteURL.hostName && + currentRemoteURL.pathName === remoteURL.pathName + ) + isValid = true; + else isValid = false; + } else { + if (currentRemoteURL.ref === remoteURL.ref) isValid = true; + else isValid = false; + } + + if (!isValid) { + SFPLogger.log(`remoteURL: ${JSON.stringify(remoteURL)}`, LoggerLevel.DEBUG, this.packageLogger); + SFPLogger.log( + `currentRemoteURL: ${JSON.stringify(currentRemoteURL)}`, + LoggerLevel.DEBUG, + this.packageLogger + ); + throw new Error( + `Artifacts must originate from the same source repository, for deployment to work. The artifact ${sfpPackage.packageName} has repository URL that doesn't meet the current repository URL ${JSON.stringify(currentRemoteURL)} not equal ${JSON.stringify(remoteURL)}` + ); + } + } + } + + /** + * Remove packages that do not have an artifact from the package manifest + * @param latestPackageManifest + * @param artifacts + */ + private pruneLatestPackageManifest(latestPackageManifest: any, sfpPackages: SfpPackage[]) { + let prunedLatestPackageManifest = lodash.cloneDeep(latestPackageManifest); + + let packagesWithArtifacts: string[] = []; + sfpPackages.forEach((sfpPackage) => { + packagesWithArtifacts.push(sfpPackage.packageName); + }); + + let i = prunedLatestPackageManifest.packageDirectories.length; + while (i--) { + if (!packagesWithArtifacts.includes(prunedLatestPackageManifest.packageDirectories[i].package)) { + let removedPackageDirectory = prunedLatestPackageManifest.packageDirectories.splice(i, 1); + + // Also remove references to the package as a dependency + prunedLatestPackageManifest.packageDirectories.forEach((pkg) => { + let indexOfDependency = pkg.dependencies?.findIndex( + (dependency) => dependency.package === removedPackageDirectory[0].package + ); + + if (indexOfDependency >= 0) pkg.dependencies.splice(indexOfDependency, 1); + }); + } + } + + return prunedLatestPackageManifest; + } +} + +interface RemoteURL { + ref: string; + hostName: string; + pathName: string; +} diff --git a/packages/sfpowerscripts-cli/src/core/package/SfpPackageInstaller.ts b/packages/sfpowerscripts-cli/src/core/package/SfpPackageInstaller.ts new file mode 100644 index 000000000..711db5442 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/SfpPackageInstaller.ts @@ -0,0 +1,63 @@ +import path from 'path'; +import { Logger } from '@flxblio/sfp-logger'; +import SFPOrg from '../org/SFPOrg'; +import InstallDataPackageImpl from './packageInstallers/InstallDataPackageImpl'; +import { SfpPackageInstallationOptions } from './packageInstallers/InstallPackage'; +import InstallSourcePackageImpl from './packageInstallers/InstallSourcePackageImpl'; +import InstallUnlockedPackage from './packageInstallers/InstallUnlockedPackage'; +import { PackageInstallationResult } from './packageInstallers/PackageInstallationResult'; +import SfpPackage, { PackageType } from './SfpPackage'; + +export default class SfpPackageInstaller { + public static async installPackage( + logger: Logger, + sfpPackage: SfpPackage, + targetOrg: SFPOrg, + installationOptions: SfpPackageInstallationOptions, + installationContext?: SfPPackageInstallationContext, + overridePackageTypeWith?: string + ): Promise { + let packageType = sfpPackage.packageType; + if (overridePackageTypeWith) packageType = overridePackageTypeWith; + + switch (packageType) { + case PackageType.Unlocked: + let installUnlockedPackageImpl: InstallUnlockedPackage = new InstallUnlockedPackage( + sfpPackage, + targetOrg, + installationOptions, + logger + ); + installUnlockedPackageImpl.isArtifactToBeCommittedInOrg = !installationOptions.disableArtifactCommit; + return installUnlockedPackageImpl.exec(); + case PackageType.Diff: + case PackageType.Source: + installationOptions.pathToReplacementForceIgnore = installationContext?.currentStage == 'prepare' + ? path.join(sfpPackage.sourceDir, 'forceignores', '.prepareignore') + : null; + let installSourcePackageImpl: InstallSourcePackageImpl = new InstallSourcePackageImpl( + sfpPackage, + targetOrg, + installationOptions, + logger + ); + installSourcePackageImpl.isArtifactToBeCommittedInOrg = !installationOptions.disableArtifactCommit; + return installSourcePackageImpl.exec(); + case PackageType.Data: + let installDataPackageImpl: InstallDataPackageImpl = new InstallDataPackageImpl( + sfpPackage, + targetOrg, + logger, + installationOptions + ); + installDataPackageImpl.isArtifactToBeCommittedInOrg = !installationOptions.disableArtifactCommit; + return installDataPackageImpl.exec(); + default: + throw new Error('Unknown Package Type'); + } + } +} + +export class SfPPackageInstallationContext { + currentStage: string; +} diff --git a/packages/sfpowerscripts-cli/src/core/package/analyser/AnalyzerRegistry.ts b/packages/sfpowerscripts-cli/src/core/package/analyser/AnalyzerRegistry.ts new file mode 100644 index 000000000..0e4abe323 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/analyser/AnalyzerRegistry.ts @@ -0,0 +1,20 @@ +import FHTAnalyser from './FHTAnalyzer'; +import FTAnalyser from './FTAnalyzer'; +import { PackageAnalyzer } from './PackageAnalyzer'; +import PicklistAnalyzer from './PicklistAnalyzer'; + +export class AnalyzerRegistry { + static getAnalyzers(): PackageAnalyzer[] { + let packageAnalyzers: PackageAnalyzer[] = []; + + //TODO: Make dynamic + let fhtAnalyzer = new FHTAnalyser(); + let ftAnalyser = new FTAnalyser(); + let picklistAnalyzer = new PicklistAnalyzer(); + packageAnalyzers.push(fhtAnalyzer); + packageAnalyzers.push(ftAnalyser); + packageAnalyzers.push(picklistAnalyzer); + + return packageAnalyzers; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/analyser/FHTAnalyzer.ts b/packages/sfpowerscripts-cli/src/core/package/analyser/FHTAnalyzer.ts new file mode 100644 index 000000000..3d71504e6 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/analyser/FHTAnalyzer.ts @@ -0,0 +1,76 @@ +import path from 'path'; +import * as fs from 'fs-extra'; +import * as yaml from 'js-yaml'; +import { ComponentSet, registry } from '@salesforce/source-deploy-retrieve'; +import SfpPackage, { PackageType } from '../SfpPackage'; +import { PackageAnalyzer } from './PackageAnalyzer'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; + +export default class FHTAnalyser implements PackageAnalyzer { + + public getName() { + return "Field History Tracking Analyzer" + } + + + + public async analyze(sfpPackage: SfpPackage, componentSet:ComponentSet, logger:Logger): Promise { + try { + + let fhtFields: { [key: string]: Array } = {}; + + //read the yaml + let fhtYamlPath = path.join( + sfpPackage.workingDirectory, + sfpPackage.projectDirectory, + sfpPackage.packageDirectory, + '/postDeploy/history-tracking.yml' + ); + + //read components mentioned in yaml + if (fs.existsSync(fhtYamlPath)) { + //convert yaml to json + fhtFields = yaml.load(fs.readFileSync(fhtYamlPath, { encoding: 'utf-8' })) as {[key: string]: string[]}; + } + + + //filter the components in the package + fhtFields = await this.addFieldsFromComponentSet(fhtFields, componentSet); + + if (Object.keys(fhtFields).length>0) { + sfpPackage['isFHTFieldFound'] = true; + sfpPackage['fhtFields'] = fhtFields; + } + } catch (error) { + //Ignore error for now + SFPLogger.log(`Unable to process Field History Tracking due to ${error.message}`,LoggerLevel.TRACE,logger); + } + return sfpPackage; + } + + private async addFieldsFromComponentSet( + fhtFields: { [key: string]: Array }, + componentSet: ComponentSet + ): Promise>> { + let sourceComponents = componentSet.getSourceComponents().toArray(); + + for (const sourceComponent of sourceComponents) { + if (sourceComponent.type.name !== registry.types.customobject.children.types.customfield.name) { + continue; + } + + let customField = sourceComponent.parseXmlSync().CustomField; + if (customField['trackHistory'] == 'true') { + let objName = sourceComponent.parent.fullName; + if (!fhtFields[objName]) fhtFields[objName] = []; + fhtFields[objName].push(sourceComponent.name); + } + } + return fhtFields; + } + + public async isEnabled(sfpPackage: SfpPackage,logger:Logger): Promise { + if (sfpPackage.packageType != PackageType.Data) return true; + else return false; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/analyser/FTAnalyzer.ts b/packages/sfpowerscripts-cli/src/core/package/analyser/FTAnalyzer.ts new file mode 100644 index 000000000..911e1e356 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/analyser/FTAnalyzer.ts @@ -0,0 +1,74 @@ +import path from 'path'; +import * as fs from 'fs-extra'; +import * as yaml from 'js-yaml'; +import { ComponentSet, registry } from '@salesforce/source-deploy-retrieve'; +import SfpPackage, { PackageType } from '../SfpPackage'; +import { PackageAnalyzer } from './PackageAnalyzer'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; + +export default class FTAnalyser implements PackageAnalyzer { + + public getName(): string { + return "Feed Tracking Analyzer"; + }; + + public async analyze(sfpPackage: SfpPackage, componentSet:ComponentSet, logger:Logger): Promise { + try { + + let ftFields: { [key: string]: Array } = {}; + + //read the yaml + let ftYamlPath = path.join( + sfpPackage.workingDirectory, + sfpPackage.projectDirectory, + sfpPackage.packageDirectory, + '/postDeploy/feed-tracking.yml' + ); + + //read components mentioned in yaml + if (fs.existsSync(ftYamlPath)) { + //convert yaml to json + ftFields = yaml.load(fs.readFileSync(ftYamlPath, { encoding: 'utf-8' })) as {[key: string]: string[]}; + } + + + //filter the components in the package + ftFields = await this.addFieldsFromComponentSet(ftFields, componentSet); + + if (Object.keys(ftFields).length>0) { + sfpPackage['isFTFieldFound'] = true; + sfpPackage['ftFields'] = ftFields; + } + } catch (error) { + //Ignore error for now + SFPLogger.log(`Unable to process Feed Tracking due to ${error.message}`,LoggerLevel.TRACE,logger); + } + return sfpPackage; + } + + private async addFieldsFromComponentSet( + ftFields: { [key: string]: Array }, + componentSet: ComponentSet + ): Promise>> { + let sourceComponents = componentSet.getSourceComponents().toArray(); + + for (const sourceComponent of sourceComponents) { + if (sourceComponent.type.name !== registry.types.customobject.children.types.customfield.name) { + continue; + } + + let customField = sourceComponent.parseXmlSync().CustomField; + if (customField['trackFeedHistory'] == 'true') { + let objName = sourceComponent.parent.fullName; + if (!ftFields[objName]) ftFields[objName] = []; + ftFields[objName].push(sourceComponent.name); + } + } + return ftFields; + } + + public async isEnabled(sfpPackage: SfpPackage,logger:Logger): Promise { + if (sfpPackage.packageType != PackageType.Data) return true; + else return false; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/analyser/PackageAnalyzer.ts b/packages/sfpowerscripts-cli/src/core/package/analyser/PackageAnalyzer.ts new file mode 100644 index 000000000..3072da027 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/analyser/PackageAnalyzer.ts @@ -0,0 +1,12 @@ +import { Logger } from "@flxblio/sfp-logger"; +import { ComponentSet } from "@salesforce/source-deploy-retrieve"; +import SfpPackage from "../SfpPackage"; + +export interface PackageAnalyzer +{ + getName(); + analyze(sfpPackage: SfpPackage,componentSet:ComponentSet,logger:Logger): Promise + isEnabled(sfpPackage: SfpPackage,logger:Logger): Promise + + +} \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/src/core/package/analyser/PicklistAnalyzer.ts b/packages/sfpowerscripts-cli/src/core/package/analyser/PicklistAnalyzer.ts new file mode 100644 index 000000000..35f24476c --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/analyser/PicklistAnalyzer.ts @@ -0,0 +1,52 @@ +import { ComponentSet, registry } from '@salesforce/source-deploy-retrieve'; +import SfpPackage, { PackageType } from '../SfpPackage'; +import { PackageAnalyzer } from './PackageAnalyzer'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; + +export default class PicklistAnalyzer implements PackageAnalyzer { + + public getName() { + return "Picklist Analyzer" + } + + + + public async analyze(sfpPackage: SfpPackage, componentSet:ComponentSet, logger:Logger): Promise { + try { + let sourceComponents = componentSet.getSourceComponents().toArray(); + let components = []; + + for (const sourceComponent of sourceComponents) { + if (sourceComponent.type.name == registry.types.customobject.name) { + //issues/1367 + //this can add child elements that are not custom fields.. + components.push(...sourceComponent.getChildren()); + } + + if (sourceComponent.type.name == registry.types.customobject.children.types.customfield.name) { + components.push(sourceComponent); + } + } + + if (components) { + for (const fieldComponent of components) { + let customField = fieldComponent.parseXmlSync().CustomField; + //issues/1367 + //if the component isn't a field customField will be undefined..so check + if (customField && customField['type'] == 'Picklist') { + sfpPackage.isPickListsFound= true; + break; + } + } + } + } catch (error) { + SFPLogger.log(`Unable to process Picklist update due to ${error.message}`,LoggerLevel.TRACE,logger); + } + return sfpPackage; + } + + public async isEnabled(sfpPackage: SfpPackage,logger:Logger): Promise { + if (sfpPackage.packageType != PackageType.Data) return true; + else return false; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/components/DeployDestructiveManifestToOrgImpl.ts b/packages/sfpowerscripts-cli/src/core/package/components/DeployDestructiveManifestToOrgImpl.ts new file mode 100644 index 000000000..5edac57a7 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/components/DeployDestructiveManifestToOrgImpl.ts @@ -0,0 +1,129 @@ +import SFPLogger from '@flxblio/sfp-logger'; +import { Connection } from '@salesforce/core'; +import * as fs from 'fs-extra'; +import { delay } from '../../utils/Delay'; +import { LoggerLevel } from '@flxblio/sfp-logger'; +import SFPOrg from '../../org/SFPOrg'; +import AdmZip from "adm-zip" +import path from 'path'; +import tmp from "tmp"; +import { XMLParser } from 'fast-xml-parser'; +import { isEmpty } from 'lodash'; + +export default class DeployDestructiveManifestToOrgImpl { + public constructor(private sfpOrg: SFPOrg, private destructiveManifestPath: string) { } + + + + + public async exec(): Promise { + //Connect to the org + const conn = this.sfpOrg.getConnection(); + const apiversion = await conn.retrieveMaxApiVersion(); + let workingDirectory = this.generateCacheDirectory(); + await this.copyAndValidateDestructiveManifest(this.destructiveManifestPath, workingDirectory); + this.generateEmptyPackageXml(workingDirectory, apiversion); + let zipFile = await this.generateDeploymentZipFile(workingDirectory); + await this.deployDestructiveManifest(zipFile, conn); + } + + private generateCacheDirectory() { + + let tmpDirObj = tmp.dirSync({ unsafeCleanup: true }); + let tempDir = tmpDirObj.name; + let destructCacheDirectory = path.join(tempDir, 'destruct'); + fs.mkdirSync(destructCacheDirectory); + return destructCacheDirectory; + } + + private async copyAndValidateDestructiveManifest(existingManifestPath: string, workingDirectory: string) { + let destructiveManifestFile = path.join(workingDirectory, 'destructiveChanges.xml'); + + //Copy Destructive Manifest File to Temporary Directory + fs.copyFileSync(existingManifestPath, destructiveManifestFile); + const parser = new XMLParser(); + let destructiveChanges = await parser.parse(fs.readFileSync(path.resolve(destructiveManifestFile))); + + if (isEmpty(destructiveChanges['Package']['types'])) { + throw new Error('Invalid Destructive Change Definition encountered, please check'); + } + + SFPLogger.log(destructiveChanges['Package']['types'], LoggerLevel.TRACE); + } + + + private generateEmptyPackageXml(workingDirectory: string, apiversion: string) { + let packageXml = ` + + + * + CustomLabel + + ${apiversion} + `; + + let packageXmlPath = path.join(workingDirectory, 'package.xml'); + fs.outputFileSync(packageXmlPath, packageXml); + + SFPLogger.log(`Empty Package.xml with ${apiversion} created at ${workingDirectory}`, LoggerLevel.DEBUG); + } + + private async generateDeploymentZipFile(workingDirectory: string) { + let zip = new AdmZip(); + zip.addLocalFolder(workingDirectory); + zip.writeZip(path.join(workingDirectory, 'package.zip')); + return path.join(workingDirectory, 'package.zip'); + } + + + + private async deployDestructiveManifest(zipFile: string, conn: Connection) { + //Deploy Package + conn.metadata.pollTimeout = 300; + + const zipStream = fs.createReadStream(zipFile); + let deployResult = await conn.metadata.deploy(zipStream, { rollbackOnError: true, singlePackage: true }); + + + SFPLogger.log( + `Deploying Destructive Changes with ID ${deployResult.id} to ${conn.getUsername()}`, + LoggerLevel.INFO + ); + let deploymentStatus = await this.checkDeploymentStatus(conn, deployResult.id); + + if (deploymentStatus.success) { + if (deploymentStatus.success) + SFPLogger.log( + `Deployed Destructive Changes in target org ${conn.getUsername()} succesfully`, + LoggerLevel.INFO + ); + } else { + let componentFailures = deploymentStatus.details.componentFailures; + let errorResult = []; + componentFailures.forEach((failure) => { + errorResult.push({ + componentType: failure.componentType, + fullName: failure.fullName, + problem: failure.problem, + }); + }); + + console + throw new Error('Unable to deploy the Destructive Changes: ' + JSON.stringify(errorResult)); + } + } + + private async checkDeploymentStatus(conn: Connection, retrievedId: string) { + + while (true) { + let result = await conn.metadata.checkDeployStatus(retrievedId, true); + + if (!result.done) { + SFPLogger.log('Polling for Deployment Status', LoggerLevel.INFO); + await delay(5000); + } else { + return result; + } + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/components/MetadataCount.ts b/packages/sfpowerscripts-cli/src/core/package/components/MetadataCount.ts new file mode 100644 index 000000000..23454e15d --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/components/MetadataCount.ts @@ -0,0 +1,18 @@ +import { globSync } from 'glob'; +import path from 'path'; + +export default class MetadataCount { + public static async getMetadataCount(projectDirectory: string, sourceDirectory: string): Promise { + let metadataCount; + try { + let metadataFiles: string[] = globSync(`**/*-meta.xml`, { + cwd: projectDirectory ? path.join(projectDirectory, sourceDirectory) : sourceDirectory, + absolute: true, + }); + metadataCount = metadataFiles.length; + } catch (error) { + metadataCount = -1; + } + return metadataCount; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/components/PackageManifest.ts b/packages/sfpowerscripts-cli/src/core/package/components/PackageManifest.ts new file mode 100644 index 000000000..9d0be4521 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/components/PackageManifest.ts @@ -0,0 +1,271 @@ +import path from 'path'; +import * as fs from 'fs-extra'; +import { ApexClasses } from '../SfpPackage'; +import xml2json from '../../utils/xml2json'; +const xml2js = require('xml2js'); + +export default class PackageManifest { + private _manifestJson; + private _manifestXml: string; + + /** + * Getter for package manifest JSON + */ + get manifestJson() { + return this._manifestJson; + } + + /** + * Getter for package manifest XML + */ + get manifestXml(): string { + return this._manifestXml; + } + + private constructor() {} + + /** + * Factory method + * @param mdapiDir directory containing package.xml + * @returns instance of PackageManifest + */ + static async create(mdapiDir: string): Promise { + const packageManifest = new PackageManifest(); + + const packageXml: string = fs.readFileSync(path.join(mdapiDir, 'package.xml'), 'utf8'); + + packageManifest._manifestXml = packageXml; + packageManifest._manifestJson = await xml2json(packageXml); + + return packageManifest; + } + + /** + * Factory method + * @param components + * @param apiVersion + * @returns intance of PackageManifest + */ + static createFromScratch(components: { fullName: string; type: string }[], apiVersion: string): PackageManifest { + const packageManifest = new PackageManifest(); + + const packageJson = { + $: { xmlns: 'http://soap.sforce.com/2006/04/metadata' }, + types: [], + version: apiVersion, + }; + + components.forEach((component) => { + const type = packageJson.types.find((type) => type.name === component.type); + if (type) { + // Add member to existing type + type.members.push(component.fullName); + } else { + // create new type + const newType = { + name: component.type, + members: [component.fullName], + }; + packageJson.types.push(newType); + } + }); + + const builder = new xml2js.Builder({ + xmldec: { version: '1.0', encoding: 'UTF-8' }, + }); + + let packageObj = { + Package: packageJson, + }; + + packageManifest._manifestXml = builder.buildObject(packageObj); + packageManifest._manifestJson = packageObj; + + return packageManifest; + } + + /** + * Factory method + * @param manifest package JSON + * @returns instance of PackageManifest + */ + static async createWithJSONManifest(manifest: any): Promise { + const packageManifest = new PackageManifest(); + packageManifest._manifestJson = manifest; + + const builder = new xml2js.Builder({ + xmldec: { version: '1.0', encoding: 'UTF-8' }, + }); + + packageManifest._manifestXml = builder.buildObject(manifest); + + return packageManifest; + } + + /** + * + * @returns true or false, for whether there are profiles + */ + public isProfilesInPackage(): boolean { + let isProfilesFound = false; + + if (this._manifestJson.Package.types) { + if (Array.isArray(this._manifestJson.Package.types)) { + for (const type of this._manifestJson.Package.types) { + if (type.name === 'Profile') { + isProfilesFound = true; + break; + } + } + } else if (this.manifestJson.Package.types.name === 'Profile') { + isProfilesFound = true; + } + } + + return isProfilesFound; + } + + /** + * + * @returns true or false, for whether there are profiles + */ + public isPermissionSetsInPackage(): boolean { + let isPermissionSetFound = false; + + if (this._manifestJson.Package.types) { + if (Array.isArray(this._manifestJson.Package.types)) { + for (const type of this._manifestJson.Package.types) { + if (type.name === 'PermissionSet') { + isPermissionSetFound = true; + break; + } + } + } else if (this.manifestJson.Package.types.name === 'PermissionSet') { + isPermissionSetFound = true; + } + } + + return isPermissionSetFound; + } + + public isPermissionSetGroupsFoundInPackage(): boolean { + let isPermissionSetGroupFound = false; + if (Array.isArray(this._manifestJson?.Package?.types)) { + for (let type of this._manifestJson.Package.types) { + if (type.name === 'PermissionSetGroup') { + isPermissionSetGroupFound = true; + break; + } + } + } else if (this._manifestJson?.Package?.types?.name === 'PermissionSetGroup') { + isPermissionSetGroupFound = true; + } + return isPermissionSetGroupFound; + } + + /** + * + * @returns true or false, for whether there are Apex classes and/or triggers + */ + public isApexInPackage(): boolean { + let isApexFound = false; + + if (this._manifestJson.Package.types) { + if (Array.isArray(this._manifestJson.Package.types)) { + for (const type of this._manifestJson.Package.types) { + if (type.name === 'ApexClass' || type.name === 'ApexTrigger') { + isApexFound = true; + break; + } + } + } else if ( + this._manifestJson.Package.types.name === 'ApexClass' || + this._manifestJson.Package.types.name === 'ApexTrigger' + ) { + isApexFound = true; + } + } + + return isApexFound; + } + + /** + * + * @returns Apex triggers if there are any, otherwise returns undefined + */ + public fetchTriggers(): ApexClasses { + let triggers: string[]; + + let types; + if (this._manifestJson.Package.types) { + if (this._manifestJson.Package.types instanceof Array) { + types = this._manifestJson.Package.types; + } else { + // Create array with single type + types = [this._manifestJson.Package.types]; + } + } + + if (types) { + for (const type of types) { + if (type.name === 'ApexTrigger') { + if (type.members instanceof Array) { + triggers = type.members; + } else { + // Create array with single member + triggers = [type.members]; + } + break; + } + } + } + + return triggers; + } + + public isPayloadContainTypesOtherThan(providedType: string): boolean { + let anyOtherType = false; + if (this._manifestJson.Package.types) { + if (Array.isArray(this._manifestJson.Package.types)) { + for (const type of this._manifestJson.Package.types) { + if (type.name !== providedType) { + anyOtherType = true; + break; + } + } + } else if (this._manifestJson.Package.types.name !== providedType) { + anyOtherType = true; + } + } + return anyOtherType; + } + + public isPayLoadContainTypesSupportedByProfiles(): boolean { + const profileSupportedMetadataTypes = [ + 'ApexClass', + 'CustomApplication', + 'CustomObject', + 'CustomField', + 'Layout', + 'ApexPage', + 'CustomTab', + 'RecordType', + 'SystemPermissions', + ]; + + let containsProfileSupportedType = false; + if (this._manifestJson.Package.types) { + if (Array.isArray(this._manifestJson.Package.types)) { + for (const type of this._manifestJson.Package.types) { + if (profileSupportedMetadataTypes.includes(type.name)) { + containsProfileSupportedType = true; + break; + } + } + } else if (profileSupportedMetadataTypes.includes(this._manifestJson.Package.types.name)) { + containsProfileSupportedType = true; + } + } + return containsProfileSupportedType; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/components/PackageToComponent.ts b/packages/sfpowerscripts-cli/src/core/package/components/PackageToComponent.ts new file mode 100644 index 000000000..7f6a6eef1 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/components/PackageToComponent.ts @@ -0,0 +1,28 @@ +import { ComponentSet } from '@salesforce/source-deploy-retrieve'; +import Component from '../../dependency/Component'; + + +export default class PackageToComponent { + public constructor(private packageName:string,private packageDirectory:string) {} + + public generateComponents() { + const components: Component[] = []; + + let componentSet = ComponentSet.fromSource(this.packageDirectory); + + let componentSetArray = componentSet.getSourceComponents().toArray(); + + for (const individualComponentFromComponentSet of componentSetArray) { + const component: Component = { + id: undefined, + fullName: individualComponentFromComponentSet.fullName, + type: individualComponentFromComponentSet.type.name, + files: [individualComponentFromComponentSet.xml], + package: this.packageName, + }; + components.push(component); + } + + return components; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/components/ReconcileProfileAgainstOrgImpl.ts b/packages/sfpowerscripts-cli/src/core/package/components/ReconcileProfileAgainstOrgImpl.ts new file mode 100644 index 000000000..209c95b29 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/components/ReconcileProfileAgainstOrgImpl.ts @@ -0,0 +1,53 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { ZERO_BORDER_TABLE } from '../../display/TableConstants'; +const Table = require('cli-table'); +import ProfileReconcile from '@flxblio/sfprofiles/lib/impl/source/profileReconcile'; +import SFPOrg from '../../org/SFPOrg'; +import path from 'path'; +import { METADATA_INFO } from '../../metadata/MetadataInfo'; + +export default class ReconcileProfileAgainstOrgImpl { + public constructor(private sfpOrg:SFPOrg, private project_directory: string, private logger: Logger) {} + + public async exec() { + + let result=[]; + try { + let profileReconciler = new ProfileReconcile(this.sfpOrg); + let reconcileProfiles = await profileReconciler.reconcile( + [ this.project_directory], + [], + undefined + ); + + // Return an object to be displayed with --json + + reconcileProfiles.forEach((file) => { + result.push({ + state: 'Cleaned', + fullName: path.basename(file, METADATA_INFO.Profile.sourceExtension), + type: 'Profile', + path: path.relative(this.project_directory, file), + }); + }); + } catch (err) { + SFPLogger.log(err, LoggerLevel.ERROR); + + SFPLogger.log( + 'An error occured during profile reconcile. You can rerun the command after a moment.', + LoggerLevel.ERROR + ); + } + const table = new Table({ + head: ['State', 'Full Name', 'Type', 'Path'], + chars: ZERO_BORDER_TABLE, + }); + for (let res of result) { + table.push([res.state, res.fullName, res.type, res.path]); + } + SFPLogger.log(table.toString(), LoggerLevel.INFO); + return result; + } + + +} diff --git a/packages/sfpowerscripts-cli/src/core/package/coverage/PackageTestCoverage.ts b/packages/sfpowerscripts-cli/src/core/package/coverage/PackageTestCoverage.ts new file mode 100644 index 000000000..b2d37496e --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/coverage/PackageTestCoverage.ts @@ -0,0 +1,273 @@ +import SFPLogger, { COLOR_WARNING, Logger } from '@flxblio/sfp-logger'; +import IndividualClassCoverage from '../../apex/coverage/IndividualClassCoverage'; +import SfpPackage, { PackageType } from '../SfpPackage'; +import { Connection } from '@salesforce/core'; +import ApexClassFetcher from '../../apex/ApexClassFetcher'; +import ApexCodeCoverageAggregateFetcher from '../../apex/coverage/ApexCodeCoverageAggregateFetcher'; +import ApexTriggerFetcher from '../../apex/ApexTriggerFetcher'; + +export default class PackageTestCoverage { + private individualClassCoverage: IndividualClassCoverage; + private packageTestCoverage: number = -1; // Set inital value + + public constructor( + private pkg: SfpPackage, + private codeCoverage: any, + private logger: Logger, + private readonly conn: Connection + ) { + this.individualClassCoverage = new IndividualClassCoverage(this.codeCoverage, this.logger); + } + + public async getCurrentPackageTestCoverage(): Promise { + let packageClasses: string[] = this.pkg.apexClassWithOutTestClasses; + let triggers: string[] = this.pkg.triggers; + + let filteredCodeCoverage = this.filterCodeCoverageToPackageClassesAndTriggers( + this.codeCoverage, + packageClasses, + triggers + ); + + let totalLines: number = 0; + let totalCovered: number = 0; + for (let classCoverage of filteredCodeCoverage) { + if (classCoverage.coveredPercent !== null) { + totalLines += classCoverage.totalLines; + totalCovered += classCoverage.totalCovered; + } + } + + let listOfApexClassOrTriggerId: string[] = []; + + let classesNotTouchedByTestClass = this.getClassesNotTouchedByTestClass(packageClasses, this.codeCoverage); + if (classesNotTouchedByTestClass.length > 0) { + let apexClassIds = ( + await new ApexClassFetcher(this.conn).fetchApexClassByName(classesNotTouchedByTestClass) + ).map((apexClass) => apexClass.Id); + listOfApexClassOrTriggerId = listOfApexClassOrTriggerId.concat(apexClassIds); + } + + let triggersNotTouchedByTestClass = this.getTriggersNotTouchedByTestClass(triggers, this.codeCoverage); + if (triggersNotTouchedByTestClass.length > 0) { + let triggerIds = ( + await new ApexTriggerFetcher(this.conn).fetchApexTriggerByName(triggersNotTouchedByTestClass) + ).map((trigger) => trigger.Id); + listOfApexClassOrTriggerId = listOfApexClassOrTriggerId.concat(triggerIds); + } + + if (listOfApexClassOrTriggerId.length > 0) { + let recordsOfApexCodeCoverageAggregate = await new ApexCodeCoverageAggregateFetcher( + this.conn + ).fetchACCAById(listOfApexClassOrTriggerId); + + if (recordsOfApexCodeCoverageAggregate.length > 0) { + let numLinesUncovered: number = 0; // aggregate number of unconvered lines for classes & triggers that are not touched by any test classes + recordsOfApexCodeCoverageAggregate.forEach((record) => { + numLinesUncovered += record.NumLinesUncovered; + }); + totalLines += numLinesUncovered; + } + } + + let testCoverage = Math.floor((totalCovered / totalLines) * 100); + this.packageTestCoverage = testCoverage; + return testCoverage; + } + + public async validateTestCoverage( + coverageThreshold?: number + ): Promise<{ + result: boolean; + message?: string; + packageTestCoverage: number; + classesCovered?: { name: string; coveredPercent: number }[]; + classesWithInvalidCoverage?: { name: string; coveredPercent: number }[]; + }> { + if (this.packageTestCoverage == -1) + //No Value available + await this.getCurrentPackageTestCoverage(); + + let classesCovered = this.getIndividualClassCoverageByPackage(this.codeCoverage); + + if (coverageThreshold == undefined || coverageThreshold < 75) { + SFPLogger.log('Setting minimum coverage percentage to 75%.'); + coverageThreshold = 75; + } + + + + if (this.pkg.packageType === PackageType.Unlocked) { + if (this.packageTestCoverage < coverageThreshold) { + // Coverage inadequate, set result to false + return { + result: false, // Had earlier Changed to warning in Apr-22, due to unstable coverage, now reverting + packageTestCoverage: this.packageTestCoverage, + classesCovered: classesCovered, + message: `${COLOR_WARNING( + `The package has an overall coverage of ${this.packageTestCoverage}%, which does not meet the required overall coverage of ${coverageThreshold}%` + )}`, + }; + } else { + return { + result: true, + packageTestCoverage: this.packageTestCoverage, + classesCovered: classesCovered, + message: `Package overall coverage is greater than ${coverageThreshold}%`, + }; + } + } else if (this.pkg.packageType === PackageType.Source || this.pkg.packageType === PackageType.Diff) { + SFPLogger.log("Package type is Source. Validating individual class coverage"); + + let individualClassValidationResults = this.individualClassCoverage.validateIndividualClassCoverage( + this.getIndividualClassCoverageByPackage(this.codeCoverage), + coverageThreshold + ); + + if (individualClassValidationResults.result) { + return { + result: true, + packageTestCoverage: this.packageTestCoverage, + classesCovered: classesCovered, + classesWithInvalidCoverage: individualClassValidationResults.classesWithInvalidCoverage, + message: `Individidual coverage of classes is greater than ${coverageThreshold}%`, + }; + } else { + return { + result: false, + packageTestCoverage: this.packageTestCoverage, + classesCovered: classesCovered, + classesWithInvalidCoverage: individualClassValidationResults.classesWithInvalidCoverage, + message: `There are classes that do not satisfy the minimum code coverage of ${coverageThreshold}%`, + }; + } + } else { + throw new Error('Unhandled package type'); + } + } + + private getIndividualClassCoverageByPackage(codeCoverageReport: any): { name: string; coveredPercent: number }[] { + let individualClassCoverage: { + name: string; + coveredPercent: number; + }[] = []; + + let packageClasses: string[] = this.pkg.apexClassWithOutTestClasses; + let triggers: string[] = this.pkg.triggers; + + codeCoverageReport = this.filterCodeCoverageToPackageClassesAndTriggers( + codeCoverageReport, + packageClasses, + triggers + ); + + for (let classCoverage of codeCoverageReport) { + if (classCoverage['coveredPercent'] !== null) { + individualClassCoverage.push({ + name: classCoverage['name'], + coveredPercent: classCoverage['coveredPercent'], + }); + } + } + + let namesOfClassesWithoutTest: string[] = this.getClassesNotTouchedByTestClass( + packageClasses, + codeCoverageReport + ); + + if (namesOfClassesWithoutTest.length > 0) { + let classesWithoutTest: { + name: string; + coveredPercent: number; + }[] = namesOfClassesWithoutTest.map((className) => { + return { name: className, coveredPercent: 0 }; + }); + individualClassCoverage = individualClassCoverage.concat(classesWithoutTest); + } + + // Check for triggers with no test class + let namesOfTriggersWithoutTest: string[] = this.getTriggersNotTouchedByTestClass(triggers, codeCoverageReport); + + if (namesOfTriggersWithoutTest.length > 0) { + let triggersWithoutTest: { + name: string; + coveredPercent: number; + }[] = namesOfTriggersWithoutTest.map((triggerName) => { + return { name: triggerName, coveredPercent: 0 }; + }); + individualClassCoverage = individualClassCoverage.concat(triggersWithoutTest); + } + + return individualClassCoverage; + } + + /** + * Returns names of triggers in the package that are not triggered by the execution of any test classes + * Returns empty array if triggers is null or undefined + * @param triggers + * @param codeCoverageReport + * @returns + */ + private getTriggersNotTouchedByTestClass(triggers: string[], codeCoverageReport: any): string[] { + if (triggers != null) { + return triggers.filter((trigger) => { + for (let classCoverage of codeCoverageReport) { + if (classCoverage['name'] === trigger) { + // Filter out triggers if accounted for in coverage json + return false; + } + } + return true; + }); + } else return []; + } + + /** + * Returns name of classes in the package that are not touched by the execution of any test classes + * Returns empty array if packageClasses is null or undefined + * @param packageClasses + * @param codeCoverageReport + * @returns + */ + private getClassesNotTouchedByTestClass(packageClasses: string[], codeCoverageReport: any): string[] { + if (packageClasses != null) { + return packageClasses.filter((packageClass) => { + for (let classCoverage of codeCoverageReport) { + if (classCoverage['name'] === packageClass) { + // Filter out package class if accounted for in coverage json + return false; + } + } + return true; + }); + } else return []; + } + + /** + * Filter code coverage to classes and triggers in the package + * @param codeCoverage + * @param packageClasses + * @param triggers + */ + private filterCodeCoverageToPackageClassesAndTriggers(codeCoverage, packageClasses: string[], triggers: string[]) { + let filteredCodeCoverage = codeCoverage.filter((classCoverage) => { + if (packageClasses != null) { + for (let packageClass of packageClasses) { + if (packageClass === classCoverage['name']) return true; + } + } + + if (triggers != null) { + for (let trigger of triggers) { + if (trigger === classCoverage['name']) { + return true; + } + } + } + + return false; + }); + + return filteredCodeCoverage; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/coverage/PackageVersionCoverage.ts b/packages/sfpowerscripts-cli/src/core/package/coverage/PackageVersionCoverage.ts new file mode 100644 index 000000000..bee874e13 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/coverage/PackageVersionCoverage.ts @@ -0,0 +1,39 @@ +import { Connection } from '@salesforce/core'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import Package2VersionFetcher from '../version/Package2VersionFetcher'; + +export default class PackageVersionCoverage { + public constructor(private connection: Connection, private logger: Logger) {} + + public async getCoverage(versionId: string): Promise { + const package2VersionFetcher = new Package2VersionFetcher(this.connection); + const package2Version = await package2VersionFetcher.fetchBySubscriberPackageVersionId(versionId); + SFPLogger.log(`Fetched Record ${JSON.stringify(package2Version)}`, LoggerLevel.TRACE, this.logger); + if (package2Version) { + var packageCoverage = {}; + packageCoverage.HasPassedCodeCoverageCheck = package2Version.HasPassedCodeCoverageCheck; + packageCoverage.coverage = package2Version.CodeCoverage ? package2Version.CodeCoverage.apexCodeCoveragePercentage : 0; + packageCoverage.packageId = package2Version.Package2Id; + packageCoverage.packageName = package2Version.Package2.Name; + packageCoverage.packageVersionId = package2Version.SubscriberPackageVersionId; + packageCoverage.packageVersionNumber = `${package2Version.MajorVersion}.${package2Version.MinorVersion}.${package2Version.PatchVersion}.${package2Version.BuildNumber}`; + + SFPLogger.log( + `Successfully Retrieved the Apex Test Coverage of the package version`, + LoggerLevel.INFO, + this.logger + ); + } else { + throw new Error(`Package version doesnot exist, Please check the version details`); + } + return packageCoverage; + } +} +interface PackageCoverage { + coverage: number; + packageName: string; + packageId: string; + packageVersionNumber: string; + packageVersionId: string; + HasPassedCodeCoverageCheck: boolean; +} diff --git a/packages/sfpowerscripts-cli/src/core/package/dependencies/ExternalPackage2DependencyResolver.ts b/packages/sfpowerscripts-cli/src/core/package/dependencies/ExternalPackage2DependencyResolver.ts new file mode 100644 index 000000000..a91dc95e0 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/dependencies/ExternalPackage2DependencyResolver.ts @@ -0,0 +1,103 @@ +import { Connection } from '@salesforce/core'; +import PackageDependencyResolver from './PackageDependencyResolver'; +import _ from 'lodash'; +import Package2VersionFetcher from '../version/Package2VersionFetcher'; +import Package2Detail from '../Package2Detail'; + +/** + * Resolves external package dependency versions to their subscriber version + */ +export default class ExternalPackage2DependencyResolver { + //TOOD: Finalize Keys + constructor(private conn: Connection, private projectConfig, private keys) {} + + public async resolveExternalPackage2DependenciesToVersions( + packagesToBeResolved?: string[], + packagesToBeSkipped?: string[], + isDependencyValidated?: boolean + ): Promise { + if (isDependencyValidated == undefined) isDependencyValidated = true; + //Do a dependency resolution first only for external dependencies + //Resolve .LATEST to exact version numbers + let revisedProjectConfig = await new PackageDependencyResolver( + this.conn, + this.projectConfig, + packagesToBeSkipped, + null, + isDependencyValidated + ).resolvePackageDependencyVersions(); + + let packageVersions: Package2Detail[] = []; + let packageVersionFetcher = new Package2VersionFetcher(this.conn); + + let packagesToKeys: { [p: string]: string }; + if (this.keys) { + packagesToKeys = this.parseKeys(this.keys); + } + + //Resolve provided version Number to SubscriberVersionId + for (const sfdxPackage of revisedProjectConfig.packageDirectories) { + + if(packagesToBeResolved && !packagesToBeResolved.includes(sfdxPackage.package)) + continue; + + if (sfdxPackage.dependencies && Array.isArray(sfdxPackage.dependencies)) { + for (let i = 0; i < sfdxPackage.dependencies.length; i++) { + let dependency = sfdxPackage.dependencies[i]; + + if (packagesToBeSkipped && packagesToBeSkipped.includes(dependency.package)) + { + let dependendentPackage: Package2Detail = { name: dependency.package }; + packageVersions.push(dependendentPackage); + continue; + } + + if (!packageVersions.find((elem) => elem.name == dependency.package)) { + let dependendentPackage: Package2Detail = { name: dependency.package }; + if (dependency.versionNumber) { + dependendentPackage.versionNumber = dependency.versionNumber; + let packageVersion = await packageVersionFetcher.fetchByPackage2Id( + revisedProjectConfig.packageAliases[dependendentPackage.name], + dependendentPackage.versionNumber, + true + ); + dependendentPackage.subscriberPackageVersionId = + packageVersion[0].SubscriberPackageVersionId; + } else { + dependendentPackage.subscriberPackageVersionId = + revisedProjectConfig.packageAliases[dependendentPackage.name]; + } + if (packagesToKeys?.[dependendentPackage.name]) { + dependendentPackage.key = packagesToKeys[dependency.package]; + } + packageVersions.push(dependendentPackage); + } + } + } + } + return packageVersions; + } + + /** + * Parse keys in string format "packageA:key packageB:key packageC:key" + * Returns map of packages to keys + * @param keys + */ + private parseKeys(keys: string) { + let output: { [p: string]: string } = {}; + + keys = keys.trim(); + let listOfKeys = keys.split(' '); + + for (let key of listOfKeys) { + let packageKeyPair = key.split(':'); + if (packageKeyPair.length === 2) { + output[packageKeyPair[0]] = packageKeyPair[1]; + } else { + // Format is incorrect, throw an error + throw new Error(`Error parsing keys, format should be: "packageA:key packageB:key packageC:key"`); + } + } + return output; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/dependencies/PackageDependencyResolver.ts b/packages/sfpowerscripts-cli/src/core/package/dependencies/PackageDependencyResolver.ts new file mode 100644 index 000000000..43babb444 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/dependencies/PackageDependencyResolver.ts @@ -0,0 +1,269 @@ +import { Connection } from '@salesforce/core'; +import lodash = require('lodash'); +import Git from '../../git/Git'; +import GitTags from '../../git/GitTags'; +import Package2VersionFetcher, { Package2Version } from '../version/Package2VersionFetcher'; +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; + + +/** + * Resolves package dependency versions to their exact versions + */ +export default class PackageDependencyResolver { + private package2VersionCache: Package2VersionCache = new Package2VersionCache(); + + constructor( + private conn: Connection, + private projectConfig, + private packagesToBeSkipped?: string[], + private packagesToBeResolved?: string[], + private resolveExternalDepenciesOnly?: boolean + ) { + // prevent mutation of original config + this.projectConfig = lodash.cloneDeep(this.projectConfig); + } + + /** + * Resolves package dependency versions in project config + * Skips dependencies on packages that are queued for build, as they are resolved dynamically(packagesToBeSkipped) + * @returns new project config JSON, does not change original JSON + */ + public async resolvePackageDependencyVersions() { + for (const packageDirectory of this.projectConfig.packageDirectories) { + if (this.packagesToBeResolved?.length > 0 && this.packagesToBeSkipped?.length > 0) { + throw Error(`Unsupported path.. Use only one of the options at any given time`); + } + + if (this.packagesToBeSkipped && !this.packagesToBeSkipped.includes(packageDirectory.package)) { + continue; + } + + if (this.packagesToBeResolved && !this.packagesToBeResolved.includes(packageDirectory.package)) { + continue; + } + if (packageDirectory.dependencies && Array.isArray(packageDirectory.dependencies)) { + for (let i = 0; i < packageDirectory.dependencies.length; i++) { + let dependency = packageDirectory.dependencies[i]; + if (this.projectConfig.packageAliases[dependency.package] === undefined && !this.isSubscriberPackageVersionId(dependency.package)) { + + throw new Error(`Can't find package id for dependency: ` + dependency.package); + } + + let packageVersionId = this.isSubscriberPackageVersionId(dependency.package)?dependency.package:this.projectConfig.packageAliases[dependency.package] + + if (this.isSubscriberPackageVersionId(packageVersionId)) { + // Already resolved + continue; + } + + if (this.packagesToBeSkipped && this.packagesToBeSkipped.includes(dependency.package) && !dependency.branch) { + // Dependency is part of the same build, will be resolved when new version is created + continue; + } + let package2VersionForDependency: any = ''; + if ( dependency.branch && dependency.branch !== '' ) { + SFPLogger.log(`Specified branch: ${dependency.branch} for dependency: ${dependency.package}`, LoggerLevel.INFO); + package2VersionForDependency = await this.getPackage2VersionForDependency( + this.conn, + dependency, + packageVersionId, + dependency.branch + ); + SFPLogger.log(`Fetched latest branched package of ${dependency.package},` + +`version: ${package2VersionForDependency.MajorVersion}.` + +`${package2VersionForDependency.MinorVersion}.` + +`${package2VersionForDependency.PatchVersion}.` + +`${package2VersionForDependency.BuildNumber}`, LoggerLevel.INFO); + + let branchedPackageAlias = `${dependency.package}@` + +`${package2VersionForDependency.MajorVersion}.` + +`${package2VersionForDependency.MinorVersion}.` + +`${package2VersionForDependency.PatchVersion}.` + +`${package2VersionForDependency.BuildNumber}-` + +`${dependency.branch}`; + dependency.package = branchedPackageAlias; + this.projectConfig.packageAliases[branchedPackageAlias] = package2VersionForDependency.SubscriberPackageVersionId; + delete dependency.versionNumber; + delete dependency.branch; + continue; + + }else { + package2VersionForDependency = await this.getPackage2VersionForDependency( + this.conn, + dependency, + packageVersionId + ); + } + + + if (package2VersionForDependency == null) { + packageDirectory.dependencies.splice(i, 1); + i--; + } else + dependency.versionNumber = `${package2VersionForDependency.MajorVersion}.${package2VersionForDependency.MinorVersion}.${package2VersionForDependency.PatchVersion}.${package2VersionForDependency.BuildNumber}`; + } + } + } + return this.projectConfig; + } + + /** + * Get last validated Package2 version for package dependency + * @param conn + * @param dependency + * @returns Package2Version + */ + private async getPackage2VersionForDependency( + conn: Connection, + dependency: { package: string; versionNumber: string }, + packageVersionId: string, + branch?: string, + ): Promise { + + //Dont hit api's if its only for external dependencies + if (this.projectConfig.packageDirectories.find((dir) => dir.package === dependency.package)) { + if (this.resolveExternalDepenciesOnly) return null; + } + + let package2Version: Package2Version; + + let versionNumber: string = dependency.versionNumber; + let vers: string[] = versionNumber.split('.'); + if (vers.length === 4 && vers[3] === 'LATEST') { + versionNumber = `${vers[0]}.${vers[1]}.${vers[2]}`; + } + + let package2Versions: Package2Version[]; + if (this.package2VersionCache.has(packageVersionId, versionNumber)) { + package2Versions = this.package2VersionCache.get( + packageVersionId, + versionNumber + ); + } else { + const package2VersionFetcher = new Package2VersionFetcher(conn); + let records; + if( branch ){ + records = await package2VersionFetcher.fetchByPackageBranchAndName( + branch, + dependency.package, + versionNumber + ); + }else{ + records = await package2VersionFetcher.fetchByPackage2Id( + packageVersionId, + versionNumber, + true + ); + } + + this.package2VersionCache.set( + packageVersionId, + versionNumber, + records + ); + package2Versions = this.package2VersionCache.get( + packageVersionId, + versionNumber + ); + } + + if (package2Versions.length === 0) { + throw new Error( + `Failed to find any validated Package2 versions for the dependency ${dependency.package} with version ${dependency.versionNumber}` + ); + } + + if (this.projectConfig.packageDirectories.find((dir) => dir.package === dependency.package && !branch)) { + package2Version = await this.getPackage2VersionFromCurrentBranch(package2Versions, dependency); + } else { + // Take last validated package for external packages + package2Version = package2Versions[0]; + } + return package2Version; + } + + /** + * Get Package2 version created from the current branch + * @param package2Versions + * @param dependency + * @returns Package2Version + */ + private async getPackage2VersionFromCurrentBranch( + package2Versions: Package2Version[], + dependency: { package: string; versionNumber: string } + ) { + let package2VersionOnCurrentBranch: Package2Version; + + const git = await Git.initiateRepo(); + const gitTags = new GitTags(git, dependency.package); + const tags = await gitTags.listTagsOnBranch(); + + for (const package2Version of package2Versions) { + const version = `${package2Version.MajorVersion}.${package2Version.MinorVersion}.${package2Version.PatchVersion}.${package2Version.BuildNumber}`; + for (const tag of tags) { + if (tag.endsWith(version)) { + package2VersionOnCurrentBranch = package2Version; + break; + } + } + if (package2VersionOnCurrentBranch) break; + } + + if (!package2VersionOnCurrentBranch) { + throw new Error( + `Failed to find validated Package2 version for dependency ${dependency.package} with version ${dependency.versionNumber} created from the current branch` + ); + } + + return package2VersionOnCurrentBranch; + } + + private isSubscriberPackageVersionId(packageAlias: string): boolean { + const subscriberPackageVersionIdPrefix = '04t'; + return packageAlias.startsWith(subscriberPackageVersionIdPrefix); + } +} + +class Package2VersionCache { + private cache: { [p: string]: Package2Version[] } = {}; + + /** + * Checks whether cache contains key for package ID and version number + * @param packageId + * @param versionNumberstartw + * @returns true or false + */ + has(packageId: string, versionNumber: string): boolean { + const key = `${packageId}-${versionNumber}`; + if (this.cache[key]) return true; + else return false; + } + + /** + * Set the cache value, Package2 versions, for package ID and version number + * @param packageId + * @param versionNumber + * @param package2Versions + * @returns cache + */ + set( + packageId: string, + versionNumber: string, + package2Versions: Package2Version[] + ): { [p: string]: Package2Version[] } { + const key = `${packageId}-${versionNumber}`; + this.cache[key] = package2Versions; + return this.cache; + } + + /** + * + * @param packageId + * @param versionNumber + * @returns Package2 versions for package ID and version number + */ + get(packageId: string, versionNumber: string): Package2Version[] { + const key = `${packageId}-${versionNumber}`; + return this.cache[key]; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/dependencies/TransitiveDependencyResolver.ts b/packages/sfpowerscripts-cli/src/core/package/dependencies/TransitiveDependencyResolver.ts new file mode 100644 index 000000000..d91c91f27 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/dependencies/TransitiveDependencyResolver.ts @@ -0,0 +1,109 @@ +import ProjectConfig from '../../project/ProjectConfig'; +import { COLOR_HEADER, COLOR_KEY_MESSAGE, COLOR_SUCCESS, COLOR_ERROR } from '@flxblio/sfp-logger'; +import SFPLogger, { LoggerLevel, Logger } from '@flxblio/sfp-logger'; +import _, { uniq } from 'lodash'; +import semver = require('semver'); +import convertBuildNumDotDelimToHyphen from '../../utils/VersionNumberConverter'; +import { Connection } from '@salesforce/core'; +import UserDefinedExternalDependencyMap from '../../project/UserDefinedExternalDependency'; + +export default class TransitiveDependencyResolver { + constructor(private sfdxProjectConfig: any, private logger?: Logger) {} + + public async resolveTransitiveDependencies(): Promise> { + SFPLogger.log('Validating Project Dependencies...', LoggerLevel.INFO, this.logger); + + let clonedProjectConfig = await _.cloneDeep(this.sfdxProjectConfig); + clonedProjectConfig = await new UserDefinedExternalDependencyMap().cleanupEntries(clonedProjectConfig); + let pkgWithDependencies = ProjectConfig.getAllPackagesAndItsDependencies(clonedProjectConfig); + pkgWithDependencies = this.fillDepsWithUserDefinedExternalDependencyMap( + pkgWithDependencies, + new UserDefinedExternalDependencyMap().fetchDependencyEntries(clonedProjectConfig) + ); + pkgWithDependencies = this.fillDepsTransitively(pkgWithDependencies); + + return pkgWithDependencies; + } + + private fillDepsWithUserDefinedExternalDependencyMap( + pkgWithDependencies: Map, + externalDependencyMap: any + ): Map { + if (externalDependencyMap) { + for (let pkg of Object.keys(externalDependencyMap)) { + pkgWithDependencies.set(pkg, externalDependencyMap[pkg]); + } + } + return pkgWithDependencies; + } + + private fillDepsTransitively( + dependencyMap: Map + ): Map { + let pkgs = Array.from(dependencyMap.keys()); + for (let pkg of pkgs) { + SFPLogger.log( + COLOR_HEADER(`fetching dependencies for package:`) + COLOR_KEY_MESSAGE(pkg), + LoggerLevel.TRACE, + this.logger + ); + let dependenencies: { package: string; versionNumber?: string }[] = []; + for (let dependency of dependencyMap.get(pkg)) { + if (dependencyMap.get(dependency.package)) { + //push parents first + dependenencies = dependenencies.concat(dependencyMap.get(dependency.package)); + SFPLogger.log( + `pushing ${dependencyMap.get(dependency.package).length} dependencies from package ${ + dependency.package + }`, + LoggerLevel.TRACE, + this.logger + ); + } + //push itself + dependenencies.push(dependency); + } + //deduplicate dependency list + let uniqueDependencies = [ + ...new Set(dependenencies.map((objects) => JSON.stringify(objects))), + ].map((tmpString) => JSON.parse(tmpString)); + for (let j = 0; j < uniqueDependencies.length; j++) { + if (uniqueDependencies[j].versionNumber) { + let version = convertBuildNumDotDelimToHyphen(uniqueDependencies[j].versionNumber); + + for (let i = j + 1; i < uniqueDependencies.length; i++) { + if (uniqueDependencies[j].package == uniqueDependencies[i].package) { + let versionToCompare = convertBuildNumDotDelimToHyphen(uniqueDependencies[i].versionNumber); + // replace existing packageInfo if package version number is newer + if (semver.lt(version, versionToCompare)) { + uniqueDependencies = this.swapAndDropArrayElement(uniqueDependencies,j,i); + + } else { + uniqueDependencies.splice(i, 1); + i--; + } + } + } + } + //do a dedup again + uniqueDependencies = [ + ...new Set(uniqueDependencies.map((objects) => JSON.stringify(objects))), + ].map((tmpString) => JSON.parse(tmpString)); + } + dependencyMap.set(pkg, uniqueDependencies); + } + return dependencyMap; + } + + private swapAndDropArrayElement(arr: T[], i: number, j: number): T[] { + if (i < 0 || i >= arr.length || j < 0 || j >= arr.length) { + return arr; + } + + let newArr = [...arr]; + [newArr[i], newArr[j]] = [newArr[j], newArr[i]]; + return [...newArr.slice(0, j), ...newArr.slice(j + 1)]; + } + + +} diff --git a/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/DeploymentCustomizer.ts b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/DeploymentCustomizer.ts new file mode 100644 index 000000000..c7a74f82e --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/DeploymentCustomizer.ts @@ -0,0 +1,27 @@ +import { Logger } from "@flxblio/sfp-logger"; +import { Connection } from "@salesforce/core"; +import { ComponentSet } from "@salesforce/source-deploy-retrieve"; +import { DeploymentOptions } from "../../deployers/DeploySourceToOrgImpl"; +import SfpPackage from "../SfpPackage"; +import SFPOrg from "../../org/SFPOrg"; +import { DeploySourceResult } from "../../deployers/DeploymentExecutor"; + +export interface DeploymentContext +{ + apiVersion: string; + waitTime: string; +} + +export interface DeploymentCustomizer +{ + gatherComponentsToBeDeployed(sfpPackage: SfpPackage, componentSet:ComponentSet, conn: Connection, logger: Logger):Promise<{location:string, componentSet:ComponentSet}>; + isEnabled(sfpPackage:SfpPackage, conn:Connection,logger:Logger):Promise; + getDeploymentOptions( target_org: string, waitTime: string, apiVersion: string):Promise + getName():string + execute(sfpPackage: SfpPackage, + componentSet: ComponentSet, + sfpOrg:SFPOrg, + logger: Logger, + deploymentContext:DeploymentContext + ):Promise +} \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/FHTEnabler.ts b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/FHTEnabler.ts new file mode 100644 index 000000000..709ce227f --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/FHTEnabler.ts @@ -0,0 +1,121 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { ComponentSet, registry } from '@salesforce/source-deploy-retrieve'; +import * as fs from 'fs-extra'; +import QueryHelper from '../../queryHelper/QueryHelper'; +import SfpPackage from '../SfpPackage'; +import { Connection } from '@salesforce/core'; + +import { Schema } from 'jsforce'; +import CustomFieldFetcher from '../../metadata/CustomFieldFetcher'; +import SFPOrg from '../../org/SFPOrg'; +import path from 'path'; +import OrgDetailsFetcher from '../../org/OrgDetailsFetcher'; +import { DeploymentOptions } from '../../deployers/DeploySourceToOrgImpl'; +import { TestLevel } from '../../apextest/TestOptions'; +import { MetdataDeploymentCustomizer } from './MetadataDeploymentCustomizer'; + +const QUERY_BODY = + 'SELECT QualifiedApiName, EntityDefinition.QualifiedApiName FROM FieldDefinition WHERE IsFieldHistoryTracked = true AND EntityDefinitionId IN '; + +export default class FHTEnabler extends MetdataDeploymentCustomizer { + + public async isEnabled(sfpPackage: SfpPackage, conn: Connection, logger: Logger): Promise { + //ignore if its a scratch org + const orgDetails = await new OrgDetailsFetcher(conn.getUsername()).getOrgDetails(); + if (orgDetails.isScratchOrg) return false; + + if ( + sfpPackage['isFHTFieldFound'] && + (sfpPackage.packageDescriptor.enableFHT == undefined || sfpPackage.packageDescriptor.enableFHT == true) + ) { + return true; + } + } + + + + public async getDeploymentOptions( target_org: string, waitTime: string, apiVersion: string):Promise + { + return { + ignoreWarnings:true, + waitTime:waitTime, + apiVersion:apiVersion, + testLevel : TestLevel.RunSpecifiedTests, + specifiedTests :'skip', + rollBackOnError:true + } + } + + public async gatherComponentsToBeDeployed( + sfpPackage: SfpPackage, + componentSet: ComponentSet, + conn: Connection, + logger: Logger + ): Promise<{ location: string; componentSet: ComponentSet }> { + //First retrieve all objects/fields of interest from the package + let objList = []; + let fieldList = []; + Object.keys(sfpPackage['fhtFields']).forEach((key) => { + objList.push(`'${key}'`); + sfpPackage['fhtFields'][key].forEach((field) => fieldList.push(key + '.' + field)); + }); + //Now query all the fields for this object where FHT is already enabled + SFPLogger.log( + `Gathering fields which are already enabled with trackHistory on target org....`, + LoggerLevel.INFO, + logger + ); + + SFPLogger.log('FHT QUERY: '+`${QUERY_BODY + '(' + objList + ')'}`,LoggerLevel.DEBUG) + let fhtFieldsInOrg = await QueryHelper.query<{ + QualifiedApiName: string; + EntityDefinition: any; + IsFieldHistoryTracked: boolean; + }>(QUERY_BODY + '(' + objList + ')', conn, true); + + //Clear of the fields that alread has FHT applied and keep a reduced filter + fhtFieldsInOrg.map((record) => { + let field = record.EntityDefinition.QualifiedApiName + '.' + record.QualifiedApiName; + const index = fieldList.indexOf(field); + if (index > -1) { + fieldList.splice(index, 1); + } + }); + + if (fieldList.length > 0) { + //Now retrieve the fields from the org + let customFieldFetcher: CustomFieldFetcher = new CustomFieldFetcher(logger); + let sfpOrg = await SFPOrg.create({ connection: conn }); + let fetchedCustomFields = await customFieldFetcher.getCustomFields(sfpOrg, fieldList); + + + + //Modify the component set + //Parsing is risky due to various encoding, so do an inplace replacement + for (const sourceComponent of fetchedCustomFields.components.getSourceComponents()) { + let metadataOfComponent = fs.readFileSync(sourceComponent.xml).toString(); + + metadataOfComponent = metadataOfComponent.replace( + 'false', + 'true' + ); + + + + fs.writeFileSync(path.join(sourceComponent.xml), metadataOfComponent); + } + + return { location: fetchedCustomFields.location, componentSet: fetchedCustomFields.components }; + } else SFPLogger.log(`No fields are required to be updated, skipping update of Field History Tracking`, LoggerLevel.INFO, logger); + } + + public getName(): string { + return 'Field History Tracking Enabler'; + } +} + +interface CustomField { + QualifiedApiName: string; + IsFieldHistoryTracked: boolean; + EntityDefinitionId: string; +} diff --git a/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/FTEnabler.ts b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/FTEnabler.ts new file mode 100644 index 000000000..3daa6708b --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/FTEnabler.ts @@ -0,0 +1,107 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { ComponentSet } from '@salesforce/source-deploy-retrieve'; +import * as fs from 'fs-extra'; +import QueryHelper from '../../queryHelper/QueryHelper'; +import SfpPackage from '../SfpPackage'; +import { Connection } from '@salesforce/core'; +import { Schema } from 'jsforce'; +import CustomFieldFetcher from '../../metadata/CustomFieldFetcher'; +import SFPOrg from '../../org/SFPOrg'; +import path from 'path'; +import OrgDetailsFetcher from '../../org/OrgDetailsFetcher'; +import { DeploymentOptions } from '../../deployers/DeploySourceToOrgImpl'; +import { TestLevel } from '../../apextest/TestOptions'; +import { MetdataDeploymentCustomizer } from './MetadataDeploymentCustomizer'; + +const QUERY_BODY = + 'SELECT QualifiedApiName, EntityDefinition.QualifiedApiName FROM FieldDefinition WHERE IsFeedEnabled = true AND EntityDefinitionId IN '; + +export default class FTEnabler extends MetdataDeploymentCustomizer { + public async isEnabled(sfpPackage: SfpPackage, conn: Connection, logger: Logger): Promise { + //ignore if its a scratch org + const orgDetails = await new OrgDetailsFetcher(conn.getUsername()).getOrgDetails(); + if (orgDetails.isScratchOrg) return false; + + if ( + sfpPackage['isFTFieldFound'] && + (sfpPackage.packageDescriptor.enableFT == undefined || sfpPackage.packageDescriptor.enableFT == true) + ) { + return true; + } + } + + public async getDeploymentOptions( target_org: string, waitTime: string, apiVersion: string):Promise + { + return { + ignoreWarnings:true, + waitTime:waitTime, + apiVersion:apiVersion, + testLevel : TestLevel.RunSpecifiedTests, + specifiedTests :'skip', + rollBackOnError:true + } + } + + public async gatherComponentsToBeDeployed( + sfpPackage: SfpPackage, + componentSet: ComponentSet, + conn: Connection, + logger: Logger + ): Promise<{ location: string; componentSet: ComponentSet }> { + //First retrieve all objects/fields of interest from the package + let objList = []; + let fieldList = []; + Object.keys(sfpPackage['ftFields']).forEach((key) => { + objList.push(`'${key}'`); + sfpPackage['ftFields'][key].forEach((field) => fieldList.push(key + '.' + field)); + }); + //Now query all the fields for this object where FT is already enabled + SFPLogger.log( + `Gathering fields which are already enabled with feed traking in the target org....`, + LoggerLevel.INFO, + logger + ); + + SFPLogger.log('FT QUERY: '+`${QUERY_BODY + '(' + objList + ')'}`,LoggerLevel.DEBUG) + let ftFieldsInOrg = await QueryHelper.query<{ + QualifiedApiName: string; + EntityDefinition: any; + IsFeedEnabled: boolean; + }>(QUERY_BODY + '(' + objList + ')', conn, true); + + //Clear of the fields that alread has FT applied and keep a reduced filter + ftFieldsInOrg.map((record) => { + let field = record.EntityDefinition.QualifiedApiName + '.' + record.QualifiedApiName; + const index = fieldList.indexOf(field); + if (index > -1) { + fieldList.splice(index, 1); + } + }); + + if (fieldList.length > 0) { + //Now retrieve the fields from the org + let customFieldFetcher: CustomFieldFetcher = new CustomFieldFetcher(logger); + let sfpOrg = await SFPOrg.create({ connection: conn }); + let fetchedCustomFields = await customFieldFetcher.getCustomFields(sfpOrg, fieldList); + + //Modify the component set + //Parsing is risky due to various encoding, so do an inplace replacement + for (const sourceComponent of fetchedCustomFields.components.getSourceComponents()) { + let metadataOfComponent = fs.readFileSync(sourceComponent.xml).toString(); + + metadataOfComponent = metadataOfComponent.replace( + 'false', + 'true' + ); + + fs.writeFileSync(path.join(sourceComponent.xml), metadataOfComponent); + } + + return { location: fetchedCustomFields.location, componentSet: fetchedCustomFields.components }; + } else SFPLogger.log(`No fields are required to be updated,skipping updates to Feed History tracking`, LoggerLevel.INFO, logger); + } + + public getName(): string { + return 'Feed Tracking Enabler'; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/MetadataDeploymentCustomizer.ts b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/MetadataDeploymentCustomizer.ts new file mode 100644 index 000000000..1c524e287 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/MetadataDeploymentCustomizer.ts @@ -0,0 +1,83 @@ +import { Connection } from "@salesforce/core"; +import DeploySourceToOrgImpl, { DeploymentOptions } from "../../deployers/DeploySourceToOrgImpl"; +import SfpPackage from "../SfpPackage"; +import { DeploymentContext, DeploymentCustomizer } from "./DeploymentCustomizer"; +import SFPLogger,{COLOR_KEY_MESSAGE,Logger,LoggerLevel} from "@flxblio/sfp-logger" +import { ComponentSet } from "@salesforce/source-deploy-retrieve"; +import SFPOrg from "../../org/SFPOrg"; +import DeploymentExecutor, { DeploySourceResult } from "../../deployers/DeploymentExecutor"; +import PackageComponentPrinter from "../../display/PackageComponentPrinter"; +import DeployErrorDisplayer from "../../display/DeployErrorDisplayer"; + +export abstract class MetdataDeploymentCustomizer implements DeploymentCustomizer +{ + abstract gatherComponentsToBeDeployed(sfpPackage: SfpPackage, componentSet: ComponentSet, conn: Connection, logger: Logger): Promise<{ location: string; componentSet: ComponentSet; }>; + abstract isEnabled(sfpPackage: SfpPackage, conn: Connection, logger: Logger): Promise; + abstract getDeploymentOptions(target_org: string, waitTime: string, apiVersion: string): Promise; + abstract getName(): string; + + + async execute(sfpPackage: SfpPackage, + componentSet: ComponentSet, + sfpOrg:SFPOrg, + logger: Logger, + deploymentContext:DeploymentContext + ):Promise + { + if (await this.isEnabled(sfpPackage, sfpOrg.getConnection(), logger)) { + SFPLogger.log( + `Executing Post Deployer ${COLOR_KEY_MESSAGE(this.getName())}`, + LoggerLevel.INFO, + logger + ); + let modifiedPackage = await this.gatherComponentsToBeDeployed( + sfpPackage, + componentSet, + sfpOrg.getConnection(), + logger + ); + + //Check if there are components to be deployed + //Asssume its sucessfully deployed + if (!modifiedPackage || modifiedPackage.componentSet.getSourceComponents().toArray().length == 0) { + return { + deploy_id: `000000`, + result: true, + message: `No deployment required`, + }; + } + + + //deploy the fht enabled components to the org + let deploymentOptions = await this.getDeploymentOptions( + sfpOrg.getUsername(), + deploymentContext.waitTime, + deploymentContext.apiVersion + ); + + //Print components inside Component Set + let components = modifiedPackage.componentSet.getSourceComponents(); + PackageComponentPrinter.printComponentTable(components, logger); + + let deploySourceToOrgImpl: DeploymentExecutor = new DeploySourceToOrgImpl( + sfpOrg, + modifiedPackage.location, + modifiedPackage.componentSet, + deploymentOptions, + logger + ); + + let result = await deploySourceToOrgImpl.exec(); + if (!result.result) { + DeployErrorDisplayer.displayErrors(result.response, logger); + } + return result; + } else { + SFPLogger.log( + `Post Deployer ${COLOR_KEY_MESSAGE(this.getName())} skipped or not enabled`, + LoggerLevel.INFO, + logger + ); + } + } +} \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/PicklistEnabler.ts b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/PicklistEnabler.ts new file mode 100644 index 000000000..b17b48be5 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/PicklistEnabler.ts @@ -0,0 +1,218 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { ComponentSet, registry } from '@salesforce/source-deploy-retrieve'; +import SfpPackage, { PackageType } from '../SfpPackage'; +import { Connection } from '@salesforce/core'; +import QueryHelper from '../../queryHelper/QueryHelper'; +import { DeploymentContext, DeploymentCustomizer } from './DeploymentCustomizer'; +import { DeploySourceResult } from '../../deployers/DeploymentExecutor'; +import SFPOrg from '../../org/SFPOrg'; +import { Schema } from 'jsforce'; +import { DeploymentOptions } from '../../deployers/DeploySourceToOrgImpl'; + +const QUERY_BODY = 'SELECT Id FROM FieldDefinition WHERE EntityDefinition.QualifiedApiName = '; + +export default class PicklistEnabler implements DeploymentCustomizer { + public async isEnabled(sfpPackage: SfpPackage, conn: Connection, logger: Logger): Promise { + if (sfpPackage.packageType === PackageType.Unlocked) { + if ( + sfpPackage.isPickListsFound && + (sfpPackage.packageDescriptor.enablePicklist == undefined || + sfpPackage.packageDescriptor.enablePicklist == true) + ) { + return true; + } + } else return false; + } + + async execute( + sfpPackage: SfpPackage, + componentSet: ComponentSet, + sfpOrg: SFPOrg, + logger: Logger, + deploymentContext: DeploymentContext + ): Promise { + try { + let sourceComponents = componentSet.getSourceComponents().toArray(); + let components = []; + + for (const sourceComponent of sourceComponents) { + if (sourceComponent.type.name == registry.types.customobject.name) { + components.push(...sourceComponent.getChildren()); + } + + if (sourceComponent.type.name == registry.types.customobject.children.types.customfield.name) { + components.push(sourceComponent); + } + } + + if (components) { + for (const fieldComponent of components) { + let customField = fieldComponent.parseXmlSync().CustomField; + //check for empty picklists + if ( + !customField || + customField['type'] !== 'Picklist' || + !customField.valueSet?.valueSetDefinition + ) { + continue; + } + //no updates for custom metadata picklists + if (customField['fieldManageability']) continue; + + let objName = fieldComponent.parent.fullName; + let picklistName = fieldComponent.name; + let urlId = + QUERY_BODY + "'" + objName + "'" + ' AND QualifiedApiName = ' + "'" + picklistName + "'"; + + let picklistValueSource = await this.getPicklistSource(customField); + + SFPLogger.log( + `Fetching picklist for custom field ${picklistName} on object ${objName}`, + LoggerLevel.INFO, + logger + ); + + let picklistInOrg = await this.getPicklistInOrg(urlId, sfpOrg.getConnection()); + + //check for empty picklists on org and fix first deployment issue + if (!picklistInOrg?.Metadata?.valueSet?.valueSetDefinition) { + SFPLogger.log( + `Picklist field ${objName}.${picklistName} not in target Org. Skipping`, + LoggerLevel.TRACE, + logger + ); + continue; + } + + + let picklistValueInOrg = []; + + for (const value of picklistInOrg.Metadata.valueSet.valueSetDefinition.value) { + //ignore inactive values from org + if (value.isActive == false) { + continue; + } + + let valueInfo: { [key: string]: string } = {}; + valueInfo.fullName = value['valueName']; + decodeURIComponent(valueInfo.fullName); + valueInfo.label = value['label']; + decodeURIComponent(valueInfo.label); + valueInfo.default = value['default'] && value['default'] === true ? 'true' : 'false'; + picklistValueInOrg.push(valueInfo); + } + + let isPickListIdentical = this.arePicklistsIdentical(picklistValueInOrg, picklistValueSource); + + if (!isPickListIdentical) { + this.deployPicklist(picklistInOrg, picklistValueSource, sfpOrg.getConnection(), logger); + } else { + SFPLogger.log( + `Picklist for custom field ${objName}.${picklistName} is identical to the source.No deployment`, + LoggerLevel.INFO, + logger + ); + } + } + + return { + deploy_id: `000000`, + result: true, + message: `Patched Picklists`, + }; + } + } catch (error) { + SFPLogger.log(`Unable to process Picklist update due to ${error.message}`, LoggerLevel.WARN, logger); + SFPLogger.log(`Error Details : ${error.stack}`, LoggerLevel.TRACE); + } + } + + private async getPicklistInOrg(urlId: string, conn: Connection): Promise { + let response = await QueryHelper.query(urlId, conn, true); + + if (response && Array.isArray(response) && response.length > 0 && response[0].attributes) { + let responseUrl = response[0].attributes.url; + let fieldId = responseUrl.slice(responseUrl.lastIndexOf('.') + 1); + let responsePicklist = await conn.tooling.sobject('CustomField').find({ Id: fieldId }); + + if (responsePicklist) { + return responsePicklist[0]; + } + } + } + + gatherComponentsToBeDeployed( + sfpPackage: SfpPackage, + componentSet: ComponentSet, + conn: Connection, + logger: Logger + ): Promise<{ location: string; componentSet: ComponentSet }> { + throw new Error('Method not implemented.'); + } + getDeploymentOptions(target_org: string, waitTime: string, apiVersion: string): Promise { + throw new Error('Method not implemented.'); + } + + private async getPicklistSource(customField: any): Promise { + let picklistValueSet = []; + let values = customField.valueSet?.valueSetDefinition?.value; + //only push values when picklist > 1 or exactly 1 value + if (Array.isArray(values)) { + for (const value of values) { + //ignore inactive values from source + if(!value?.isActive || value?.isActive == 'true'){ + picklistValueSet.push({fullName: value['fullName'] ? decodeURI(value['fullName']) : value['fullName'] , default: value.default, label: value['label'] ? decodeURI(value['label']) : value['label']}); + } + } + } else if (typeof values === 'object' && 'fullName' in values) { + //ignore inactive values from source + if(!values?.isActive || values?.isActive == 'true'){ + picklistValueSet.push({fullName: values['fullName'] ? decodeURI(values['fullName']) : values['fullName'] , default: values.default, label: values['label'] ? decodeURI(values['label']) : values['label']}); + } + } + return picklistValueSet; + } + + private arePicklistsIdentical(picklistValueInOrg: any[], picklistValueSource: any[]): boolean { + return ( + picklistValueInOrg.length === picklistValueSource.length && + picklistValueInOrg.every((element_1) => + picklistValueSource.some( + (element_2) => + element_1.fullName === element_2.fullName && + element_1.label === element_2.label && + element_1.default === element_2.default + ) + ) + ); + } + + private async deployPicklist(picklistInOrg: any, picklistValueSource: any, conn: Connection, logger: Logger) { + //empty the the old value set + picklistInOrg.Metadata.valueSet.valueSetDefinition.value = []; + picklistValueSource.map((value) => { + picklistInOrg.Metadata.valueSet.valueSetDefinition.value.push(value); + }); + picklistInOrg.Metadata.valueSet.valueSettings = []; + + let picklistToDeploy: any; + picklistToDeploy = { + attributes: picklistInOrg.attributes, + Id: picklistInOrg.Id, + Metadata: picklistInOrg.Metadata, + FullName: picklistInOrg.FullName, + }; + SFPLogger.log(`Update picklist for custom field ${picklistToDeploy.FullName}`, LoggerLevel.INFO, logger); + try { + await conn.tooling.sobject('CustomField').update(picklistToDeploy); + } catch (error) { + throw new Error( + `Unable to update picklist for custom field ${picklistToDeploy.FullName} due to ${error.message}` + ); + } + } + + public getName(): string { + return 'Picklist Enabler'; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/PostDeployersRegistry.ts b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/PostDeployersRegistry.ts new file mode 100644 index 000000000..d2ac675f6 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/PostDeployersRegistry.ts @@ -0,0 +1,18 @@ +import { DeploymentCustomizer } from './DeploymentCustomizer'; +import FHTEnabler from './FHTEnabler'; +import FTEnabler from './FTEnabler'; + + +export class PostDeployersRegistry { + static getPostDeployers(): DeploymentCustomizer[] { + let postDeployers: DeploymentCustomizer[] = []; + + //TODO: Make dynamic + let fhtEnabler = new FHTEnabler(); + let ftEnabler = new FTEnabler(); + postDeployers.push(fhtEnabler); + postDeployers.push(ftEnabler); + + return postDeployers; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/PreDeployersRegistry.ts b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/PreDeployersRegistry.ts new file mode 100644 index 000000000..2e423cbc9 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/deploymentCustomizers/PreDeployersRegistry.ts @@ -0,0 +1,14 @@ +import { DeploymentCustomizer } from './DeploymentCustomizer'; +import PicklistEnabler from './PicklistEnabler'; + + +export class PreDeployersRegistry { + static getPreDeployers(): DeploymentCustomizer[] { + let preDeployers: DeploymentCustomizer[] = []; + + let picklistEnabler = new PicklistEnabler(); + preDeployers.push(picklistEnabler); + + return preDeployers; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/deploymentFilters/DeploymentFilter.ts b/packages/sfpowerscripts-cli/src/core/package/deploymentFilters/DeploymentFilter.ts new file mode 100644 index 000000000..47290e189 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/deploymentFilters/DeploymentFilter.ts @@ -0,0 +1,13 @@ +import { ComponentSet } from "@salesforce/source-deploy-retrieve"; +import { Logger } from "@flxblio/sfp-logger"; +import SFPOrg from "../../org/SFPOrg"; +import { PackageType } from "../SfpPackage"; + +export interface DeploymentFilter +{ + apply(org: SFPOrg, componentSet: ComponentSet,logger:Logger):Promise; + isToApply(projectConfig: any,packageType:string): boolean; + +} + + diff --git a/packages/sfpowerscripts-cli/src/core/package/deploymentFilters/DeploymentFilterRegistry.ts b/packages/sfpowerscripts-cli/src/core/package/deploymentFilters/DeploymentFilterRegistry.ts new file mode 100644 index 000000000..4e5721e2f --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/deploymentFilters/DeploymentFilterRegistry.ts @@ -0,0 +1,18 @@ +import { DeploymentFilter } from './DeploymentFilter'; +import EntitlementVersionFilter from './EntitlementVersionFilter'; + + + + +export class DeploymentFilterRegistry { + static getImplementations(): DeploymentFilter[] { + let deploymentFilterImpls: DeploymentFilter[] = []; + + //TODO: Make dynamic + let entitlementVersionFilter = new EntitlementVersionFilter(); + deploymentFilterImpls.push(entitlementVersionFilter); + + + return deploymentFilterImpls; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/deploymentFilters/EntitlementVersionFilter.ts b/packages/sfpowerscripts-cli/src/core/package/deploymentFilters/EntitlementVersionFilter.ts new file mode 100644 index 000000000..47d9399c8 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/deploymentFilters/EntitlementVersionFilter.ts @@ -0,0 +1,110 @@ +import { ComponentSet, registry } from '@salesforce/source-deploy-retrieve'; +import SFPOrg from '../../org/SFPOrg'; +import QueryHelper from '../../queryHelper/QueryHelper'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { DeploymentFilter } from './DeploymentFilter'; +import * as fs from 'fs-extra'; +import SettingsFetcher from '../../metadata/SettingsFetcher'; +import { PackageType } from '../SfpPackage'; +const { XMLBuilder } = require('fast-xml-parser'); + +const EXISTING_SLAPPROCESS_QUERY = `SELECT Name, NameNorm,VersionNumber, VersionMaster FROM SlaProcess ORDER BY VersionNumber DESC`; +const EXISTING_SLAPPROCESS_QUERY_NO_VERSIONING = `SELECT Name, NameNorm FROM SlaProcess`; + +export default class EntitlementVersionFilter implements DeploymentFilter { + + public async apply(org: SFPOrg, componentSet: ComponentSet, logger: Logger): Promise { + //Only do if entitlment exits in the package + let sourceComponents = componentSet.getSourceComponents().toArray(); + let isEntitlementFound: boolean = false; + for (const sourceComponent of sourceComponents) { + if (sourceComponent.type.name === registry.types.entitlementprocess.name) { + isEntitlementFound = true; + break; + } + } + if (!isEntitlementFound) return componentSet; + + try { + let entitlementSettings = await new SettingsFetcher(logger).getSetttingMetadata(org, `Entitlement`); + + let query; + if (entitlementSettings.enableEntitlementVersioning == true) { + SFPLogger.log(`Entitlement Versioning enabled in the org....`, LoggerLevel.INFO, logger); + query = EXISTING_SLAPPROCESS_QUERY; + } else { + SFPLogger.log(`Entitlement Versioning not enabled in the org....`, LoggerLevel.INFO, logger); + query = EXISTING_SLAPPROCESS_QUERY_NO_VERSIONING; + } + + SFPLogger.log(`Filtering Entitlement Process....`, LoggerLevel.INFO, logger); + //Fetch Entitlements currently in the org + let slaProcessesInOrg = await QueryHelper.query(query, org.getConnection(), false); + let modifiedComponentSet = new ComponentSet(); + //Compare version numbers in the org vs version in the component set + //Remove if the version numbers match + for (const sourceComponent of sourceComponents) { + if (sourceComponent.type.name === registry.types.entitlementprocess.name) { + let slaProcessLocal = sourceComponent.parseXmlSync(); + + let slaProcessMatchedByName: SlaProcess = slaProcessesInOrg.find( + (element: SlaProcess) => element.Name == slaProcessLocal['EntitlementProcess']['name'] + ); + + if ( + slaProcessMatchedByName && + entitlementSettings.enableEntitlementVersioning && + slaProcessLocal['EntitlementProcess']['versionNumber'] > slaProcessMatchedByName.VersionNumber + ) { + //This is a deployment candidate + //Modify versionMaster tag to match in the org + slaProcessLocal['EntitlementProcess']['versionMaster'] = slaProcessMatchedByName.VersionMaster; + let builder = new XMLBuilder({ + format: true, + ignoreAttributes: false, + attributeNamePrefix: '@_', + }); + let xmlContent = builder.build(slaProcessLocal); + fs.writeFileSync(sourceComponent.xml, xmlContent); + modifiedComponentSet.add(sourceComponent); + } else if (slaProcessMatchedByName) { + SFPLogger.log( + `Skipping EntitlementProcess ${sourceComponent.name} as this version is already deployed`, + LoggerLevel.INFO, + logger + ); + } else { + //Doesnt exist, deploy + modifiedComponentSet.add(sourceComponent); + } + } else { + modifiedComponentSet.add(sourceComponent); + } + } + + SFPLogger.log(`Completed Filtering of EntitlementProcess\n`, LoggerLevel.INFO, logger); + return modifiedComponentSet; + } catch (error) { + SFPLogger.log(`Unable to filter entitlements, returning the unmodified package`, LoggerLevel.ERROR, logger); + return componentSet; + } + } + + public isToApply(projectConfig: any, packageType: string): boolean { + if (packageType != PackageType.Source) return false; + + if (projectConfig?.plugins?.sfp?.disableEntitlementFilter) return false; + else return true; + } + + + + +} + +interface SlaProcess { + Name: string; + NameNorm: string; + VersionNumber: string; + VersionMaster: string; +} diff --git a/packages/sfpowerscripts-cli/src/core/package/diff/PackageComponentDiff.ts b/packages/sfpowerscripts-cli/src/core/package/diff/PackageComponentDiff.ts new file mode 100644 index 000000000..10bd597bd --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/diff/PackageComponentDiff.ts @@ -0,0 +1,424 @@ +import * as xml2js from 'xml2js'; +import * as path from 'path'; +import * as fs from 'fs-extra'; +import * as rimraf from 'rimraf'; +import * as _ from 'lodash'; +import simplegit from 'simple-git'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import ProjectConfig from '../../project/ProjectConfig'; +import MetadataFiles from '../../metadata/MetadataFiles'; +import { SOURCE_EXTENSION_REGEX, MetadataInfo, METADATA_INFO } from '../../metadata/MetadataInfo'; +import { MetadataResolver } from '@salesforce/source-deploy-retrieve'; +import GitDiffUtils, { DiffFile, DiffFileStatus } from '../../git/GitDiffUtil'; + +const deleteNotSupported = ['RecordType']; +const git = simplegit(); +let sfdxManifest; + +export default class PackageComponentDiff { + private gitDiffUtils: GitDiffUtils; + + destructivePackageObjPre: any[]; + destructivePackageObjPost: any[]; + resultOutput: { + action: string; + metadataType: string; + componentName: string; + message: string; + path: string; + }[]; + public constructor( + private logger: Logger, + private sfdxPackage: string, + private revisionFrom?: string, + private revisionTo?: string, + private isDestructive?: boolean + ) { + if (this.revisionTo == null || this.revisionTo.trim() === '') { + this.revisionTo = 'HEAD'; + } + if (this.revisionFrom == null) { + this.revisionFrom = ''; + } + this.destructivePackageObjPost = []; + this.destructivePackageObjPre = []; + this.resultOutput = []; + + sfdxManifest = ProjectConfig.getSFDXProjectConfig(null); + this.gitDiffUtils = new GitDiffUtils(); + } + + public async build(outputFolder: string) { + rimraf.sync(outputFolder); + + const sepRegex = /\n|\r/; + let data = ''; + + //check if same commit + const commitFrom = await git.raw(['rev-list', '-n', '1', this.revisionFrom]); + const commitTo = await git.raw(['rev-list', '-n', '1', this.revisionTo]); + if (commitFrom === commitTo) { + throw new Error(`Unable to compute diff, as both commits are same`); + } + //Make it relative to make the command works from a project created as a subfolder in a repository + data = await git.diff([ + '--raw', + this.revisionFrom, + this.revisionTo, + '--relative', + ProjectConfig.getPackageDescriptorFromConfig(this.sfdxPackage, sfdxManifest).path, + ]); + + let content = data.split(sepRegex); + let diffFile: DiffFile = await this.parseContent(content); + await this.gitDiffUtils.fetchFileListRevisionTo(this.revisionTo, this.logger); + + let filesToCopy = diffFile.addedEdited; + let deletedFiles = diffFile.deleted; + + deletedFiles = deletedFiles.filter((deleted) => { + let found = false; + let deletedMetadata = MetadataFiles.getFullApiNameWithExtension(deleted.path); + for (let i = 0; i < filesToCopy.length; i++) { + let addedOrEdited = MetadataFiles.getFullApiNameWithExtension(filesToCopy[i].path); + if (deletedMetadata === addedOrEdited) { + found = true; + break; + } + } + return !found; + }); + + if (fs.existsSync(outputFolder) == false) { + fs.mkdirSync(outputFolder); + } + + const resolver = new MetadataResolver(); + + if (filesToCopy && filesToCopy.length > 0) { + for (let i = 0; i < filesToCopy.length; i++) { + + try { + let filePath = filesToCopy[i].path; + + let sourceComponents = resolver.getComponentsFromPath(filePath); + for (const sourceComponent of sourceComponents) { + if (sourceComponent.type.strategies?.adapter == AdapterId.MatchingContentFile) { + await this.gitDiffUtils.copyFile(sourceComponent.xml, outputFolder, this.logger); + await this.gitDiffUtils.copyFile(sourceComponent.content, outputFolder, this.logger); + } else if (sourceComponent.type.strategies?.adapter == AdapterId.MixedContent) { + await this.gitDiffUtils.copyFile(sourceComponent.xml, outputFolder, this.logger); + if(path.extname(sourceComponent.content)) + await this.gitDiffUtils.copyFile(sourceComponent.content, outputFolder, this.logger); + else + await this.gitDiffUtils.copyFolder(sourceComponent.content, outputFolder, this.logger); + } else if (sourceComponent.type.strategies?.adapter == AdapterId.Decomposed) { + await this.gitDiffUtils.copyFile(sourceComponent.xml, outputFolder, this.logger); + } else if (sourceComponent.type.strategies?.adapter == AdapterId.Bundle) { + await this.gitDiffUtils.copyFolder(sourceComponent.content, outputFolder, this.logger); + } else { + await this.gitDiffUtils.copyFile(sourceComponent.xml, outputFolder, this.logger); + } + } + } catch (error) { + + if(error.message.includes(`Unable to find the required file`)) + throw error; + + //Metadata resolver is not respecting forceignores at this stage + // So it fails on diff packages with post deploy, so lets ignore and move on + SFPLogger.log( + `Error while inferencing type of ${filesToCopy[i].path} to ${outputFolder} : ${error.message}`, + LoggerLevel.TRACE, + this.logger + ); + } + } + } + + if (this.isDestructive) { + SFPLogger.log('Creating Destructive Manifest..', LoggerLevel.TRACE, this.logger); + await this.createDestructiveChanges(deletedFiles, outputFolder); + } + + //Folder is empty after all this operations, return without copying additional files + if (fs.readdirSync(outputFolder).length === 0) { + rimraf.sync(outputFolder); + return null; + } + + SFPLogger.log(`Generating output summary`, LoggerLevel.TRACE, this.logger); + + return this.resultOutput; + } + + //TODO: Refactor using proper ignore + private checkForIngore(pathToIgnore: any[], filePath: string) { + pathToIgnore = pathToIgnore || []; + if (pathToIgnore.length === 0) { + return true; + } + + let returnVal = true; + pathToIgnore.forEach((ignore) => { + if ( + path.resolve(ignore) === path.resolve(filePath) || + path.resolve(filePath).includes(path.resolve(ignore)) + ) { + returnVal = false; + } + }); + return returnVal; + } + + private async createDestructiveChanges(filePaths: DiffFileStatus[], outputFolder: string) { + if (_.isNil(this.destructivePackageObjPost)) { + this.destructivePackageObjPost = []; + } else { + this.destructivePackageObjPost = this.destructivePackageObjPost.filter((metaType) => { + return !_.isNil(metaType.members) && metaType.members.length > 0; + }); + } + this.destructivePackageObjPre = []; + //returns root, dir, base and name + for (let i = 0; i < filePaths.length; i++) { + let filePath = filePaths[i].path; + try { + let matcher = filePath.match(SOURCE_EXTENSION_REGEX); + let extension = ''; + if (matcher) { + extension = matcher[0]; + } else { + extension = path.parse(filePath).ext; + } + + let name = MetadataInfo.getMetadataName(filePath); + + if (name) { + if (!MetadataFiles.isCustomMetadata(filePath, name)) { + // avoid to generate destructive for Standard Components + //Support on Custom Fields and Custom Objects for now + + this.resultOutput.push({ + action: 'Skip', + componentName: MetadataFiles.getMemberNameFromFilepath(filePath, name), + metadataType: 'StandardField/CustomMetadata', + message: '', + path: '--', + }); + + continue; + } + let member = MetadataFiles.getMemberNameFromFilepath(filePath, name); + if (name === METADATA_INFO.CustomField.xmlName) { + let isFormular = await this.gitDiffUtils.isFileIncludesContent(filePaths[i], ''); + if (isFormular) { + this.destructivePackageObjPre = this.buildDestructiveTypeObj( + this.destructivePackageObjPre, + name, + member + ); + + SFPLogger.log( + `${filePath} ${MetadataFiles.isCustomMetadata(filePath, name)}`, + LoggerLevel.DEBUG, + this.logger + ); + + this.resultOutput.push({ + action: 'Delete', + componentName: member, + metadataType: name, + message: '', + path: 'Manual Intervention Required', + }); + } else { + this.destructivePackageObjPost = this.buildDestructiveTypeObj( + this.destructivePackageObjPost, + name, + member + ); + } + SFPLogger.log( + `${filePath} ${MetadataFiles.isCustomMetadata(filePath, name)}`, + LoggerLevel.DEBUG, + this.logger + ); + + this.resultOutput.push({ + action: 'Delete', + componentName: member, + metadataType: name, + message: '', + path: 'destructiveChanges.xml', + }); + } else { + if (!deleteNotSupported.includes(name)) { + this.destructivePackageObjPost = this.buildDestructiveTypeObj( + this.destructivePackageObjPost, + name, + member + ); + this.resultOutput.push({ + action: 'Delete', + componentName: member, + metadataType: name, + message: '', + path: 'destructiveChanges.xml', + }); + } else { + //add the component in the manual action list + // TODO + } + } + } + } catch (ex) { + this.resultOutput.push({ + action: 'ERROR', + componentName: '', + metadataType: '', + message: ex.message, + path: filePath, + }); + } + } + + this.writeDestructivechanges(this.destructivePackageObjPost, outputFolder, 'destructiveChanges.xml'); + } + + private writeDestructivechanges(destrucObj: Array, outputFolder: string, fileName: string) { + //ensure unique component per type + for (let i = 0; i < destrucObj.length; i++) { + destrucObj[i].members = _.uniq(destrucObj[i].members); + } + destrucObj = destrucObj.filter((metaType) => { + return metaType.members && metaType.members.length > 0; + }); + + if (destrucObj.length > 0) { + let dest = { + Package: { + $: { + xmlns: 'http://soap.sforce.com/2006/04/metadata', + }, + types: destrucObj, + }, + }; + + let destructivePackageName = fileName; + let filepath = path.join(outputFolder, destructivePackageName); + let builder = new xml2js.Builder(); + let xml = builder.buildObject(dest); + fs.writeFileSync(filepath, xml); + } + } + + private buildDestructiveTypeObj(destructiveObj, name, member) { + let typeIsPresent = false; + for (let i = 0; i < destructiveObj.length; i++) { + if (destructiveObj[i].name === name) { + typeIsPresent = true; + destructiveObj[i].members.push(member); + break; + } + } + let typeNode: any; + if (typeIsPresent === false) { + typeNode = { + name: name, + members: [member], + }; + destructiveObj.push(typeNode); + } + return destructiveObj; + } + + private async parseContent(fileContents): Promise { + const statusRegEx = /\sA\t|\sM\t|\sD\t/; + const renamedRegEx = /\sR[0-9]{3}\t|\sC[0-9]{3}\t/; + const tabRegEx = /\t/; + const deletedFileRegEx = new RegExp(/\sD\t/); + const lineBreakRegEx = /\r?\n|\r|( $)/; + + let metadataFiles = new MetadataFiles(); + + let diffFile: DiffFile = { + deleted: [], + addedEdited: [], + }; + + for (let i = 0; i < fileContents.length; i++) { + if (statusRegEx.test(fileContents[i])) { + let lineParts = fileContents[i].split(statusRegEx); + + let finalPath = path.join('.', lineParts[1].replace(lineBreakRegEx, '')); + finalPath = finalPath.trim(); + finalPath = finalPath.replace('\\303\\251', 'é'); + + if (!(await metadataFiles.isInModuleFolder(finalPath))) { + continue; + } + + if (!metadataFiles.accepts(finalPath)) { + continue; + } + + let revisionPart = lineParts[0].split(/\t|\s/); + + if (deletedFileRegEx.test(fileContents[i])) { + //Deleted + diffFile.deleted.push({ + revisionFrom: revisionPart[2].substring(0, 9), + revisionTo: revisionPart[3].substring(0, 9), + path: finalPath, + }); + } else { + // Added or edited + diffFile.addedEdited.push({ + revisionFrom: revisionPart[2].substring(0, 9), + revisionTo: revisionPart[3].substring(0, 9), + path: finalPath, + }); + } + } else if (renamedRegEx.test(fileContents[i])) { + let lineParts = fileContents[i].split(renamedRegEx); + + let paths = lineParts[1].trim().split(tabRegEx); + + let finalPath = path.join('.', paths[1].trim()); + finalPath = finalPath.replace('\\303\\251', 'é'); + let revisionPart = lineParts[0].split(/\t|\s/); + + if (!(await metadataFiles.isInModuleFolder(finalPath))) { + continue; + } + + if (!metadataFiles.accepts(paths[0].trim())) { + continue; + } + + diffFile.addedEdited.push({ + revisionFrom: '0000000', + revisionTo: revisionPart[3], + renamedPath: path.join('.', paths[0].trim()), + path: finalPath, + }); + + //allow deletion of renamed components + diffFile.deleted.push({ + revisionFrom: revisionPart[2], + revisionTo: '0000000', + path: paths[0].trim(), + }); + } + } + return diffFile; + } +} +enum AdapterId { + Bundle = 'bundle', + Decomposed = 'decomposed', + Default = 'default', + MatchingContentFile = 'matchingContentFile', + MixedContent = 'mixedContent', +} diff --git a/packages/sfpowerscripts-cli/src/core/package/diff/PackageDiffImpl.ts b/packages/sfpowerscripts-cli/src/core/package/diff/PackageDiffImpl.ts new file mode 100644 index 000000000..e620bf96c --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/diff/PackageDiffImpl.ts @@ -0,0 +1,166 @@ +const fs = require('fs'); +const path = require('path'); +import Git from '../../git/Git'; +import IgnoreFiles from '../../ignore/IgnoreFiles'; +import SFPLogger, { COLOR_ERROR, COLOR_KEY_MESSAGE, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import ProjectConfig from '../../project/ProjectConfig'; +import GitTags from '../../git/GitTags'; +import lodash = require('lodash'); +import { EOL } from 'os'; +import { PackageType } from '../SfpPackage'; + +export class PackageDiffOptions { + skipPackageDescriptorChange?: boolean = false; + //If not set, utlize latest git tags + useLatestGitTags?:boolean=true; + packagesMappedToLastKnownCommitId?: { [p: string]: string }; + pathToReplacementForceIgnore?: string; +} + +export default class PackageDiffImpl { + public constructor( + private logger: Logger, + private sfdx_package: string, + private project_directory: string|null, + private diffOptions?: PackageDiffOptions + ) {} + + public async exec(): Promise<{ isToBeBuilt: boolean; reason: string; tag?: string }> { + let git: Git = await Git.initiateRepo(this.logger,this.project_directory); + + let projectConfig = ProjectConfig.getSFDXProjectConfig(this.project_directory); + let pkgDescriptor = ProjectConfig.getPackageDescriptorFromConfig(this.sfdx_package, projectConfig); + + SFPLogger.log( + COLOR_KEY_MESSAGE( + `${EOL}Checking last known tags for ${this.sfdx_package} to determine whether package is to be built...`, + ), + LoggerLevel.TRACE, + this.logger + ); + + let tag: string; + if (!this.diffOptions?.useLatestGitTags && this.diffOptions?.packagesMappedToLastKnownCommitId != null) { + tag = this.getLatestCommitFromMap(this.sfdx_package, this.diffOptions?.packagesMappedToLastKnownCommitId); + } else { + tag = await this.getLatestTagFromGit(git, this.sfdx_package); + } + + if (tag) { + SFPLogger.log(COLOR_KEY_MESSAGE(`\nUtilizing tag ${tag} for ${this.sfdx_package}`),LoggerLevel.TRACE,this.logger); + + // Get the list of modified files between the tag and HEAD refs + let modified_files: string[]; + try { + modified_files = await git.diff([`${tag}`, `HEAD`, `--no-renames`, `--name-only`]); + } catch (error) { + SFPLogger.log(COLOR_ERROR(`Unable to compute diff, The head of the branch is not reachable from the commit id ${tag}`)); + SFPLogger.log(COLOR_ERROR(`Check your current branch (in case of build) or the scratch org in case of validate command`)); + SFPLogger.log(COLOR_ERROR(`Actual error received:`)); + SFPLogger.log(COLOR_ERROR(error)); + throw new Error(`Failed to compute git diff for package ${this.sfdx_package} against commit id ${tag}`) + } + + let packageType: string = ProjectConfig.getPackageType(projectConfig, this.sfdx_package); + + if (packageType !== PackageType.Data) modified_files = this.applyForceIgnoreToModifiedFiles(modified_files); + + SFPLogger.log( + `Checking for changes in source directory ${path.normalize(pkgDescriptor.path)}`, + LoggerLevel.TRACE, + this.logger + ); + + // Check whether the package has been modified + for (let filename of modified_files) { + + let normalizedPkgPath = path.normalize(pkgDescriptor.path); + let normalizedFilename = path.normalize(filename); + + let relativePath = path.relative(normalizedPkgPath, normalizedFilename); + + if (!relativePath.startsWith('..')) { + SFPLogger.log(`Found change(s) in ${filename}`, LoggerLevel.TRACE, this.logger); + return { isToBeBuilt: true, reason: `Found change(s) in package`, tag: tag }; + } + } + + SFPLogger.log( + `Checking for changes to package descriptor in sfdx-project.json`, + LoggerLevel.TRACE, + this.logger + ); + let isPackageDescriptorChanged = await this.isPackageDescriptorChanged(git, tag, pkgDescriptor); + if (isPackageDescriptorChanged) { + return { isToBeBuilt: true, reason: `Package Descriptor Changed`, tag: tag }; + } + + return { isToBeBuilt: false, reason: `No changes found`, tag: tag }; + } else { + SFPLogger.log( + `Tag missing for ${this.sfdx_package}...marking package for build anyways`, + LoggerLevel.TRACE, + this.logger + ); + return { isToBeBuilt: true, reason: `Previous version not found` }; + } + } + + private applyForceIgnoreToModifiedFiles(modified_files: string[]) { + let forceignorePath: string; + if (this.diffOptions?.pathToReplacementForceIgnore) forceignorePath = this.diffOptions?.pathToReplacementForceIgnore; + else if (this.project_directory != null) forceignorePath = path.join(this.project_directory, '.forceignore'); + else forceignorePath = '.forceignore'; + + let ignoreFiles: IgnoreFiles = new IgnoreFiles(fs.readFileSync(forceignorePath).toString()); + + // Filter the list of modified files with .forceignore + modified_files = ignoreFiles.filter(modified_files); + + return modified_files; + } + + private async getLatestTagFromGit(git: Git, sfdx_package: string): Promise { + const gitTags: GitTags = new GitTags(git, sfdx_package); + let tags: string[] = await gitTags.listTagsOnBranch(); + + SFPLogger.log('Analysing tags:', LoggerLevel.DEBUG); + if (tags.length > 10) { + SFPLogger.log(tags.slice(-10).toString().replace(/,/g, '\n'), LoggerLevel.TRACE,this.logger); + } else { + SFPLogger.log(tags.toString().replace(/,/g, '\n'), LoggerLevel.TRACE,this.logger); + } + + return tags.pop(); + } + + private async isPackageDescriptorChanged(git: Git, latestTag: string, packageDescriptor: any): Promise { + let projectConfigJson: string = await git.show([`${latestTag}:sfdx-project.json`]); + let projectConfig = JSON.parse(projectConfigJson); + + let packageDescriptorFromLatestTag: string; + for (let dir of projectConfig['packageDirectories']) { + if (this.sfdx_package === dir.package) { + packageDescriptorFromLatestTag = dir; + } + } + + if (!lodash.isEqual(packageDescriptor, packageDescriptorFromLatestTag)) { + SFPLogger.log(`Found change in ${this.sfdx_package} package descriptor`, LoggerLevel.TRACE, this.logger); + + //skip check and ignore + if (this.diffOptions?.skipPackageDescriptorChange) { + SFPLogger.log(`Ignoring changes in package desriptor as asked to..`, LoggerLevel.TRACE, this.logger); + return false; + } else return true; + } else return false; + } + + private getLatestCommitFromMap(sfdx_package: string, packagesToCommits: { [p: string]: string }): string { + if (packagesToCommits[sfdx_package] != null) { + return packagesToCommits[sfdx_package]; + } else { + return null; + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/generators/SfpPackageContentGenerator.ts b/packages/sfpowerscripts-cli/src/core/package/generators/SfpPackageContentGenerator.ts new file mode 100644 index 000000000..cf5e58c58 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/generators/SfpPackageContentGenerator.ts @@ -0,0 +1,301 @@ +import ProjectConfig from '../../project/ProjectConfig'; +import * as rimraf from 'rimraf'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { mkdirpSync } from 'fs-extra'; +import * as fs from 'fs-extra'; +import PackageComponentDiff from '../diff/PackageComponentDiff'; +let path = require('path'); + +export default class SfpPackageContentGenerator { + public static isPreDeploymentScriptAvailable: boolean = false; + public static isPostDeploymentScriptAvailable: boolean = false; + + public static async generateSfpPackageDirectory( + logger: Logger, + projectDirectory: string, + projectConfig: any, + sfdx_package: string, + packageDirectory: string, + versionNumber:string, + destructiveManifestFilePath?: string, + configFilePath?: string, + pathToReplacementForceIgnore?: string, + revisionFrom?: string, + revisionTo?: string + ): Promise { + let artifactDirectory: string = `.sfp/${this.makefolderid(5)}_source`, + rootDirectory: string; + + if (projectDirectory) { + rootDirectory = projectDirectory; + } else { + rootDirectory = ''; + } + + if (packageDirectory == null) packageDirectory = ''; + + mkdirpSync(artifactDirectory); + + //Ensure the directory is clean + rimraf.sync(path.join(artifactDirectory, packageDirectory)); + + //Create a new directory + fs.mkdirsSync(path.join(artifactDirectory, packageDirectory)); + + SfpPackageContentGenerator.createScripts(artifactDirectory, rootDirectory, sfdx_package); + + SfpPackageContentGenerator.createForceIgnores(artifactDirectory, rootDirectory); + + + if (pathToReplacementForceIgnore) + SfpPackageContentGenerator.replaceRootForceIgnore(artifactDirectory, pathToReplacementForceIgnore, logger); + + if (destructiveManifestFilePath) { + SfpPackageContentGenerator.copyDestructiveManifests( + destructiveManifestFilePath, + artifactDirectory, + rootDirectory, + logger + ); + } + + if (configFilePath) { + SfpPackageContentGenerator.copyConfigFilePath(configFilePath, artifactDirectory, rootDirectory, logger); + } + + SfpPackageContentGenerator.handleUnpackagedMetadata( + sfdx_package, + projectConfig, + rootDirectory, + artifactDirectory + ); + + SfpPackageContentGenerator.createPackageManifests( + artifactDirectory, + rootDirectory, + projectConfig, + sfdx_package, + versionNumber + ); + + fs.copySync(path.join(rootDirectory, packageDirectory), path.join(artifactDirectory, packageDirectory)); + + return artifactDirectory; + } + + private static handleUnpackagedMetadata( + sfdx_package: string, + projectConfig: any, + rootDirectory: string, + artifactDirectory: string + ) { + const packageDescriptor = ProjectConfig.getPackageDescriptorFromConfig(sfdx_package, projectConfig); + if (packageDescriptor.unpackagedMetadata?.path) { + if (fs.pathExistsSync(packageDescriptor.unpackagedMetadata.path)) { + let unpackagedMetadataDir: string = path.join(artifactDirectory, `unpackagedMetadata`); + mkdirpSync(unpackagedMetadataDir); + fs.copySync(path.join(rootDirectory, packageDescriptor.unpackagedMetadata.path), unpackagedMetadataDir); + } else { + throw new Error(`unpackagedMetadata ${packageDescriptor.unpackagedMetadata.path} does not exist`); + } + } + } + + private static createPackageManifests( + artifactDirectory: string, + projectDirectory: string, + projectConfig: any, + sfdx_package: string, + versionNumber:string + ) { + // Create pruned package manifest in source directory + let cleanedUpProjectManifest = ProjectConfig.cleanupMPDFromProjectConfig(projectConfig, sfdx_package); + + //Ensure version numbers are used from + cleanedUpProjectManifest.packageDirectories[0].versionNumber=versionNumber + + //Handle unpackaged metadata + if (fs.existsSync(path.join(artifactDirectory, 'unpackagedMetadata'))) { + cleanedUpProjectManifest.packageDirectories[0].unpackagedMetadata.path = path.join('unpackagedMetadata'); + cleanedUpProjectManifest.packageDirectories.push({ path: path.join('unpackagedMetadata'), default: false }); + } + + //Setup preDeployment Script Path + if (fs.existsSync(path.join(artifactDirectory, 'scripts', `preDeployment`))) + cleanedUpProjectManifest.packageDirectories[0].preDeploymentScript = path.join('scripts', `preDeployment`); + + //Setup postDeployment Script Path + if (fs.existsSync(path.join(artifactDirectory, 'scripts', `postDeployment`))) + cleanedUpProjectManifest.packageDirectories[0].postDeploymentScript = path.join( + 'scripts', + `postDeployment` + ); + + fs.writeFileSync(path.join(artifactDirectory, 'sfdx-project.json'), JSON.stringify(cleanedUpProjectManifest)); + + // Copy original package manifest + let manifestsDir: string = path.join(artifactDirectory, `manifests`); + mkdirpSync(manifestsDir); + fs.copySync(path.join(projectDirectory, 'sfdx-project.json'), path.join(manifestsDir, 'sfdx-project.json.ori')); + } + + /** + * Create scripts directory containing preDeploy & postDeploy + * @param artifactDirectory + * @param projectDirectory + * @param sfdx_package + */ + private static createScripts(artifactDirectory: string, projectDirectory: string, sfdx_package): void { + let scriptsDir: string = path.join(artifactDirectory, `scripts`); + mkdirpSync(scriptsDir); + + let packageDescriptor = ProjectConfig.getSFDXPackageDescriptor(projectDirectory, sfdx_package); + + if (packageDescriptor.preDeploymentScript) { + if (projectDirectory) + packageDescriptor.preDeploymentScript = path.join( + projectDirectory, + packageDescriptor.preDeploymentScript + ); + + if (fs.existsSync(packageDescriptor.preDeploymentScript)) { + fs.copySync(packageDescriptor.preDeploymentScript, path.join(scriptsDir, `preDeployment`)); + } else { + throw new Error(`preDeploymentScript ${packageDescriptor.preDeploymentScript} does not exist`); + } + } + + if (packageDescriptor.postDeploymentScript) { + if (projectDirectory) + packageDescriptor.postDeploymentScript = path.join( + projectDirectory, + packageDescriptor.postDeploymentScript + ); + + if (fs.existsSync(packageDescriptor.postDeploymentScript)) { + fs.copySync(packageDescriptor.postDeploymentScript, path.join(scriptsDir, `postDeployment`)); + } else { + throw new Error(`postDeploymentScript ${packageDescriptor.postDeploymentScript} does not exist`); + } + } + } + + /** + * Create root forceignore and forceignores directory containing ignore files for different stages + * @param artifactDirectory + * @param projectDirectory + */ + private static createForceIgnores(artifactDirectory: string, projectDirectory: string): void { + let forceIgnoresDir: string = path.join(artifactDirectory, `forceignores`); + mkdirpSync(forceIgnoresDir); + + let projectConfig = ProjectConfig.getSFDXProjectConfig(projectDirectory); + let ignoreFiles = projectConfig.plugins?.sfp?.ignoreFiles; + + //TODO: Make this readable + //This is a fix when sfppackage is used in stages where build is not involved + //So it has to be build from the root of the unzipped directory + //and whatever mentioned in .json is already translated + + let rootForceIgnore: string = path.join(projectDirectory, '.forceignore'); + let copyForceIgnoreForStage = (stage) => { + if (ignoreFiles?.[stage]) { + if (fs.existsSync(path.join(projectDirectory, ignoreFiles[stage]))) { + fs.copySync( + path.join(projectDirectory, ignoreFiles[stage]), + path.join(forceIgnoresDir, '.' + stage + 'ignore') + ); + } else if (fs.existsSync(path.join(projectDirectory, 'forceignores', '.' + stage + 'ignore'))) { + fs.copySync( + path.join(projectDirectory, 'forceignores', '.' + stage + 'ignore'), + path.join(forceIgnoresDir, '.' + stage + 'ignore') + ); + } else throw new Error(`${ignoreFiles[stage]} does not exist`); + } else fs.copySync(rootForceIgnore, path.join(forceIgnoresDir, '.' + stage + 'ignore')); + + //append additional entry to force ignore file + //TODO: Revisit the location + fs.appendFileSync( path.join(forceIgnoresDir, '.' + stage + 'ignore'),"\n**/postDeploy"); + }; + + let stages: string[] = ['prepare', 'validate', 'quickbuild', 'build']; + stages.forEach((stage) => copyForceIgnoreForStage(stage)); + + fs.copySync(rootForceIgnore, path.join(artifactDirectory, '.forceignore')); + } + + /** + * Replaces root forceignore with provided forceignore + * @param artifactDirectory + * @param pathToReplacementForceIgnore + */ + private static replaceRootForceIgnore( + artifactDirectory: string, + pathToReplacementForceIgnore: string, + logger: Logger + ): void { + if (fs.existsSync(pathToReplacementForceIgnore)) { + fs.copySync(pathToReplacementForceIgnore, path.join(artifactDirectory, '.forceignore')); + } else { + SFPLogger.log(`${pathToReplacementForceIgnore} does not exist`, LoggerLevel.INFO, logger); + SFPLogger.log( + 'Package creation will continue using the unchanged forceignore in the root directory', + LoggerLevel.INFO, + logger + ); + } + } + + private static copyDestructiveManifests( + destructiveManifestFilePath: string, + artifactDirectory: string, + projectDirectory: any, + logger: Logger + ) { + if (fs.existsSync(destructiveManifestFilePath)) { + try { + fs.mkdirsSync(path.join(artifactDirectory, 'destructive')); + fs.copySync( + path.join(projectDirectory, destructiveManifestFilePath), + path.join(artifactDirectory, 'destructive', 'destructiveChanges.xml') + ); + } catch (error) { + SFPLogger.log( + 'Unable to read/parse destructive manifest, Please check your artifacts, Will result in an error while deploying', + LoggerLevel.WARN, + logger + ); + } + } + } + + private static copyConfigFilePath( + configFilePath: string, + artifactDirectory: string, + projectDirectory: any, + logger: Logger + ) { + if (fs.existsSync(configFilePath)) { + try { + fs.mkdirsSync(path.join(artifactDirectory, 'config')); + fs.copySync( + path.join(projectDirectory, configFilePath), + path.join(artifactDirectory, 'config', 'project-scratch-def.json') + ); + } catch (error) { + SFPLogger.log(error, LoggerLevel.TRACE, logger); + SFPLogger.log('Utilizing default config file path', LoggerLevel.TRACE, logger); + } + } + } + + private static makefolderid(length): string { + var result = ''; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charactersLength = characters.length; + for (var i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateDataPackageImpl.ts b/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateDataPackageImpl.ts new file mode 100644 index 000000000..5d59eadba --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateDataPackageImpl.ts @@ -0,0 +1,86 @@ +import SFPLogger, { LoggerLevel, Logger } from '@flxblio/sfp-logger'; +import path from 'path'; +import FileSystem from '../../utils/FileSystem'; +import { CreatePackage } from './CreatePackage'; +import SfpPackage, { PackageType, SfpPackageParams } from '../SfpPackage'; +import { PackageCreationParams } from '../SfpPackageBuilder'; + +const SFDMU_CONFIG = 'export.json'; +const VLOCITY_CONFIG = 'VlocityComponents.yaml'; + +export default class CreateDataPackageImpl extends CreatePackage { + public constructor( + protected projectDirectory: string, + protected sfpPackage: SfpPackage, + protected packageCreationParams: PackageCreationParams, + protected logger?: Logger, + protected params?: SfpPackageParams + ) { + super(projectDirectory, sfpPackage, packageCreationParams, logger, params); + } + + getTypeOfPackage() { + return PackageType.Data; + } + + isEmptyPackage(projectDirectory: string, packageDirectory: string): boolean { + let files: string[] = FileSystem.readdirRecursive(path.join(projectDirectory, packageDirectory)); + + let hasExportJson = files.find((file) => path.basename(file) === 'export.json'); + + let hasCsvFile = files.find((file) => path.extname(file) === '.csv'); + + let hasYAMLFile = files.find((file) => path.extname(file) === '.yaml'); //check for vlocity config + + if(hasYAMLFile) return false; + + if (!hasExportJson || !hasCsvFile) return true; + else return false; + } + + preCreatePackage(sfpPackage) { + this.validateDataPackage(sfpPackage.resolvedPackageDirectory); + } + + createPackage(sfpPackage: SfpPackage) { + //Do Nothing, as no external calls or processing is required + } + + postCreatePackage(sfpPackage: SfpPackage) {} + + printAdditionalPackageSpecificHeaders() {} + + // Validate type of data package and existence of the correct configuration files + private validateDataPackage(packageDirectory: string) { + const files = FileSystem.readdirRecursive(packageDirectory); + let isSfdmu: boolean; + let isVlocity: boolean; + + for (const file of files) { + if (path.basename(file) === SFDMU_CONFIG) isSfdmu = true; + if (path.basename(file) === VLOCITY_CONFIG) isVlocity = true; + } + + if (isSfdmu && isVlocity) { + throw new Error( + `Data package '${this.sfpPackage.packageName}' contains both SFDMU & Vlocity configuration` + ); + } else if (isSfdmu) { + SFPLogger.log( + `Found export.json in ${packageDirectory}.. Utilizing it as data package and will be deployed using sfdmu`, + LoggerLevel.INFO, + this.logger + ); + } else if (isVlocity) { + SFPLogger.log( + `Found VlocityComponents.yaml in ${packageDirectory}.. Utilizing it as data package and will be deployed using vbt`, + LoggerLevel.INFO, + this.logger + ); + } else { + throw new Error( + `Could not find export.json or VlocityComponents.yaml in ${packageDirectory}. sfp only support vlocity or sfdmu based data packages` + ); + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateDiffPackageImpl.ts b/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateDiffPackageImpl.ts new file mode 100644 index 000000000..e94b293ab --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateDiffPackageImpl.ts @@ -0,0 +1,271 @@ +import SFPLogger, { LoggerLevel, Logger } from '@flxblio/sfp-logger'; +import { ApexSortedByType } from '../../apex/parser/ApexTypeFetcher'; +import SFPStatsSender from '../../stats/SFPStatsSender'; +import PackageEmptyChecker from '../validators/PackageEmptyChecker'; +import SfpPackage, { DiffPackageMetadata, PackageType, SfpPackageParams } from '../SfpPackage'; +import { PackageCreationParams } from '../SfpPackageBuilder'; +import SFPOrg from '../../org/SFPOrg'; +import CreateSourcePackageImpl from './CreateSourcePackageImpl'; +import PackageToComponent from '../components/PackageToComponent'; +import path from 'path'; +import * as fs from 'fs-extra'; +import ImpactedApexTestClassFetcher from '../../apextest/ImpactedApexTestClassFetcher'; +import SourceToMDAPIConvertor from '../packageFormatConvertors/SourceToMDAPIConvertor'; +import PackageManifest from '../components/PackageManifest'; +import MetadataCount from '../components/MetadataCount'; +import * as rimraf from 'rimraf'; +import Component from '../../dependency/Component'; +import PackageComponentDiff from '../diff/PackageComponentDiff'; + +export default class CreateDiffPackageImp extends CreateSourcePackageImpl { + public constructor( + protected projectDirectory: string, + protected sfpPackage: SfpPackage, + protected packageCreationParams: PackageCreationParams, + protected logger?: Logger, + protected params?: SfpPackageParams + ) { + super(projectDirectory, sfpPackage, packageCreationParams, logger, params); + } + + getTypeOfPackage() { + return PackageType.Diff; + } + + printAdditionalPackageSpecificHeaders() {} + + isEmptyPackage(projectDirectory: string, packageDirectory: string) { + return PackageEmptyChecker.isEmptyFolder(projectDirectory, packageDirectory); + } + + async preCreatePackage(sfpPackage: SfpPackage) { + const devhubOrg = await SFPOrg.create({ aliasOrUsername: this.packageCreationParams.devHub }); + + //Fetch Baseline commit from DevHub + let commitsOfPackagesInstalledInDevHub = await this.getCommitsOfPackagesInstalledInDevHub(devhubOrg); + + if (this.packageCreationParams.revisionFrom) { + this.sfpPackage.commitSHAFrom = this.packageCreationParams.revisionFrom; + } else if (commitsOfPackagesInstalledInDevHub[this.sfpPackage.packageName]) { + this.sfpPackage.commitSHAFrom = commitsOfPackagesInstalledInDevHub[this.sfpPackage.packageName]; + } else { + this.sfpPackage.commitSHAFrom = this.sfpPackage.sourceVersion; + } + + if (this.packageCreationParams.revisionTo) { + this.sfpPackage.commitSHATo = this.packageCreationParams.revisionTo; + } else { + this.sfpPackage.commitSHATo = this.sfpPackage.sourceVersion; + } + } + + private async getCommitsOfPackagesInstalledInDevHub(diffTargetSfpOrg: SFPOrg) { + let installedArtifacts = await diffTargetSfpOrg.getInstalledArtifacts(); + let packagesInstalledInOrgMappedToCommits = await this.mapInstalledArtifactstoPkgAndCommits(installedArtifacts); + return packagesInstalledInOrgMappedToCommits; + } + + public async createPackage(sfpPackage: SfpPackage) { + //Unresolved SHAs can be same if the package is not installed in the org or is the same + if (this.sfpPackage.commitSHAFrom != this.sfpPackage.commitSHATo) { + try { + let packageComponentDiffer: PackageComponentDiff = new PackageComponentDiff( + this.logger, + this.sfpPackage.packageName, + this.sfpPackage.commitSHAFrom, + this.sfpPackage.commitSHATo, + true + ); + await packageComponentDiffer.build(path.join(sfpPackage.workingDirectory, 'diff')); + } catch (error) { + //if both are same after git resolution.. just do nothing, treat is a normal source package + if (error.message.includes('Unable to compute diff, as both commits are same')) { + SFPLogger.log( + `Both commits are same, treating it as an empty package`, + LoggerLevel.WARN, + this.logger + ); + //Create an empty diff directory to force skip of packages + const diffSrcDir = path.join(sfpPackage.workingDirectory, `diff/${sfpPackage.packageDirectory}`); + fs.mkdirpSync(diffSrcDir); + } else throw error; + } + + await this.introspectDiffPackageCreated(sfpPackage, this.params, this.logger); + + await this.replaceSourceWithDiff( + sfpPackage.workingDirectory, + sfpPackage.packageDirectory, + `diff/${sfpPackage.packageDirectory}` + ); + + SFPStatsSender.logGauge('package.metadatacount', sfpPackage.metadataCount, { + package: sfpPackage.packageName, + type: sfpPackage.packageType, + }); + } + } + + postCreatePackage(sfpPackage) {} + + private async replaceSourceWithDiff( + workingDirectory: string, + packageDirectory: string, + diffPackageDirectory: string + ) { + const srcDir = path.join(workingDirectory, packageDirectory); + const diffSrcDir = path.join(workingDirectory, diffPackageDirectory); + + // Check if src directories exist, if so remove them + if (fs.pathExistsSync(srcDir)) await fs.remove(srcDir); + + // Rename diff/src directory to src + if (fs.pathExistsSync(diffSrcDir)) await fs.move(diffSrcDir, srcDir); + else { + // Ensure package directory exists + await fs.mkdirpSync(diffSrcDir); + await fs.move(diffSrcDir, srcDir); + } + + //check if destructiveChanges.xml exist in diff directory + const destructiveChangesPath = path.join(workingDirectory, 'diff', 'destructiveChanges.xml'); + if (fs.existsSync(destructiveChangesPath)) { + //Move destructiveChanges.xml to diff directory + await fs.move(destructiveChangesPath, path.join(workingDirectory, 'destructiveChanges.xml')); + } + //remove diffSrcDir + if (fs.pathExistsSync(path.join(workingDirectory, 'diff'))) + fs.removeSync(path.join(workingDirectory, 'diff')); + } + + async mapInstalledArtifactstoPkgAndCommits(installedArtifacts: any) { + let packagesMappedToLastKnownCommitId: { [p: string]: string } = {}; + packagesMappedToLastKnownCommitId = await getPackagesToCommits(installedArtifacts); + + return packagesMappedToLastKnownCommitId; + + async function getPackagesToCommits(installedArtifacts: any): Promise<{ [p: string]: string }> { + const packagesToCommits: { [p: string]: string } = {}; + let jsonOverrides = {}; + + // Add an option to override diff package from during debugging + // Also useful for when the record is yet to be baselined + try { + const jsonData = await fs.readFile('diffPackageOverrides.json', 'utf8'); + jsonOverrides = JSON.parse(jsonData); + } catch (error) { + console.log('No JSON override file found or there is an error reading it'); + } + + // Merge the installedArtifacts data with the JSON overrides + if (installedArtifacts) { + installedArtifacts.forEach((artifact) => { + packagesToCommits[artifact.Name] = artifact.CommitId__c; + }); + } + + // Add additional packages from the JSON overrides that are not in installedArtifacts + Object.keys(jsonOverrides).forEach((pkgName) => { + if (!packagesToCommits.hasOwnProperty(pkgName)) { + packagesToCommits[pkgName] = jsonOverrides[pkgName]; + } + }); + + if (process.env.VALIDATE_REMOVE_PKG) delete packagesToCommits[process.env.VALIDATE_REMOVE_PKG]; + + return packagesToCommits; + } + } + + private async introspectDiffPackageCreated( + sfpPackage: SfpPackage, + packageParams: SfpPackageParams, + logger: Logger + ): Promise { + let workingDirectory = path.join(sfpPackage.workingDirectory, 'diff'); + if (fs.existsSync(path.join(workingDirectory, sfpPackage.packageDirectory))) { + let changedComponents = new PackageToComponent( + sfpPackage.packageName, + path.join(workingDirectory, sfpPackage.packageDirectory) + ).generateComponents(); + + let impactedApexTestClassFetcher: ImpactedApexTestClassFetcher = new ImpactedApexTestClassFetcher( + sfpPackage, + changedComponents, + logger + ); + let impactedTestClasses = await impactedApexTestClassFetcher.getImpactedTestClasses(); + + //Convert again for finding the values in the diff package + let sourceToMdapiConvertor = new SourceToMDAPIConvertor( + workingDirectory, + sfpPackage.packageDescriptor.path, + sfpPackage.apiVersion, + logger + ); + + let mdapiDirPath = (await sourceToMdapiConvertor.convert()).packagePath; + + const packageManifest: PackageManifest = await PackageManifest.create(mdapiDirPath); + + sfpPackage.payload = packageManifest.manifestJson; + sfpPackage.apexTestClassses = impactedTestClasses; + sfpPackage.apexClassWithOutTestClasses = getOnlyChangedClassesFromPackage( + changedComponents, + sfpPackage.apexClassesSortedByTypes + ); + sfpPackage.isApexFound = packageManifest.isApexInPackage(); + sfpPackage.isProfilesFound = packageManifest.isProfilesInPackage(); + sfpPackage.isPermissionSetGroupFound = packageManifest.isPermissionSetGroupsFoundInPackage(); + sfpPackage.isPayLoadContainTypesSupportedByProfiles = packageManifest.isPayLoadContainTypesSupportedByProfiles(); + + sfpPackage.metadataCount = await MetadataCount.getMetadataCount( + workingDirectory, + sfpPackage.packageDescriptor.path + ); + rimraf.sync(mdapiDirPath); + } else { + //Souce Diff Directory is empty + sfpPackage.payload = {}; + sfpPackage.apexTestClassses = []; + sfpPackage.apexClassWithOutTestClasses = []; + sfpPackage.isApexFound = false; + sfpPackage.isProfilesFound = false; + sfpPackage.isPermissionSetGroupFound = false; + sfpPackage.isPayLoadContainTypesSupportedByProfiles = false; + sfpPackage.metadataCount = 0; + } + + function getOnlyChangedClassesFromPackage( + changedComponents: Component[], + apexClassesSortedByTypes: ApexSortedByType + ): string[] { + // Check if the parameters are not empty or undefined + if (!changedComponents || !apexClassesSortedByTypes) { + return undefined; + } + + // Check if the 'class' property exists in apexClassesSortedByTypes + if (!apexClassesSortedByTypes.class) { + return undefined; + } + + // Get the names of all classes in the ApexSortedByType + let apexClassNames = apexClassesSortedByTypes.class.map((cls) => cls.name); + let interfaces = apexClassesSortedByTypes.interface.map((cls) => cls.name); + const apexTestClassNames = apexClassesSortedByTypes.testClass.map((cls) => cls.name); + apexClassNames = apexClassNames.filter((name) => !apexTestClassNames.includes(name)); + apexClassNames = apexClassNames.filter((name) => !interfaces.includes(name)); + + // Filter changedComponents based on class names in ApexSortedByType and type === 'ApexClass' + const filteredComponents = changedComponents.filter( + (component) => apexClassNames.includes(component.fullName) && component.type === 'ApexClass' + ); + + // Extract the fullName property from the filtered components + const filteredChangedClasses = filteredComponents.map((component) => component.fullName); + + return filteredChangedClasses; + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreatePackage.ts b/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreatePackage.ts new file mode 100644 index 000000000..4f5225b8d --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreatePackage.ts @@ -0,0 +1,146 @@ +import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE, COLOR_WARNING, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import SFPStatsSender from '../../stats/SFPStatsSender'; +import SfpPackage, { PackageType, SfpPackageParams } from '../SfpPackage'; +import { PackageCreationParams } from '../SfpPackageBuilder'; + +export abstract class CreatePackage { + private startTime: number; + + constructor( + protected projectDirectory: string, + protected sfpPackage: SfpPackage, + protected packageCreationParams?: PackageCreationParams, + protected logger?: Logger, + protected params?: SfpPackageParams + ) { + //Initialize Params + if (this.params == null) this.params = {}; + } + + public async exec(): Promise { + //Capture Start TimegetSFDXProjectConfig + this.startTime = Date.now(); + + //Print Header + this.printHeader(); + + //Check if the package is empty + await this.checkWhetherProvidedPackageIsEmpty(this.sfpPackage.packageDescriptor.path); + //Call lifecycle commands + await this.preCreatePackage(this.sfpPackage); + await this.createPackage(this.sfpPackage); + await this.postCreatePackage(this.sfpPackage); + + //Add addtional descriptors available + this.writeDeploymentStepsToArtifact(this.sfpPackage); + + //Send Metrics to Logging system + this.sendMetricsWhenSuccessfullyCreated(); + + return this.sfpPackage; + } + + abstract getTypeOfPackage(); + + abstract preCreatePackage(sfpPackage: SfpPackage); + abstract createPackage(sfpPackage: SfpPackage); + abstract postCreatePackage(sfpPackage: SfpPackage); + + private sendMetricsWhenSuccessfullyCreated() { + let elapsedTime = Date.now() - this.startTime; + + this.sfpPackage.creation_details = { + creation_time: elapsedTime, + timestamp: Date.now(), + }; + + if (this.getTypeOfPackage() === PackageType.Source || this.getTypeOfPackage() === PackageType.Unlocked) + SFPStatsSender.logGauge('package.metadatacount', this.sfpPackage.metadataCount, { + package: this.sfpPackage.package_name, + type: this.sfpPackage.package_type, + }); + + SFPStatsSender.logCount('package.created', { + package: this.sfpPackage.package_name, + type: this.sfpPackage.package_type, + is_dependency_validated: String(this.sfpPackage.isDependencyValidated), + }); + + SFPStatsSender.logElapsedTime('package.elapsed.time', this.sfpPackage.creation_details.creation_time, { + package: this.sfpPackage.package_name, + type: this.sfpPackage.package_type, + is_dependency_validated: String(this.sfpPackage.isDependencyValidated), + }); + SFPStatsSender.logElapsedTime('package.creation.elapsed_time', this.sfpPackage.creation_details.creation_time, { + package: this.sfpPackage.package_name, + type: this.sfpPackage.package_type, + is_dependency_validated: String(this.sfpPackage.isDependencyValidated), + }); + } + + private writeDeploymentStepsToArtifact(packageDescriptor: any) { + if (packageDescriptor.assignPermSetsPreDeployment) { + if (packageDescriptor.assignPermSetsPreDeployment instanceof Array) + this.sfpPackage.assignPermSetsPreDeployment = packageDescriptor.assignPermSetsPreDeployment; + else throw new Error("Property 'assignPermSetsPreDeployment' must be of type array"); + } + + if (packageDescriptor.assignPermSetsPostDeployment) { + if (packageDescriptor.assignPermSetsPostDeployment instanceof Array) + this.sfpPackage.assignPermSetsPostDeployment = packageDescriptor.assignPermSetsPostDeployment; + else throw new Error("Property 'assignPermSetsPostDeployment' must be of type array"); + } + } + + private async checkWhetherProvidedPackageIsEmpty(packageDirectory: string) { + if (await this.isEmptyPackage(this.projectDirectory, packageDirectory)) { + if (this.packageCreationParams.breakBuildIfEmpty) + throw new Error(`Package directory ${packageDirectory} is empty`); + else this.printEmptyArtifactWarning(); + } + } + + abstract isEmptyPackage(projectDirectory: string, packageDirectory: string); + + protected printEmptyArtifactWarning() { + SFPLogger.printHeaderLine( + `WARNING! Empty aritfact encountered`, + COLOR_WARNING, + LoggerLevel.INFO, + this.logger + ); + SFPLogger.log( + 'Either this folder is empty or the application of .forceignore results in an empty folder', + LoggerLevel.INFO, + this.logger + ); + SFPLogger.log('Proceeding to create an empty artifact', LoggerLevel.INFO, this.logger); + SFPLogger.printHeaderLine('',COLOR_WARNING,LoggerLevel.INFO,this.logger); + } + + private printHeader() { + SFPLogger.log(COLOR_HEADER(`command: ${COLOR_KEY_MESSAGE(`create package`)}`), LoggerLevel.INFO, this.logger); + SFPLogger.log( + COLOR_HEADER(`package name: ${COLOR_KEY_MESSAGE(`${this.sfpPackage.packageName}`)}`), + LoggerLevel.INFO, + this.logger + ); + SFPLogger.log( + COLOR_HEADER(`package type: ${COLOR_KEY_MESSAGE(`${this.getTypeOfPackage()}`)}`), + LoggerLevel.INFO, + this.logger + ); + + SFPLogger.log( + COLOR_HEADER(`package directory: ${COLOR_KEY_MESSAGE(`${this.sfpPackage.packageDescriptor.path}`)}`), + LoggerLevel.INFO, + this.logger + ); + + this.printAdditionalPackageSpecificHeaders(); + + SFPLogger.printHeaderLine('',COLOR_HEADER,LoggerLevel.INFO,this.logger); + } + + abstract printAdditionalPackageSpecificHeaders(); +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateSourcePackageImpl.ts b/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateSourcePackageImpl.ts new file mode 100644 index 000000000..bbef35a16 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateSourcePackageImpl.ts @@ -0,0 +1,117 @@ +import SFPLogger, { LoggerLevel, Logger } from '@flxblio/sfp-logger'; +import { EOL } from 'os'; +import { ApexSortedByType, FileDescriptor } from '../../apex/parser/ApexTypeFetcher'; +import SFPStatsSender from '../../stats/SFPStatsSender'; +import PackageEmptyChecker from '../validators/PackageEmptyChecker'; +import SfpPackage, { PackageType, SfpPackageParams } from '../SfpPackage'; +import { CreatePackage } from './CreatePackage'; +import { PackageCreationParams } from '../SfpPackageBuilder'; +import { ZERO_BORDER_TABLE } from '../../display/TableConstants'; +import { COLOR_INFO } from '@flxblio/sfp-logger'; +import { COLOR_HEADER } from '@flxblio/sfp-logger'; +import { COLOR_WARNING } from '@flxblio/sfp-logger'; +const Table = require('cli-table'); + +export default class CreateSourcePackageImpl extends CreatePackage { + public constructor( + protected projectDirectory: string, + protected sfpPackage: SfpPackage, + protected packageCreationParams: PackageCreationParams, + protected logger?: Logger, + protected params?: SfpPackageParams + ) { + super(projectDirectory, sfpPackage, packageCreationParams, logger, params); + } + + getTypeOfPackage() { + return PackageType.Source; + } + + printAdditionalPackageSpecificHeaders() {} + + isEmptyPackage(projectDirectory: string, packageDirectory: string) { + return PackageEmptyChecker.isEmptyFolder(projectDirectory, packageDirectory); + } + + preCreatePackage(sfpPackage: SfpPackage) {} + + public async createPackage(sfpPackage: SfpPackage) { + this.handleApexTestClasses(sfpPackage); + + SFPStatsSender.logGauge('package.metadatacount', sfpPackage.metadataCount, { + package: sfpPackage.packageName, + type: sfpPackage.packageType, + }); + } + + postCreatePackage(sfpPackage) {} + + protected handleApexTestClasses(sfpPackage: SfpPackage) { + let classTypes: ApexSortedByType = sfpPackage.apexClassesSortedByTypes; + + if (sfpPackage.isApexFound && classTypes?.testClass?.length == 0) { + this.printSlowDeploymentWarning(); + sfpPackage.isTriggerAllTests = true; + } else if (sfpPackage.isApexFound && classTypes?.testClass?.length > 0) { + if (classTypes?.parseError?.length > 0) { + SFPLogger.printHeaderLine('',COLOR_HEADER,LoggerLevel.INFO,this.logger); + SFPLogger.log( + 'Unable to parse these classes to correctly identify test classes, Its not your issue, its ours!'+ + 'Please raise a issue in our repo!', + LoggerLevel.INFO, + this.logger + ); + this.printClassesIdentified(classTypes?.parseError); + sfpPackage.isTriggerAllTests = true; + } else { + this.printHintForOptimizedDeployment(); + sfpPackage.isTriggerAllTests = false; + this.printClassesIdentified(classTypes?.testClass); + sfpPackage.apexTestClassses = []; + classTypes?.testClass.forEach((element) => { + sfpPackage.apexTestClassses.push(element.name); + }); + } + } + } + + private printHintForOptimizedDeployment() { + SFPLogger.printHeaderLine('OPTION FOR DEPLOYMENT OPTIMIZATION AVAILABLE',COLOR_HEADER,LoggerLevel.INFO,this.logger); + SFPLogger.log( + `Following apex test classes were identified and can be used for deploying this package,${EOL}` + + `in an optimal manner, provided each individual class meets the test coverage requirement of 75% and above${EOL}` + + `Ensure each apex class/trigger is validated for coverage in the validation stage`, + null, + this.logger + ); + SFPLogger.printHeaderLine('',COLOR_HEADER,LoggerLevel.INFO,this.logger); + } + + private printSlowDeploymentWarning() { + SFPLogger.printHeaderLine('WARNING! YOU MIGHT NOT BE ABLE TO DEPLOY OR WILL HAVE A SLOW DEPLOYMENT',COLOR_WARNING,LoggerLevel.INFO,this.logger); + SFPLogger.log( + `This package has apex classes/triggers, however apex test classes were not found, You would not be able to deploy` + + `to production org optimally if each class do not have coverage of 75% and above,We will attempt deploying` + + `this package by triggering all local tests in the org which could be realy costly in terms of deployment time!`, + null, + this.logger + ); + SFPLogger.printHeaderLine('',COLOR_HEADER,LoggerLevel.INFO,this.logger); + } + + private printClassesIdentified(fetchedClasses: FileDescriptor[]) { + if (fetchedClasses === null || fetchedClasses === undefined) return; + + let table = new Table({ + head: ['Class', 'Error'], + chars: ZERO_BORDER_TABLE + }); + + for (let fetchedClass of fetchedClasses) { + let item = [fetchedClass.name, fetchedClass.error ? JSON.stringify(fetchedClass.error) : 'N/A']; + table.push(item); + } + SFPLogger.log('Following apex test classes were identified', LoggerLevel.INFO, this.logger); + SFPLogger.log(table.toString(), LoggerLevel.INFO, this.logger); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateUnlockedPackageImpl.ts b/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateUnlockedPackageImpl.ts new file mode 100644 index 000000000..3d15823b1 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageCreators/CreateUnlockedPackageImpl.ts @@ -0,0 +1,262 @@ +import ProjectConfig from '../../project/ProjectConfig'; +import SFPLogger, { LoggerLevel, Logger, COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; +import * as fs from 'fs-extra'; +import { delay } from '../../utils/Delay'; +import SfpPackage, { PackageType, SfpPackageParams } from '../SfpPackage'; +import { CreatePackage } from './CreatePackage'; +import PackageEmptyChecker from '../validators/PackageEmptyChecker'; +import PackageVersionCoverage from '../coverage/PackageVersionCoverage'; +import { Connection, SfProject } from '@salesforce/core'; +import SFPStatsSender from '../../stats/SFPStatsSender'; +import { EOL } from 'os'; +import SFPOrg, { PackageTypeInfo } from '../../org/SFPOrg'; +import { PackageCreationParams } from '../SfpPackageBuilder'; +import { PackageVersion, PackageVersionCreateRequestResult } from '@salesforce/packaging'; +import { Duration } from '@salesforce/kit'; +import PackageDependencyDisplayer from '../../display/PackageDependencyDisplayer'; +const path = require('path'); + +export default class CreateUnlockedPackageImpl extends CreatePackage { + private static packageTypeInfos: PackageTypeInfo[]; + private isOrgDependentPackage: boolean = false; + private connection: Connection; + private devhubOrg: SFPOrg; + workingDirectory: string; + + public constructor( + protected projectDirectory: string, + protected sfpPackage: SfpPackage, + protected packageCreationParams: PackageCreationParams, + protected logger?: Logger, + protected params?: SfpPackageParams + ) { + super(projectDirectory, sfpPackage, packageCreationParams, logger, params); + } + + getTypeOfPackage() { + return PackageType.Unlocked; + } + + async preCreatePackage(sfpPackage: SfpPackage) { + this.devhubOrg = await SFPOrg.create({ aliasOrUsername: this.packageCreationParams.devHub }); + + this.connection = this.devhubOrg.getConnection(); + + let packageId = ProjectConfig.getPackageId(sfpPackage.projectConfig, this.sfpPackage.packageName); + + // Get working directory + this.workingDirectory = sfpPackage.workingDirectory; + + //Get the one in working directory, this is always hardcoded to match + this.params.configFilePath = path.join('config', 'project-scratch-def.json'); + + //Get Type of Package + SFPLogger.log('Fetching Package Type Info from DevHub', LoggerLevel.INFO, this.logger); + let packageTypeInfos = await this.getPackageTypeInfos(); + let packageTypeInfo = packageTypeInfos.find((pkg) => pkg.Id == packageId); + if (packageTypeInfo == null) { + SFPLogger.log( + 'Unable to find a package info for this particular package, Are you sure you created this package?', + LoggerLevel.WARN, + this.logger + ); + throw new Error('Unable to fetch Package Info'); + } + + if (packageTypeInfo.IsOrgDependent == 'Yes') this.isOrgDependentPackage = true; + + SFPLogger.log(`Package ${packageTypeInfo.Name}`, LoggerLevel.INFO, this.logger); + SFPLogger.log(`IsOrgDependent ${packageTypeInfo.IsOrgDependent}`, LoggerLevel.INFO, this.logger); + SFPLogger.log(`Package Id ${packageTypeInfo.Id}`, LoggerLevel.INFO, this.logger); + SFPLogger.log('-------------------------', LoggerLevel.INFO, this.logger); + + //cleanup sfp constructs in working directory + this.deletesfpAdditionsToProjectConfig(this.workingDirectory); + + //Resolve the package dependencies + if (this.isOrgDependentPackage) { + // Store original dependencies to artifact + sfpPackage.dependencies = sfpPackage.packageDescriptor['dependencies']; + } else if (!this.isOrgDependentPackage && !this.packageCreationParams.isSkipValidation) { + sfpPackage.packageDescriptor = ProjectConfig.getSFDXPackageDescriptor( + this.workingDirectory, + this.sfpPackage.packageName + ); + //Store the resolved dependencies + sfpPackage.dependencies = sfpPackage.packageDescriptor['dependencies']; + } else { + sfpPackage.dependencies = sfpPackage.packageDescriptor['dependencies']; + } + + //Print Dependencies + PackageDependencyDisplayer.printPackageDependencies(sfpPackage.dependencies,sfpPackage.projectConfig, this.logger); + + } + + async createPackage(sfpPackage: SfpPackage) { + const sfProject = await SfProject.resolve(this.workingDirectory); + + // fix for #1202 + // bug packaging lib doesnt support unpackaged metadata from working directory which is not the root + // it keeps on searching for the unpackage in the root folder + // so fix up the path manually + let targetPackageDir = sfProject.getPackageDirectories()[0]; + if (targetPackageDir['unpackagedMetadata']) + targetPackageDir['unpackagedMetadata'] = { path: path.join(this.workingDirectory, 'unpackagedMetadata') }; + + let result = await PackageVersion.create( + { + connection: this.devhubOrg.getConnection(), + project: sfProject, + installationkey: this.packageCreationParams.installationkey, + installationkeybypass: this.packageCreationParams.installationkeybypass, + tag: sfpPackage.tag, + skipvalidation: + this.packageCreationParams.isSkipValidation && !this.isOrgDependentPackage ? true : false, + codecoverage: + this.packageCreationParams.isCoverageEnabled && !this.isOrgDependentPackage ? true : false, + versionnumber: sfpPackage.versionNumber, + definitionfile: path.join(this.workingDirectory, this.params.configFilePath), + packageId: this.sfpPackage.packageName, + }, + { timeout: Duration.minutes(0), frequency: Duration.seconds(30) } + ); + + SFPLogger.log(`Package creation for ${this.sfpPackage.packageName} Initiated`, LoggerLevel.INFO, this.logger); + //Poll for package creation every 30 seconds + let currentPackageCreationStatus: PackageVersionCreateRequestResult; + while (true) { + await delay(30000); //Poll every 30 seconds + currentPackageCreationStatus = await PackageVersion.getCreateStatus( + result.Id, + this.devhubOrg.getConnection() + ); + + //Too Verbose when reading errors.. use only for debug + SFPLogger.log( + `Status: ${COLOR_KEY_MESSAGE(currentPackageCreationStatus.Status)}, Next Status check in 30 seconds`, + LoggerLevel.DEBUG, + this.logger + ); + if (currentPackageCreationStatus.Status === `Success`) { + break; + } else if (currentPackageCreationStatus.Status === 'Error') { + let errorMessage = ''; + const errors = currentPackageCreationStatus?.Error; + if (errors?.length) { + errorMessage = 'Creation errors: '; + for (let i = 0; i < errors.length; i++) { + errorMessage += `\n${i + 1}) ${errors[i]}`; + } + } + throw new Error(`Unable to create ${this.sfpPackage.packageName} due to \n` + errorMessage); + } + } + + SFPLogger.log(`Package Result:${JSON.stringify(currentPackageCreationStatus)}`, LoggerLevel.TRACE, this.logger); + + //Get the full details on the package and throw an error if the result is null, usually when the comamnd is timed out + if (currentPackageCreationStatus.SubscriberPackageVersionId) { + sfpPackage.package_version_id = currentPackageCreationStatus.SubscriberPackageVersionId; + await this.getPackageInfo(sfpPackage); + } else { + throw new Error( + `The build for ${this.sfpPackage.packageName} was not completed in the wait time ${this.packageCreationParams.waitTime} provided.${EOL} + You might want to increase the wait time or better check the dependencies or convert to different package type ${EOL} + Read more about it here https://docs.flxblio.io/development-practices/types-of-packaging/unlocked-packages#build-options-with-unlocked-packages` + ); + } + + //Break if coverage is low + if (this.packageCreationParams.isCoverageEnabled && !this.isOrgDependentPackage) { + if (!sfpPackage.has_passed_coverage_check) + throw new Error('This package has not meet the minimum coverage requirement of 75%'); + } + } + + postCreatePackage(sfpPackage: SfpPackage) { + //copy the original config back as existing one would have cleaned up + fs.copyFileSync( + path.join(this.workingDirectory, 'sfdx-project-bak.json'), + path.join(this.workingDirectory, 'sfdx-project.json') + ); + fs.unlinkSync(path.join(this.workingDirectory, 'sfdx-project-bak.json')); + if (sfpPackage.isDependencyValidated) { + SFPStatsSender.logGauge('package.testcoverage', sfpPackage.test_coverage, { + package: sfpPackage.package_name, + from: 'createpackage', + }); + } + } + + isEmptyPackage(projectDirectory: string, packageDirectory: string) { + return PackageEmptyChecker.isEmptyFolder(projectDirectory, packageDirectory); + } + + printAdditionalPackageSpecificHeaders() {} + + private deletesfpAdditionsToProjectConfig(workingDirectory: string) { + let projectManifestFromWorkingDirectory = ProjectConfig.getSFDXProjectConfig(workingDirectory); + let packageDescriptorInWorkingDirectory = ProjectConfig.getPackageDescriptorFromConfig( + this.sfpPackage.packageName, + projectManifestFromWorkingDirectory + ); + + fs.writeJsonSync(path.join(workingDirectory, 'sfdx-project-bak.json'), projectManifestFromWorkingDirectory); + + //Cleanup sfp constructs + if (this.isOrgDependentPackage) delete packageDescriptorInWorkingDirectory['dependencies']; + + delete packageDescriptorInWorkingDirectory['type']; + delete packageDescriptorInWorkingDirectory['assignPermSetsPreDeployment']; + delete packageDescriptorInWorkingDirectory['assignPermSetsPostDeployment']; + delete packageDescriptorInWorkingDirectory['skipDeployOnOrgs']; + delete packageDescriptorInWorkingDirectory['skipTesting']; + delete packageDescriptorInWorkingDirectory['skipCoverageValidation']; + delete packageDescriptorInWorkingDirectory['ignoreOnStages']; + delete packageDescriptorInWorkingDirectory['ignoreDeploymentErrors']; + delete packageDescriptorInWorkingDirectory['preDeploymentScript']; + delete packageDescriptorInWorkingDirectory['postDeploymentScript']; + delete packageDescriptorInWorkingDirectory['aliasfy']; + delete packageDescriptorInWorkingDirectory['checkpointForPrepare']; + delete packageDescriptorInWorkingDirectory['testSynchronous']; + delete packageDescriptorInWorkingDirectory['tags']; + + fs.writeJsonSync(path.join(workingDirectory, 'sfdx-project.json'), projectManifestFromWorkingDirectory); + } + + private async getPackageInfo(sfpPackage: SfpPackage) { + let packageVersionCoverage: PackageVersionCoverage = new PackageVersionCoverage(this.connection, this.logger); + let count = 0; + while (count < 10) { + count++; + try { + SFPLogger.log('Fetching Version Number and Coverage details', LoggerLevel.INFO, this.logger); + + let pkgInfoResult = await packageVersionCoverage.getCoverage(sfpPackage.package_version_id); + + sfpPackage.isDependencyValidated = !this.packageCreationParams.isSkipValidation; + sfpPackage.package_version_number = pkgInfoResult.packageVersionNumber; + sfpPackage.test_coverage = pkgInfoResult.coverage; + sfpPackage.has_passed_coverage_check = pkgInfoResult.HasPassedCodeCoverageCheck; + break; + } catch (error) { + SFPLogger.log( + `Unable to fetch package version info due to ${error.message}`, + LoggerLevel.INFO, + this.logger + ); + SFPLogger.log('Retrying...', LoggerLevel.INFO, this.logger); + await delay(2000); + continue; + } + } + } + + private async getPackageTypeInfos() { + if (CreateUnlockedPackageImpl.packageTypeInfos == null) { + CreateUnlockedPackageImpl.packageTypeInfos = await this.devhubOrg.listAllPackages(); + } + return CreateUnlockedPackageImpl.packageTypeInfos; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageFormatConvertors/SourceToMDAPIConvertor.ts b/packages/sfpowerscripts-cli/src/core/package/packageFormatConvertors/SourceToMDAPIConvertor.ts new file mode 100644 index 000000000..c58e992d7 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageFormatConvertors/SourceToMDAPIConvertor.ts @@ -0,0 +1,52 @@ +import { ComponentSet, MetadataConverter } from '@salesforce/source-deploy-retrieve'; +import path from 'path'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; + +export default class SourceToMDAPIConvertor { + public constructor( + private projectDirectory: string, + private sourceDirectory: string, + private sourceApiVersion: string, + private logger?: Logger + ) {} + + public async convert() { + let mdapiDir = `.sfp/${this.makefolderid(5)}_mdapi`; + //Create destination directory + if (this.projectDirectory != null) mdapiDir = path.resolve(this.projectDirectory, mdapiDir); + + //Source Directory is nested inside project directory when used with artifacts + //TODO: projectDirectory nomenclature is incorrect, should be parentDirectory perhaps? + let resolvedSourceDirectory = this.sourceDirectory; + if (this.projectDirectory != null) + resolvedSourceDirectory = path.resolve(this.projectDirectory, this.sourceDirectory); + + //Build component set from provided source directory + let componentSet = ComponentSet.fromSource({ + fsPaths: [resolvedSourceDirectory], + }); + + if (this.sourceApiVersion) componentSet.sourceApiVersion = this.sourceApiVersion; + + const converter = new MetadataConverter(); + let convertResult = await converter.convert(componentSet, 'metadata', { + type: 'directory', + outputDirectory: mdapiDir, + }); + SFPLogger.log(`Source converted successfully to ${mdapiDir}`, LoggerLevel.TRACE, this.logger); + SFPLogger.log(`ConvertResult:` + JSON.stringify(convertResult), LoggerLevel.TRACE, this.logger); + + return convertResult; + } + + private makefolderid(length): string { + var result = ''; + var characters = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charactersLength = characters.length; + for (var i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallDataPackageImpl.ts b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallDataPackageImpl.ts new file mode 100644 index 000000000..9d4af0579 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallDataPackageImpl.ts @@ -0,0 +1,104 @@ +const fs = require('fs-extra'); +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import SFDMURunImpl from '../../sfdmuwrapper/SFDMURunImpl'; +import VlocityPackDeployImpl from '../../vlocitywrapper/VlocityPackDeployImpl'; +import { SFDXCommand } from '@flxblio/sfdx-process-wrapper/lib/SFDXCommand'; +const path = require('path'); +import OrgDetailsFetcher from '../../org/OrgDetailsFetcher'; +import { InstallPackage, SfpPackageInstallationOptions } from './InstallPackage'; +import SfpPackage from '../SfpPackage'; +import SFPOrg from '../../org/SFPOrg'; + +export default class InstallDataPackageImpl extends InstallPackage { + public constructor( + sfpPackage: SfpPackage, + targetOrg:SFPOrg, + logger: Logger, + options: SfpPackageInstallationOptions, + ) { + super(sfpPackage, targetOrg, logger,options); + } + + public async install() { + try { + //Fetch the sfdxcommand executor for the type + let dataPackageDeployer: SFDXCommand = await this.getSFDXCommand( + this.sfpPackage.sourceDir, + this.packageDirectory + ); + + SFPLogger.log(`Executing installation command: ${dataPackageDeployer.getGeneratedSFDXCommandWithParams()}`,LoggerLevel.INFO,this.logger); + let result = await dataPackageDeployer.exec(false); + + SFPLogger.log(result, LoggerLevel.INFO, this.logger); + } catch (error) { + let csvIssuesReportFilepath: string = path.join( + this.sfpPackage.sourceDir, + this.packageDirectory, + `CSVIssuesReport.csv` + ); + if (fs.existsSync(csvIssuesReportFilepath)) { + SFPLogger.log( + `\n---------------------WARNING: SFDMU detected CSV issues, verify the following files -------------------------------`, + LoggerLevel.WARN, + this.logger + ); + SFPLogger.log(fs.readFileSync(csvIssuesReportFilepath, 'utf8'), LoggerLevel.INFO, this.logger); + } + throw error; + } + } + private async getSFDXCommand(sourceDirectory: string, packageDirectory: string): Promise { + //Determine package type + let packageType: string = this.determinePackageType(path.join(sourceDirectory, packageDirectory)); + + //Pick the type of SFDX command to use + let dataPackageDeployer: SFDXCommand; + if (packageType === 'sfdmu') { + let orgDomainUrl = await new OrgDetailsFetcher( this.sfpOrg.getUsername()).getOrgDomainUrl(); + + dataPackageDeployer = new SFDMURunImpl( + sourceDirectory, + this.sfpOrg.getUsername(), + orgDomainUrl, + packageDirectory, + this.logger, + LoggerLevel.INFO + ); + } else if (packageType === 'vlocity') { + dataPackageDeployer = new VlocityPackDeployImpl( + this.sfpPackage.sourceDir, + this.sfpOrg.getUsername(), + packageDirectory, + null, + null + ); + } else { + throw new Error('Unsupported package type'); + } + + return dataPackageDeployer; + } + + private determinePackageType(packageDirectory: string): string { + if (fs.pathExistsSync(path.join(packageDirectory, 'export.json'))) { + SFPLogger.log( + `Found export.json in ${packageDirectory}.. Utilizing it as data package and will be deployed using sfdmu`, + LoggerLevel.INFO, + this.logger + ); + return 'sfdmu'; + } else if (fs.pathExistsSync(path.join(packageDirectory, 'VlocityComponents.yaml'))) { + SFPLogger.log( + `Found VlocityComponents.yaml in ${packageDirectory}.. Utilizing it as data package and will be deployed using vbt`, + LoggerLevel.INFO, + this.logger + ); + return 'vlocity'; + } else { + throw new Error( + `Could not find export.json or VlocityComponents.yaml in ${packageDirectory}. sfp only support vlocity or sfdmu based data packages` + ); + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallPackage.ts b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallPackage.ts new file mode 100644 index 000000000..304a5e2bd --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallPackage.ts @@ -0,0 +1,497 @@ +import SFPLogger, { COLOR_KEY_MESSAGE, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { PackageInstallationResult, PackageInstallationStatus } from './PackageInstallationResult'; +import ProjectConfig from '../../project/ProjectConfig'; +import SFPStatsSender from '../../stats/SFPStatsSender'; +import AssignPermissionSets from '../../permsets/AssignPermissionSets'; +import ScriptExecutor from '../../scriptExecutor/ScriptExecutorHelpers'; +import { Connection } from '@salesforce/core'; +import * as fs from 'fs-extra'; +import FileSystem from '../../utils/FileSystem'; +import OrgDetailsFetcher, { OrgDetails } from '../../org/OrgDetailsFetcher'; +import path = require('path'); +import PermissionSetGroupUpdateAwaiter from '../../permsets/PermissionSetGroupUpdateAwaiter'; +import SfpOrg from '../../org/SFPOrg'; +import SfpPackage, { PackageType } from '../SfpPackage'; +import DeploymentExecutor, { DeploySourceResult, DeploymentType } from '../../deployers/DeploymentExecutor'; +import DeploySourceToOrgImpl, { DeploymentOptions } from '../../deployers/DeploySourceToOrgImpl'; +import getFormattedTime from '../../utils/GetFormattedTime'; +import { TestLevel } from '../../apextest/TestOptions'; +import { PostDeployersRegistry } from '../deploymentCustomizers/PostDeployersRegistry'; +import { ComponentSet } from '@salesforce/source-deploy-retrieve'; +import PackageComponentPrinter from '../../display/PackageComponentPrinter'; +import DeployErrorDisplayer from '../../display/DeployErrorDisplayer'; +import { PreDeployersRegistry } from '../deploymentCustomizers/PreDeployersRegistry'; +import { AnalyzerRegistry } from '../../package/analyser/AnalyzerRegistry'; + +export class SfpPackageInstallationOptions { + installationkey?: string; + apexcompile?: string = 'package'; + securitytype?: string = 'none'; + upgradetype?: string = 'mixed-mode'; + waitTime?: string; + apiVersion?: string; + publishWaitTime?: number = 60; + skipTesting?: boolean; + optimizeDeployment?: boolean; + deploymentType?: DeploymentType; + disableArtifactCommit?: boolean = false; + isInstallingForValidation?: boolean; + skipIfPackageInstalled: boolean; + isDryRun?: boolean = false; + pathToReplacementForceIgnore?: string; +} + +export abstract class InstallPackage { + protected connection: Connection; + protected packageDescriptor; + protected packageDirectory; + + private _isArtifactToBeCommittedInOrg: boolean = true; + + public constructor( + protected sfpPackage: SfpPackage, + protected sfpOrg: SfpOrg, + protected logger: Logger, + protected options: SfpPackageInstallationOptions + ) { } + + public async exec(): Promise { + let startTime = Date.now(); + let elapsedTime: number; + try { + this.packageDescriptor = ProjectConfig.getSFDXPackageDescriptor( + this.sfpPackage.sourceDir, + this.sfpPackage.packageName + ); + + this.connection = this.sfpOrg.getConnection(); + + if (await this.isPackageToBeInstalled(this.options.skipIfPackageInstalled)) { + if (!this.options.isDryRun) { + await this.waitTillAllPermissionSetGroupIsUpdated(); + await this.assignPermsetsPreDeployment(); + await this.executePreDeploymentScripts(); + await this.setPackageDirectoryForPackage(); + await this.executePreDeployers(); + await this.checkPackageDirectoryExists(); + await this.install(); + await this.assignPermsetsPostDeployment(); + await this.executePostDeployers(); + await this.executePostDeploymentScript(); + await this.commitPackageInstallationStatus(); + + elapsedTime = Date.now() - startTime; + this.sendMetricsWhenSuccessfullyInstalled(elapsedTime); + } + return { result: PackageInstallationStatus.Succeeded, elapsedTime: elapsedTime }; + } else { + SFPLogger.log('Skipping Package Installation', LoggerLevel.INFO, this.logger); + return { result: PackageInstallationStatus.Skipped }; + } + } catch (error) { + elapsedTime = Date.now() - startTime; + this.sendMetricsWhenFailed(elapsedTime); + return { + result: PackageInstallationStatus.Failed, + message: error.message, + elapsedTime: elapsedTime, + }; + } + } + + checkPackageDirectoryExists() { + let absPackageDirectory: string = path.join(this.sfpPackage.sourceDir, this.packageDirectory); + if (!fs.existsSync(absPackageDirectory)) { + throw new Error(`Package directory ${absPackageDirectory} does not exist`); + } + } + + private async waitTillAllPermissionSetGroupIsUpdated() { + try { + //Package Has Permission Set Group + let permissionSetGroupUpdateAwaiter: PermissionSetGroupUpdateAwaiter = new PermissionSetGroupUpdateAwaiter( + this.connection, + this.logger + ); + await permissionSetGroupUpdateAwaiter.waitTillAllPermissionSetGroupIsUpdated(); + } catch (error) { + //Ignore error + // Lets try proceeding + SFPLogger.log( + `Unable to check the status of Permission Set Groups due to ${error}`, + LoggerLevel.WARN, + this.logger + ); + } + } + + protected async setPackageDirectoryForPackage() { + if (this.packageDescriptor.aliasfy) { + const searchDirectory = path.join(this.sfpPackage.sourceDir, this.packageDescriptor.path); + const files = FileSystem.readdirRecursive(searchDirectory, true); + + let aliasDir: string; + + let alias = await this.sfpOrg.getAlias(); + aliasDir = files.find( + (file) => path.basename(file) === alias && fs.lstatSync(path.join(searchDirectory, file)).isDirectory() + ); + + SFPLogger.log(`Using alias directory ${aliasDir ? aliasDir : 'default'}`, LoggerLevel.INFO, this.logger); + + if (!aliasDir) { + const orgDetails = await new OrgDetailsFetcher(this.sfpOrg.getUsername()).getOrgDetails(); + + if (orgDetails.isSandbox) { + // If the target org is a sandbox, find a 'default' directory to use as package directory + aliasDir = files.find( + (file) => + path.basename(file) === 'default' && + fs.lstatSync(path.join(searchDirectory, file)).isDirectory() + ); + } + } + + if (!aliasDir) { + throw new Error( + `Aliasfied package '${this.sfpPackage.packageName}' does not have an alias with '${alias}' or 'default' directory` + ); + } + + this.packageDirectory = path.join(this.packageDescriptor.path, aliasDir); + } + else { + this.packageDirectory = path.join(this.packageDescriptor['path']); + } + + } + + private sendMetricsWhenFailed(elapsedTime: number) { + SFPLogger.log( + `Package ${COLOR_KEY_MESSAGE( + this.sfpPackage.package_name + )} installation attempt failed,it took ${COLOR_KEY_MESSAGE(getFormattedTime(elapsedTime))}` + ); + SFPStatsSender.logCount('package.installation.failure', { + package: this.sfpPackage.package_name, + type: this.sfpPackage.package_type, + target_org: this.sfpOrg.getUsername(), + }); + } + + private sendMetricsWhenSuccessfullyInstalled(elapsedTime: number) { + SFPLogger.log( + `Package ${COLOR_KEY_MESSAGE(this.sfpPackage.package_name)} installation took ${COLOR_KEY_MESSAGE( + getFormattedTime(elapsedTime) + )}`, + LoggerLevel.INFO, + this.logger + ); + SFPStatsSender.logElapsedTime('package.installation.elapsed_time', elapsedTime, { + package: this.sfpPackage.package_name, + type: this.sfpPackage.package_type, + target_org: this.sfpOrg.getUsername(), + }); + SFPStatsSender.logCount('package.installation', { + package: this.sfpPackage.package_name, + type: this.sfpPackage.package_type, + target_org: this.sfpOrg.getUsername(), + }); + } + + //Set this to disable whethere info about the artifact has to be recorded in the org + public set isArtifactToBeCommittedInOrg(toCommit: boolean) { + this._isArtifactToBeCommittedInOrg = toCommit; + } + + private async commitPackageInstallationStatus() { + if (this._isArtifactToBeCommittedInOrg) { + try { + await this.sfpOrg.updateArtifactInOrg(this.logger, this.sfpPackage); + } catch (error) { + SFPLogger.log( + 'Unable to commit information about the package into org..Check whether prerequisities are installed', + LoggerLevel.WARN, + this.logger + ); + } + } + } + + protected async isPackageToBeInstalled(skipIfPackageInstalled: boolean): Promise { + if (skipIfPackageInstalled) { + let installationStatus = await this.sfpOrg.isArtifactInstalledInOrg(this.logger, this.sfpPackage); + return !installationStatus.isInstalled; + } else if(this.sfpPackage.packageType == PackageType.Diff) + { + // If diff package, check if there are any changes to be deployed, else skip + if(!this.sfpPackage.destructiveChanges && this.sfpPackage.metadataCount==0) + { + return false; + } + } + + return true; // Always install packages if skipIfPackageInstalled is false + } + + private async assignPermsetsPreDeployment() { + try { + if (this.sfpPackage.assignPermSetsPreDeployment) { + SFPLogger.log('Assigning permission sets before deployment:', LoggerLevel.INFO, this.logger); + + await AssignPermissionSets.applyPermsets( + this.sfpPackage.assignPermSetsPreDeployment, + this.connection, + this.sfpPackage.sourceDir, + this.logger + ); + } + } catch (error) { + //Proceed ahead not a critical error to break installation + SFPLogger.log(`Unable to assign permsets (Pre Deployment) due to ${error}`, LoggerLevel.WARN, this.logger); + } + } + + public async executePreDeploymentScripts() { + let preDeploymentScript: string = path.join(this.sfpPackage.sourceDir, `scripts`, `preDeployment`); + if (fs.existsSync(preDeploymentScript)) { + let alias = await this.sfpOrg.getAlias(); + SFPLogger.log('Executing preDeployment script', LoggerLevel.INFO, this.logger); + await ScriptExecutor.executeScript( + this.logger, + preDeploymentScript, + this.sfpPackage.packageName, + this.sfpOrg.getUsername(), + alias ? alias : this.sfpOrg.getUsername(), + this.sfpPackage.sourceDir, + this.sfpPackage.packageDirectory + ); + } + } + + abstract install(); + + private async assignPermsetsPostDeployment() { + try { + if (this.sfpPackage.assignPermSetsPostDeployment) { + SFPLogger.log('Assigning permission sets after deployment:', LoggerLevel.INFO, this.logger); + + await AssignPermissionSets.applyPermsets( + this.sfpPackage.assignPermSetsPostDeployment, + this.connection, + this.sfpPackage.sourceDir, + this.logger + ); + } + } catch (error) { + //Proceed ahead not a critical error to break installation + SFPLogger.log(`Unable to assign permsets (Post Deployment) due to ${error}`, LoggerLevel.WARN, this.logger); + } + } + + public async executePostDeploymentScript() { + let postDeploymentScript: string = path.join(this.sfpPackage.sourceDir, `scripts`, `postDeployment`); + if (fs.existsSync(postDeploymentScript)) { + SFPLogger.log('Executing postDeployment script', LoggerLevel.INFO, this.logger); + let alias = await this.sfpOrg.getAlias(); + await ScriptExecutor.executeScript( + this.logger, + postDeploymentScript, + this.sfpPackage.packageName, + this.sfpOrg.getUsername(), + alias ? alias : this.sfpOrg.getUsername(), + this.sfpPackage.sourceDir, + this.sfpPackage.packageDirectory + ); + } + } + + private async executePostDeployers() { + SFPLogger.log(`Executing Post Deployers`, LoggerLevel.INFO, this.logger); + + //Gather componentSet + let componentSet = ComponentSet.fromSource( + path.join(this.sfpPackage.projectDirectory, this.sfpPackage.packageDirectory) + ); + + for (const postDeployer of PostDeployersRegistry.getPostDeployers()) { + try { + if (await postDeployer.isEnabled(this.sfpPackage, this.connection, this.logger)) { + SFPLogger.log( + `Executing Pre Deployer ${COLOR_KEY_MESSAGE(postDeployer.getName())}`, + LoggerLevel.INFO, + this.logger + ); + + await postDeployer.execute( + this.sfpPackage, + componentSet, + this.sfpOrg, + this.logger, + {apiVersion:this.options.apiVersion,waitTime:this.options.waitTime} + ); + + } else { + SFPLogger.log( + `Post Deployer ${COLOR_KEY_MESSAGE(postDeployer.getName())} skipped or not enabled`, + LoggerLevel.INFO, + this.logger + ); + } + } catch (error) { + SFPLogger.log( + `Unable to process post deploy for ${postDeployer.getName()} due to ${error.message}`, + LoggerLevel.WARN, + this.logger + ); + SFPLogger.log( + `Pre Deployer ${COLOR_KEY_MESSAGE(postDeployer.getName())} skipped due to error`, + LoggerLevel.INFO, + this.logger + ); + } + } + } + + private async executePreDeployers() { + SFPLogger.log(`Executing Pre Deployers`, LoggerLevel.INFO, this.logger); + + //Gather componentSet + let componentSet = ComponentSet.fromSource( + path.join(this.sfpPackage.projectDirectory, this.sfpPackage.packageDirectory) + ); + + let analyzers = AnalyzerRegistry.getAnalyzers(); + for (const analyzer of analyzers) { + if(await analyzer.isEnabled(this.sfpPackage, this.logger)) + { + SFPLogger.log(`Executing ${COLOR_KEY_MESSAGE(analyzer.getName())}`, LoggerLevel.INFO, this.logger); + this.sfpPackage = await analyzer.analyze(this.sfpPackage,componentSet, this.logger); + } + else + { + SFPLogger.log(`Skipped ${COLOR_KEY_MESSAGE(analyzer.getName())}`, LoggerLevel.INFO, this.logger); + } + } + + for (const preDeployer of PreDeployersRegistry.getPreDeployers()) { + try { + if (await preDeployer.isEnabled(this.sfpPackage, this.connection, this.logger)) { + SFPLogger.log( + `Executing Pre Deployer ${COLOR_KEY_MESSAGE(preDeployer.getName())}`, + LoggerLevel.INFO, + this.logger + ); + + await preDeployer.execute( + this.sfpPackage, + componentSet, + this.sfpOrg, + this.logger, + {apiVersion:this.options.apiVersion,waitTime:this.options.waitTime} + ); + + } else { + SFPLogger.log( + `Pre Deployer ${COLOR_KEY_MESSAGE(preDeployer.getName())} skipped or not enabled`, + LoggerLevel.INFO, + this.logger + ); + } + } catch (error) { + SFPLogger.log( + `Unable to process pre deploy for ${preDeployer.getName()} due to ${error.message}`, + LoggerLevel.WARN, + this.logger + ); + SFPLogger.log( + `Pre Deployer ${COLOR_KEY_MESSAGE(preDeployer.getName())} skipped due to error`, + LoggerLevel.INFO, + this.logger + ); + } + } + } + + protected async generateDeploymentOptions( + waitTime: string, + optimizeDeployment: boolean, + skipTest: boolean, + target_org: string, + apiVersion: string + ): Promise { + let deploymentOptions: DeploymentOptions = { + ignoreWarnings: true, + waitTime: waitTime, + }; + deploymentOptions.ignoreWarnings = true; + deploymentOptions.waitTime = waitTime; + deploymentOptions.apiVersion = apiVersion; + + //Find Org Type + let orgDetails: OrgDetails; + try { + orgDetails = await new OrgDetailsFetcher(target_org).getOrgDetails(); + } catch (err) { + SFPLogger.log(`Unable to fetch org details,assuming it is production`, LoggerLevel.WARN, this.logger); + orgDetails = { + instanceUrl: undefined, + isScratchOrg: false, + isSandbox: false, + organizationType: undefined, + sfdxAuthUrl: undefined, + status: undefined, + }; + } + + + if (this.options.deploymentType == DeploymentType.MDAPI_DEPLOY && this.sfpPackage.isApexFound && this.options.isInstallingForValidation == false) { + if (orgDetails.isSandbox) { + //enforce during selective deployment + if (skipTest) { + deploymentOptions.testLevel = TestLevel.RunNoTests; + } else if (this.sfpPackage.apexTestClassses.length > 0 && optimizeDeployment) { + deploymentOptions.testLevel = TestLevel.RunSpecifiedTests; + deploymentOptions.specifiedTests = this.getAStringOfSpecificTestClasses( + this.sfpPackage.apexTestClassses + ); + } else { + deploymentOptions.testLevel = TestLevel.RunLocalTests; + } + } else { + if (this.sfpPackage.apexTestClassses.length > 0 && optimizeDeployment) { + deploymentOptions.testLevel = TestLevel.RunSpecifiedTests; + deploymentOptions.specifiedTests = this.getAStringOfSpecificTestClasses( + this.sfpPackage.apexTestClassses + ); + } else { + deploymentOptions.testLevel = TestLevel.RunLocalTests; + } + } + } + // #Issue 1417 + // Handle the use-cases of a not optimized source package validating + else if (this.sfpPackage.packageType == PackageType.Source && this.sfpPackage.isApexFound && this.options.isInstallingForValidation && !optimizeDeployment ) { + if (skipTest) { + deploymentOptions.testLevel = TestLevel.RunNoTests; + } else { + deploymentOptions.testLevel = TestLevel.RunLocalTests; + } + } else { + if (orgDetails.isSandbox) { + deploymentOptions.testLevel = TestLevel.RunNoTests; + } else { + deploymentOptions.testLevel = TestLevel.RunSpecifiedTests; + deploymentOptions.specifiedTests = 'skip'; + } + } + + deploymentOptions.rollBackOnError = true; + return deploymentOptions; + } + + private getAStringOfSpecificTestClasses(apexTestClassses: string[]) { + let specifedTests = apexTestClassses.join(); + return specifedTests; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallSourcePackageImpl.ts b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallSourcePackageImpl.ts new file mode 100644 index 000000000..dee64cf99 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallSourcePackageImpl.ts @@ -0,0 +1,397 @@ +import DeploymentExecutor, { DeploySourceResult, DeploymentType } from '../../deployers/DeploymentExecutor'; +import ReconcileProfileAgainstOrgImpl from '../components/ReconcileProfileAgainstOrgImpl'; +import DeployDestructiveManifestToOrgImpl from '../components/DeployDestructiveManifestToOrgImpl'; +import SFPLogger, { COLOR_SUCCESS, COLOR_WARNING, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import * as fs from 'fs-extra'; +const path = require('path'); +const tmp = require('tmp'); +import { InstallPackage, SfpPackageInstallationOptions } from './InstallPackage'; +import DeploySourceToOrgImpl, { DeploymentOptions } from '../../deployers/DeploySourceToOrgImpl'; +import PackageEmptyChecker from '../validators/PackageEmptyChecker'; +import { TestLevel } from '../../apextest/TestOptions'; +import SfpPackage from '../SfpPackage'; +import SFPOrg from '../../org/SFPOrg'; +import { ComponentSet } from '@salesforce/source-deploy-retrieve'; +import ProjectConfig from '../../project/ProjectConfig'; +import { DeploymentFilterRegistry } from '../deploymentFilters/DeploymentFilterRegistry'; +import DeploymentOptionDisplayer from '../../display/DeploymentOptionDisplayer'; +import PackageComponentPrinter from '../../display/PackageComponentPrinter'; +import DeployErrorDisplayer from '../../display/DeployErrorDisplayer'; +import { COLOR_ERROR } from '@flxblio/sfp-logger'; +import { globSync } from 'glob'; + +export default class InstallSourcePackageImpl extends InstallPackage { + private pathToReplacementForceIgnore: string; + private deploymentType: DeploymentType; + + + + public constructor( + sfpPackage: SfpPackage, + targetOrg: SFPOrg, + options: SfpPackageInstallationOptions, + logger: Logger + ) { + super(sfpPackage, targetOrg, logger, options); + this.options = options; + this.pathToReplacementForceIgnore = options.pathToReplacementForceIgnore; + this.deploymentType = options.deploymentType; + } + + public async install() { + let tmpDirObj = tmp.dirSync({ unsafeCleanup: true }); + let tempDir = tmpDirObj.name; + + try { + //Handle the right force ignore file + this.handleForceIgnores(); + + // Apply Destructive Manifest + await this.applyDestructiveChanges(); + + + //Apply Reconcile if Profiles are found + //To Reconcile we have to go for multiple deploys, first we have to reconcile profiles and deploy the metadata + let isReconcileActivated = false; + let isReconcileErrored = false; + let profileFolders; + ({ + profileFolders, + isReconcileActivated, + isReconcileErrored, + } = await this.reconcileProfilesBeforeDeployment( + this.sfpPackage.sourceDir, + this.sfpOrg.getUsername(), + tempDir + )); + + let deploymentOptions: DeploymentOptions; + let result: DeploySourceResult; + //Construct Deploy Command for actual payload + deploymentOptions = await this.generateDeploymentOptions( + this.options.waitTime, + this.options.optimizeDeployment, + this.options.skipTesting, + this.sfpOrg.getUsername(), + this.options.apiVersion + ); + + //enable source tracking + if (this.deploymentType === DeploymentType.SOURCE_PUSH) { + deploymentOptions.sourceTracking = true; + } + + //Make a copy.. dont mutate sourceDirectory + let resolvedSourceDirectory = this.sfpPackage.sourceDir; + + let emptyCheck = this.handleEmptyPackage(resolvedSourceDirectory, this.packageDirectory); + + if (emptyCheck.isToSkip == true) { + SFPLogger.log( + `${COLOR_SUCCESS(`Skipping the package as there is nothing to be deployed`)}`, + LoggerLevel.INFO, + this.logger + ); + return { + deploy_id: `000000`, + result: true, + message: `Package is empty, nothing to install,skipped`, + }; + } else if (emptyCheck.isToSkip == false) { + + //Create componentSet To Be Deployed + let componentSet = ComponentSet.fromSource( + path.resolve(emptyCheck.resolvedSourceDirectory, this.packageDirectory) + ); + + //Apply Filters + let deploymentFilters = DeploymentFilterRegistry.getImplementations(); + + for (const deploymentFilter of deploymentFilters) { + if ( + deploymentFilter.isToApply( + ProjectConfig.getSFDXProjectConfig(emptyCheck.resolvedSourceDirectory), + this.sfpPackage.packageType + ) + ) + componentSet = await deploymentFilter.apply(this.sfpOrg, componentSet, this.logger); + } + + //Check if there are components to be deployed after filtering + //Assume its successfully deployed + if (componentSet.size == 0) { + return { + deploy_id: `000000`, + result: true, + message: `Package contents were filtered out, nothing to install`, + }; + } + + //Print components inside Component Set + let components = componentSet.getSourceComponents(); + PackageComponentPrinter.printComponentTable(components, this.logger); + + + if (!this.options.isInstallingForValidation) { + DeploymentOptionDisplayer.printDeploymentOptions(deploymentOptions, this.logger); + } + + let deploySourceToOrgImpl: DeploymentExecutor = new DeploySourceToOrgImpl( + this.sfpOrg, + this.sfpPackage.sourceDir, + componentSet, + deploymentOptions, + this.logger + ); + + result = await deploySourceToOrgImpl.exec(); + + if (result.result) { + //Apply PostDeployment Activities + try { + if (isReconcileActivated) { + //Bring back the original profiles, reconcile and redeploy again + await this.reconcileAndRedeployProfiles( + profileFolders, + this.sfpPackage.sourceDir, + this.sfpOrg.getUsername(), + this.packageDirectory, + tempDir, + deploymentOptions + ); + } + } catch (error) { + + SFPLogger.log( + 'Failed to apply reconcile the second time, Partial Metadata applied', + LoggerLevel.INFO, + this.logger + ); + } + } else if (result.result === false) { + DeployErrorDisplayer.displayErrors(result.response, this.logger); + throw new Error(result.message); + } + } + //} + } catch (error) { + tmpDirObj.removeCallback(); + throw error; + } + } + + private handleEmptyPackage( + sourceDirectory: string, + packageDirectory: string + ): { isToSkip: boolean; resolvedSourceDirectory: string } { + //Check empty conditions + let status = PackageEmptyChecker.isToBreakBuildForEmptyDirectory(sourceDirectory, packageDirectory, false); + + + if (status.result == 'break') { + throw new Error('No components in the package, Please check your build again'); + } else if (status.result == 'skip') { + return { + isToSkip: true, + resolvedSourceDirectory: sourceDirectory, + }; + } else { + return { + isToSkip: false, + resolvedSourceDirectory: sourceDirectory, + }; + } + } + + private handleForceIgnores() { + if (this.pathToReplacementForceIgnore) { + this.replaceForceIgnoreInSourceDirectory(this.sfpPackage.sourceDir, this.pathToReplacementForceIgnore); + + + //Handle Diff condition + // if (this.isDiffFolderAvailable) + // this.replaceForceIgnoreInSourceDirectory( + // path.join(this.sfpPackage.sourceDir, 'diff'), + // this.pathToReplacementForceIgnore + // ); + } + } + + private async applyDestructiveChanges() { + + if(this.sfpPackage.destructiveChanges) + { + try { + SFPLogger.log( + 'Attempt to delete components mentioned in destructive manifest', + LoggerLevel.INFO, + this.logger + ); + let deployDestructiveManifestToOrg = new DeployDestructiveManifestToOrgImpl( + this.sfpOrg, + path.join(this.sfpPackage.sourceDir, 'destructive', 'destructiveChanges.xml') + ); + + await deployDestructiveManifestToOrg.exec(); + } catch (error) { + SFPLogger.log( + `We attempted a deletion of components, However we are not successful. \ + Either the components are already deleted or there are components which \ + have dependency to components in the manifest. \ + Please check whether this manifest works! \ + Actual Error Observed: \ + -------------------------------------- \ + ${COLOR_ERROR(error.message)}`, + LoggerLevel.INFO, + this.logger + ); + } + } + } + + private async reconcileProfilesBeforeDeployment(sourceDirectoryPath: string, target_org: string, tempDir: string) { + let profileFolders: any; + let isReconcileActivated: boolean = false; + let isReconcileErrored: boolean = false; + try { + //Hard exit.. no reconcile set in orchestrator + if (this.sfpPackage.reconcileProfiles == false) return { isReconcileActivated: false }; + + //Handle diff for fastfeedback + if (this.sfpPackage.isProfilesFound) { + } else { + return { isReconcileActivated: false }; + } + + SFPLogger.log( + 'Attempting reconcile to profiles as payload contain profiles', + LoggerLevel.INFO, + this.logger + ); + //copy the original profiles to temporary location + profileFolders = globSync('**/profiles', { + cwd: path.join(sourceDirectoryPath), + }); + if (profileFolders.length > 0) { + profileFolders.forEach((folder) => { + fs.copySync(path.join(sourceDirectoryPath, folder), path.join(tempDir, folder)); + }); + } + //Now Reconcile + let reconcileProfileAgainstOrg: ReconcileProfileAgainstOrgImpl = new ReconcileProfileAgainstOrgImpl( + this.sfpOrg, + path.join(sourceDirectoryPath), + this.logger + ); + await reconcileProfileAgainstOrg.exec(); + isReconcileActivated = true; + } catch (err) { + SFPLogger.log('Failed to reconcile profiles:' + err, LoggerLevel.INFO, this.logger); + isReconcileErrored = true; + if (profileFolders.length > 0) { + SFPLogger.log('Restoring original profiles as preprocessing failed', LoggerLevel.INFO, this.logger); + profileFolders.forEach((folder) => { + fs.copySync(path.join(tempDir, folder), path.join(this.sfpPackage.sourceDir, folder)); + }); + } + } + return { profileFolders, isReconcileActivated, isReconcileErrored }; + } + + private async reconcileAndRedeployProfiles( + profileFolders: string[], + sourceDirectoryPath: string, + target_org: string, + sourceDirectory: string, + tmpdir: string, + deploymentOptions: any + ) { + //if no profile supported metadata, no point in + //doing a reconcile + if (this.sfpPackage.isProfilesFound == false) return; + if (this.sfpPackage.isPayLoadContainTypesSupportedByProfiles == false) return; + + + if (profileFolders.length > 0) { + SFPLogger.log(`Restoring original profiles for reconcile and deploy`, LoggerLevel.INFO, this.logger); + profileFolders.forEach((folder) => { + fs.copySync(path.join(tmpdir, folder), path.join(sourceDirectoryPath, folder)); + }); + + //Now Reconcile + let reconcileProfileAgainstOrg: ReconcileProfileAgainstOrgImpl = new ReconcileProfileAgainstOrgImpl( + this.sfpOrg, + sourceDirectoryPath, + this.logger + ); + await reconcileProfileAgainstOrg.exec(); + + //Now deploy the profiles alone + + const profilesDirs = globSync('**/profiles/', { + cwd: path.join(sourceDirectoryPath, sourceDirectory), + absolute: true, + }); + + const profileDeploymentStagingDirectory = path.join( + sourceDirectoryPath, + 'ProfileDeploymentStagingDirectory' + ); + fs.mkdirpSync(path.join(profileDeploymentStagingDirectory, sourceDirectory, 'profiles')); + + for (const dir of profilesDirs) { + // Duplicate profiles are overwritten + fs.copySync(dir, path.join(profileDeploymentStagingDirectory, sourceDirectory, 'profiles')); + } + + fs.copySync( + path.join(sourceDirectoryPath, 'sfdx-project.json'), + path.join(profileDeploymentStagingDirectory, 'sfdx-project.json') + ); + fs.copySync( + path.join(sourceDirectoryPath, '.forceignore'), + path.join(profileDeploymentStagingDirectory, '.forceignore') + ); + + //Create componentSet To Be Deployed + let componentSet = ComponentSet.fromSource( + path.resolve(profileDeploymentStagingDirectory, sourceDirectory) + ); + + DeploymentOptionDisplayer.printDeploymentOptions(deploymentOptions, this.logger); + let deploySourceToOrgImpl: DeploySourceToOrgImpl = new DeploySourceToOrgImpl( + this.sfpOrg, + this.sfpPackage.sourceDir, + componentSet, + deploymentOptions, + this.logger + ); + let profileReconcile: DeploySourceResult = await deploySourceToOrgImpl.exec(); + + if (!profileReconcile.result) { + DeployErrorDisplayer.displayErrors(profileReconcile.response, this.logger); + SFPLogger.log('Unable to deploy reconciled profiles', LoggerLevel.INFO, this.logger); + } + } + } + + + + /** + * Replaces forceignore in source directory with provided forceignore + * @param sourceDirectory + * @param pathToReplacementForceIgnore + */ + private replaceForceIgnoreInSourceDirectory(sourceDirectory: string, pathToReplacementForceIgnore: string): void { + if (fs.existsSync(pathToReplacementForceIgnore)) + fs.copySync(pathToReplacementForceIgnore, path.join(sourceDirectory, '.forceignore')); + else { + SFPLogger.log(`${pathToReplacementForceIgnore} does not exist`, LoggerLevel.INFO, this.logger); + SFPLogger.log( + 'Package installation will continue using the unchanged forceignore in the source directory', + null, + this.logger + ); + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallUnlockedPackage.ts b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallUnlockedPackage.ts new file mode 100644 index 000000000..6635b61e8 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallUnlockedPackage.ts @@ -0,0 +1,91 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import PackageMetadataPrinter from '../../display/PackageMetadataPrinter'; +import { InstallPackage, SfpPackageInstallationOptions } from './InstallPackage'; +import SfpPackage from '../SfpPackage'; +import SFPOrg from '../../org/SFPOrg'; +import InstallUnlockedPackageImpl from './InstallUnlockedPackageImpl'; +import { COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; +import { EOL } from 'os'; + +export default class InstallUnlockedPackage extends InstallPackage { + private packageVersionId; + + public constructor( + sfpPackage: SfpPackage, + targetOrg: SFPOrg, + options: SfpPackageInstallationOptions, + logger: Logger + ) { + super(sfpPackage, targetOrg, logger, options); + this.packageVersionId = sfpPackage.package_version_id; + this.options = options; + } + + public async install() { + let installUnlockedPackageWrapper: InstallUnlockedPackageImpl = new InstallUnlockedPackageImpl( + this.logger, + this.sfpOrg.getUsername(), + this.packageVersionId, + this.options, + this.sfpPackage.packageName + ); + await installUnlockedPackageWrapper.install(this.sfpPackage.payload); + } + + /** + * Checks whether unlocked package version is installed in org. + * Overrides base class method. + * @param skipIfPackageInstalled + * @returns + */ + protected async isPackageToBeInstalled(skipIfPackageInstalled: boolean): Promise { + try { + if (skipIfPackageInstalled) { + SFPLogger.log( + `${EOL}Checking whether package ${COLOR_KEY_MESSAGE( + this.sfpPackage.package_name + )} with ID ${COLOR_KEY_MESSAGE( + this.packageVersionId + )} is installed in ${this.sfpOrg.getUsername()}`, + LoggerLevel.INFO, + this.logger + ); + let installedPackages = await this.sfpOrg.getAllInstalled2GPPackages(); + + let packageFound = installedPackages.find((installedPackage) => { + return installedPackage.subscriberPackageVersionId.substring(0,14) === this.packageVersionId.substring(0,14); + }); + + if (packageFound) { + SFPLogger.log( + `Package to be installed was found in the target org ${this.sfpOrg.getUsername()}`, + LoggerLevel.INFO, + this.logger + ); + return false; + } else { + SFPLogger.log( + `Package to be installed was not found in the target org ${this.sfpOrg.getUsername()}, Proceeding to install.. `, + LoggerLevel.INFO, + this.logger + ); + return true; + } + } else { + SFPLogger.log( + 'Skip if package to be installed is false, Proceeding with installation', + LoggerLevel.INFO, + this.logger + ); + return true; + } + } catch (error) { + SFPLogger.log( + 'Unable to check whether this package is installed in the target org', + LoggerLevel.INFO, + this.logger + ); + return true; + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallUnlockedPackageCollection.ts b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallUnlockedPackageCollection.ts new file mode 100644 index 000000000..34af33f68 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallUnlockedPackageCollection.ts @@ -0,0 +1,134 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import Package2Detail from '../Package2Detail'; +import InstallUnlockedPackageImpl from './InstallUnlockedPackageImpl'; +import SFPOrg from '../../org/SFPOrg'; +import { SfpPackageInstallationOptions } from './InstallPackage'; +import { COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; +import { EOL } from 'os'; + +export default class InstallUnlockedPackageCollection { + private installedPackages: Package2Detail[]; + constructor(private sfpOrg: SFPOrg, private logger: Logger,private dryRun:boolean=false) {} + + public async install( + package2s: Package2Detail[], + skipIfInstalled: boolean, + ignoreErrorIfAHigherVersionPackageIsInstalled: boolean = true + ) { + this.installedPackages = await this.sfpOrg.getAllInstalled2GPPackages(); + + SFPLogger.log(`${EOL}`, LoggerLevel.INFO, this.logger); + + for (const package2 of package2s) { + if ( + package2.subscriberPackageVersionId && + this.isPackageToBeInstalled(skipIfInstalled, package2.subscriberPackageVersionId, package2.name) + ) { + SFPLogger.log( + `Installing Package ${package2.name} in ${this.sfpOrg.getUsername()}`, + LoggerLevel.INFO, + this.logger + ); + let installUnlockedPackageImpl: InstallUnlockedPackageImpl = new InstallUnlockedPackageImpl( + this.logger, + this.sfpOrg.getUsername(), + package2.subscriberPackageVersionId, + new SfpPackageInstallationOptions(), + package2.name + ); + if (package2.key) installUnlockedPackageImpl.setInstallationKey(package2.key); + try { + if(!this.dryRun) + await installUnlockedPackageImpl.install(); + } catch (error) { + let message: string = error.message; + if ( + message.includes(`A newer version of this package is currently installed`) && + ignoreErrorIfAHigherVersionPackageIsInstalled + ) { + SFPLogger.log( + `A higher version of this package is already installed and cant be dowgraded,skipping`, + LoggerLevel.WARN, + this.logger + ); + continue; + } else { + SFPLogger.log( + `Unable to install ${package2.name} in ${this.sfpOrg.getUsername()} due to ${message}`, + LoggerLevel.ERROR, + this.logger + ); + throw error; + } + } + } else { + SFPLogger.log( + `Skipping Installing of package ${COLOR_KEY_MESSAGE( + package2.name + )} in ${this.sfpOrg.getUsername()}`, + LoggerLevel.WARN, + this.logger + ); + } + } + + SFPLogger.log(`${EOL}`, LoggerLevel.INFO, this.logger); + } + + /** + * Checks whether unlocked package version is installed in org. + * Overrides base class method. + * @param skipIfPackageInstalled + * @returns + */ + protected isPackageToBeInstalled( + skipIfPackageInstalled: boolean, + packageVersionId: string, + pacakgeName?: string + ): boolean { + try { + if (skipIfPackageInstalled) { + SFPLogger.log( + `${EOL}Checking whether package ${COLOR_KEY_MESSAGE(pacakgeName)} with ID ${COLOR_KEY_MESSAGE( + packageVersionId)}is installed in ${this.sfpOrg.getUsername()}`, + LoggerLevel.INFO, + this.logger + ); + + let packageFound = this.installedPackages.find((installedPackage) => { + return installedPackage.subscriberPackageVersionId.substring(0,15) === packageVersionId.substring(0,15); + }); + + if (packageFound) { + SFPLogger.log( + `Package to be installed was found in the target org ${this.sfpOrg.getUsername()}`, + LoggerLevel.INFO, + this.logger + ); + return false; + } else { + SFPLogger.log( + `Package to be installed was not found in the target org ${this.sfpOrg.getUsername()}, Proceeding to install.. `, + LoggerLevel.INFO, + this.logger + ); + return true; + } + } else { + SFPLogger.log( + 'Skip if package to be installed is false, Proceeding with installation', + LoggerLevel.INFO, + this.logger + ); + return true; + } + } catch (error) { + SFPLogger.log( + 'Unable to check whether this package is installed in the target org', + LoggerLevel.INFO, + this.logger + ); + return true; + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallUnlockedPackageImpl.ts b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallUnlockedPackageImpl.ts new file mode 100644 index 000000000..41574d826 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/InstallUnlockedPackageImpl.ts @@ -0,0 +1,96 @@ +import SFPLogger, { COLOR_KEY_MESSAGE, COLOR_SUCCESS, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import PackageMetadataPrinter from '../../display/PackageMetadataPrinter'; +import SFPOrg from '../../org/SFPOrg'; +import { PackageInstallCreateRequest, PackagingSObjects, SubscriberPackageVersion } from '@salesforce/packaging'; +import { delay } from '../../utils/Delay'; +import { SfpPackageInstallationOptions } from './InstallPackage'; + + + +export default class InstallUnlockedPackageImpl { + public constructor( + private logger: Logger, + private targetUserName: string, + private packageId: string, + private installationOptions: SfpPackageInstallationOptions, + private packageName?:string + ) { + } + + public setInstallationKey(installationKey: string) { + this.installationOptions.installationkey = installationKey; + } + + public async install(payloadToDisplay?: any): Promise { + let connection = (await SFPOrg.create({ aliasOrUsername: this.targetUserName })).getConnection(); + //Print Metadata carried in the package + if (payloadToDisplay) PackageMetadataPrinter.printMetadataToDeploy(payloadToDisplay, this.logger); + + const subscriberPackageVersion = new SubscriberPackageVersion({ + connection, + aliasOrId: this.packageId, + password: this.installationOptions.installationkey, + }); + + const request: PackageInstallCreateRequest = { + SubscriberPackageVersionKey: await subscriberPackageVersion.getId(), + Password: this.installationOptions.installationkey as PackageInstallCreateRequest['Password'], + ApexCompileType: 'package' as PackageInstallCreateRequest['ApexCompileType'], + SecurityType: this.installationOptions.securitytype as PackageInstallCreateRequest['SecurityType'], + UpgradeType: this.installationOptions.upgradetype as PackageInstallCreateRequest['UpgradeType'], + EnableRss: true, + }; + + //Fire a package installation + let pkgInstallRequest = await subscriberPackageVersion.install(request, {}); + let status = this.parseStatus( + pkgInstallRequest, + this.targetUserName, + this.packageName ? this.packageName : this.packageId, + this.logger + ); + while (status == 'IN_PROGRESS') { + pkgInstallRequest = await SubscriberPackageVersion.getInstallRequest(pkgInstallRequest.Id, connection); + status = this.parseStatus( + pkgInstallRequest, + this.targetUserName, + this.packageName ? this.packageName : this.packageId, + this.logger + ); + await delay(30000); //Poll every 30 seconds + } + } + public parseStatus( + request: PackagingSObjects.PackageInstallRequest, + username: string, + pkgName: string, + logger: Logger + ): 'IN_PROGRESS' | 'SUCCESS' { + const { Status } = request; + if (Status === 'SUCCESS') { + SFPLogger.log( + `Status: ${COLOR_SUCCESS(`Succesfully Installed`)} ${pkgName} to ${username} with Id ${request.Id}`, + LoggerLevel.INFO, + logger + ); + return Status; + } else if (['IN_PROGRESS', 'UNKNOWN'].includes(Status)) { + SFPLogger.log( + `Status: ${COLOR_KEY_MESSAGE(`In Progress`)} Installing ${pkgName} to ${username} with Id ${request.Id}`, + LoggerLevel.INFO, + logger + ); + return 'IN_PROGRESS'; + } else { + let errorMessage = ''; + const errors = request?.Errors?.errors; + if (errors?.length) { + errorMessage = 'Installation errors: '; + for (let i = 0; i < errors.length; i++) { + errorMessage += `\n${i + 1}) ${errors[i].message}`; + } + } + throw new Error(`Unable to install ${pkgName} due to \n` + errorMessage); + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageInstallers/PackageInstallationResult.ts b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/PackageInstallationResult.ts new file mode 100644 index 000000000..584155ed1 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageInstallers/PackageInstallationResult.ts @@ -0,0 +1,15 @@ +export type PackageInstallationResult = { + result: PackageInstallationStatus; + deploy_id?: string; + message?: string; + elapsedTime?:number; + isPreScriptExecutionSuceeded?: boolean; + isPostScriptExecutionSuceeeded?:boolean; + numberOfComponentsDeployed?:number; +}; + +export enum PackageInstallationStatus { + Skipped, + Succeeded, + Failed, +} diff --git a/packages/sfpowerscripts-cli/src/core/package/packageMerger/PackageMergeManager.ts b/packages/sfpowerscripts-cli/src/core/package/packageMerger/PackageMergeManager.ts new file mode 100644 index 000000000..cdf3ac4b2 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/packageMerger/PackageMergeManager.ts @@ -0,0 +1,160 @@ +import SfpPackage, { PackageType } from '../SfpPackage'; +import SfpPackageBuilder from '../../package/SfpPackageBuilder'; +const tmp = require('tmp'); +import * as fs from 'fs-extra'; +const path = require('path'); +import { ComponentSet, MetadataConverter } from '@salesforce/source-deploy-retrieve'; +import { Logger } from '@flxblio/sfp-logger'; + +export default class PackageMergeManager { + public constructor(private sfpPackages: SfpPackage[], private logger?: Logger) {} + + public async mergePackages(targetOrAlias?: string): Promise { + let mergeResult: MergeResult = new MergeResult(); + mergeResult.skippedPackages = []; + mergeResult.unlockedPackages = []; + mergeResult.mergedPackages = []; + + mergeResult.requestedMergeOrder = this.sfpPackages; + + //Use the .sfp directory + let tmpDir = tmp.dirSync({ unsafeCleanup: true }); + let locationOfCopiedDirectory = tmpDir.name; + //Create a temporary folder + let mergedProjectDir = path.join(locationOfCopiedDirectory, `${this.makefolderid(5)}_merged`); + mergeResult.mergedProjectDirectory = mergedProjectDir; + + let mergedPackageDir = path.join(mergedProjectDir, 'force-app'); + fs.mkdirpSync(mergedPackageDir); + + //Create sfdx project.json + fs.writeJSONSync(path.join(mergedProjectDir, 'sfdx-project.json'), this.getMergedProjectManifest(), { + spaces: 4, + }); + + const converter = new MetadataConverter(); + + for (const sfpPackage of this.sfpPackages) { + let componentSet: ComponentSet; + + if (sfpPackage.packageType == PackageType.Data) { + mergeResult.skippedPackages.push(sfpPackage); + continue; + } else if (sfpPackage.packageType == PackageType.Unlocked) { + //Push for now + mergeResult.skippedPackages.push(sfpPackage); + mergeResult.unlockedPackages.push(sfpPackage); + continue; + } else { + //handle alaisfy directory + if (sfpPackage.packageDescriptor.aliasfy) { + let aliasFolder = path.join( + process.cwd(), + sfpPackage.projectDirectory, + sfpPackage.packageDirectory, + targetOrAlias ? targetOrAlias : 'default' + ); + if (fs.existsSync(aliasFolder)) { + componentSet = ComponentSet.fromSource(aliasFolder); + } else { + continue; + } + } else { + componentSet = ComponentSet.fromSource( + path.join(process.cwd(), sfpPackage.projectDirectory, sfpPackage.packageDirectory) + ); + } + + fs.copyFileSync( + path.join(sfpPackage.projectDirectory, 'forceignores', '.buildignore'), + path.join(mergedProjectDir, '.forceignore') + ); + console.log('copied file'); + + //Merge + let results = await converter.convert(componentSet, 'source', { + type: 'merge', + mergeWith: ComponentSet.fromSource(mergedPackageDir).getSourceComponents(), + defaultDirectory: mergedPackageDir, + + forceIgnoredPaths: new Set([ + path.join(process.cwd(), sfpPackage.projectDirectory, 'forceignores', '.buildignore'), + ]), + }); + + for (const component of results.converted) { + if (this.isXmlFileSuffixDuped(component.xml)) { + this.dedupeXmlFileSuffix(component.xml); + } + } + mergeResult.mergedPackages.push(sfpPackage); + } + } + + //Build SfpPackage + if (mergeResult.mergedPackages.length > 0) { + let mergedSfPPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory( + this.logger, + mergeResult.mergedProjectDirectory, + 'merged', + { + branch: 'temp', + packageVersionNumber: '1.0.0.0', + sourceVersion: '00000000', + }, + null + ); + mergeResult.mergedPackage = mergedSfPPackage; + } + + tmpDir.removeCallback(); + return mergeResult; + } + + private isXmlFileSuffixDuped(xmlFile: string): boolean { + return xmlFile.match(/-meta\.xml/g)?.length === 2; + } + + private dedupeXmlFileSuffix(xmlFile: string): string { + let deduped = xmlFile.replace(/-meta\.xml/, ''); + fs.renameSync(xmlFile, deduped); + + return deduped; + } + + private getMergedProjectManifest() { + let projectManifest = { + packageDirectories: [ + { + path: 'force-app', + package: 'merged', + versionNumber: '2.0.0.0', + default: true, + }, + ], + namespace: '', + sourceApiVersion: '53.0', + }; + return projectManifest; + } + + private makefolderid(length): string { + var result = ''; + var characters = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charactersLength = characters.length; + for (var i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; + } +} + +export class MergeResult { + mergedProjectDirectory: string; + mergedPackage: SfpPackage; + mergedPackages: SfpPackage[]; + skippedPackages?: SfpPackage[]; + unlockedPackages?: SfpPackage[]; + requestedMergeOrder: SfpPackage[]; +} diff --git a/packages/sfpowerscripts-cli/src/core/package/promote/PromoteUnlockedPackageImpl.ts b/packages/sfpowerscripts-cli/src/core/package/promote/PromoteUnlockedPackageImpl.ts new file mode 100644 index 000000000..91ea99cef --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/promote/PromoteUnlockedPackageImpl.ts @@ -0,0 +1,34 @@ +import SFPLogger from '@flxblio/sfp-logger'; +import { SfProject } from '@salesforce/core'; +import { PackageSaveResult, PackageVersion } from '@salesforce/packaging'; +import SFPOrg from '../../org/SFPOrg'; + +export default class PromoteUnlockedPackageImpl { + public constructor( + private project_directory: string, + private package_version_id: string, + private devhub_alias: string + ) {} + + public async promote(): Promise { + let hubOrg = await SFPOrg.create({ aliasOrUsername: this.devhub_alias }); + let project = await SfProject.resolve(this.project_directory); + + const packageVersion = new PackageVersion({ + connection: hubOrg.getConnection(), + project: project, + idOrAlias: this.package_version_id, + }); + const packageVersionData = await packageVersion.getData(); + + let result: PackageSaveResult; + try { + result = await packageVersion.promote(); + result.id = packageVersionData.SubscriberPackageVersionId; + } catch (e) { + if (e.message.includes('previously released')) { + SFPLogger.log(`Package ${this.package_version_id} is already promoted, Ignoring`); + } else throw e; + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/AssignPermissionSetFetcher.ts b/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/AssignPermissionSetFetcher.ts new file mode 100644 index 000000000..079f78469 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/AssignPermissionSetFetcher.ts @@ -0,0 +1,23 @@ +import { Logger } from '@flxblio/sfp-logger'; +import SfpPackage from '../SfpPackage'; +import PropertyFetcher from './PropertyFetcher'; + +export default class AssignPermissionSetFetcher implements PropertyFetcher { + public getsfpProperties(packageContents: SfpPackage, packageLogger?: Logger) { + if (packageContents.packageDescriptor.assignPermSetsPreDeployment) { + if (packageContents.packageDescriptor.assignPermSetsPreDeployment instanceof Array) { + packageContents.assignPermSetsPreDeployment = + packageContents.packageDescriptor.assignPermSetsPreDeployment; + } else throw new Error("Property 'assignPermSetsPreDeployment' must be of type array"); + } + + if (packageContents.packageDescriptor.assignPermSetsPostDeployment) { + if (packageContents.packageDescriptor.assignPermSetsPostDeployment instanceof Array) { + packageContents.assignPermSetsPostDeployment = + packageContents.packageDescriptor.assignPermSetsPostDeployment; + } else throw new Error("Property 'assignPermSetsPostDeployment' must be of type array"); + } + + return packageContents; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/DestructiveManifestPathFetcher.ts b/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/DestructiveManifestPathFetcher.ts new file mode 100644 index 000000000..c4b05c5de --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/DestructiveManifestPathFetcher.ts @@ -0,0 +1,29 @@ +import * as fs from 'fs-extra'; +import SfpPackage from '../SfpPackage'; +import PropertyFetcher from './PropertyFetcher'; +import xml2json from '../../utils/xml2json'; +import { Logger } from '@flxblio/sfp-logger'; + +export default class DestructiveManifestPathFetcher implements PropertyFetcher { + public async getsfpProperties(packageContents: SfpPackage, packageLogger?: Logger) { + let destructiveChangesPath: string; + + if (packageContents.packageDescriptor === null || packageContents.packageDescriptor === undefined) { + throw new Error('Project Config (sfdx-project.json) is null'); + } + + if (packageContents.packageDescriptor['destructiveChangePath']) { + destructiveChangesPath = packageContents.packageDescriptor['destructiveChangePath']; + packageContents.destructiveChangesPath = destructiveChangesPath; + } + + try { + if (destructiveChangesPath != null) { + packageContents.destructiveChanges = await xml2json(fs.readFileSync(destructiveChangesPath, 'utf8')); + } + } catch (error) { + throw new Error('Unable to process destructive Manifest specified in the path or in the project manifest'); + } + return packageContents; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/PropertyFetcher.ts b/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/PropertyFetcher.ts new file mode 100644 index 000000000..fbe6c4f8e --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/PropertyFetcher.ts @@ -0,0 +1,11 @@ +import { Logger } from '@flxblio/sfp-logger'; +import SfpPackage from '../SfpPackage'; + +export default interface PropertyFetcher { + /** + * Retrieves property from packageDescriptor and adds its to SfpPackage by reference + * @param packageContents + * @param packageLogger + */ + getsfpProperties(packageContents: SfpPackage, packageLogger?: Logger); +} diff --git a/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.ts b/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.ts new file mode 100644 index 000000000..0e2bc06a0 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.ts @@ -0,0 +1,10 @@ +import SfpPackage from '../SfpPackage'; +import PropertyFetcher from './PropertyFetcher'; + +export default class ReconcilePropertyFetcher implements PropertyFetcher { + getsfpProperties(packageContents: SfpPackage, packageLogger?: any) { + if (packageContents.packageDescriptor.hasOwnProperty('reconcileProfiles')) { + packageContents.reconcileProfiles = packageContents.packageDescriptor.reconcileProfiles; + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/validators/PackageEmptyChecker.ts b/packages/sfpowerscripts-cli/src/core/package/validators/PackageEmptyChecker.ts new file mode 100644 index 000000000..8d05ba782 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/validators/PackageEmptyChecker.ts @@ -0,0 +1,85 @@ +import path from 'path'; +import { readFileSync, existsSync } from 'fs'; +import FileSystem from '../../utils/FileSystem'; +import ignore from 'ignore'; + +export default class PackageEmptyChecker { + public static isToBreakBuildForEmptyDirectory( + projectDir: string, + sourceDirectory: string, + isToBreakBuildIfEmpty: boolean + ): { + message: string; + result: string; + } { + let directoryToCheck; + let status: { message: string; result: string } = { + message: '', + result: '', + }; + + if (projectDir != null) { + directoryToCheck = path.join(projectDir, sourceDirectory); + } else directoryToCheck = sourceDirectory; + + try { + if (!existsSync(directoryToCheck)) { + //Folder do not exists, break build + if (isToBreakBuildIfEmpty) { + status.message = `Folder not Found , Stopping build as isToBreakBuildIfEmpty is ${isToBreakBuildIfEmpty}`; + status.result = 'break'; + } else { + status.message = `Folder not Found , Skipping task as isToBreakBuildIfEmpty is ${isToBreakBuildIfEmpty}`; + status.result = 'skip'; + } + return status; + } else if (PackageEmptyChecker.isEmptyFolder(projectDir, sourceDirectory)) { + if (isToBreakBuildIfEmpty) { + status.message = `Folder is Empty , Stopping build as isToBreakBuildIfEmpty is ${isToBreakBuildIfEmpty}`; + status.result = 'break'; + } else { + status.message = `Folder is Empty, Skipping task as isToBreakBuildIfEmpty is ${isToBreakBuildIfEmpty}`; + status.result = 'skip'; + } + return status; + } else { + status.result = 'continue'; + return status; + } + } catch (err) { + if (err.code === 'ENOENT') { + throw new Error(`No such file or directory ${err.path}`); // Re-throw error if .forceignore does not exist + } else if (!isToBreakBuildIfEmpty) { + status.message = `Something wrong with the path provided ${directoryToCheck}, but skipping, The exception is ${err}`; + status.result = 'skip'; + return status; + } else throw err; + } + } + + public static isEmptyFolder(projectDirectory: string, sourceDirectory: string): boolean { + let dirToCheck; + + if (projectDirectory != null) { + dirToCheck = path.join(projectDirectory, sourceDirectory); + } else { + dirToCheck = sourceDirectory; + } + + let files: string[] = FileSystem.readdirRecursive(dirToCheck, false, false); + // Include source directory in filepaths, as it can be a pattern in forceignore + files = files.map((file) => path.join(sourceDirectory, file)); + + let forceignorePath; + if (projectDirectory != null) forceignorePath = path.join(projectDirectory, '.forceignore'); + else forceignorePath = path.join(process.cwd(), '.forceignore'); + + // Ignore files that are listed in .forceignore + files = ignore() + .add(readFileSync(forceignorePath).toString()) // Add ignore patterns from '.forceignore'. + .filter(files); + + if (files == null || files.length === 0) return true; + else return false; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/version/Package2VersionFetcher.ts b/packages/sfpowerscripts-cli/src/core/package/version/Package2VersionFetcher.ts new file mode 100644 index 000000000..879b84971 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/version/Package2VersionFetcher.ts @@ -0,0 +1,109 @@ +import { Connection } from '@salesforce/core'; +import QueryHelper from '../../queryHelper/QueryHelper'; +import semver from 'semver'; + +/** + * Fetcher for second-generation package version in Dev Hub + */ +export default class Package2VersionFetcher { + private readonly query: string = + 'Select SubscriberPackageVersionId, Package2Id, Package2.Name, IsPasswordProtected, IsReleased, MajorVersion, MinorVersion, PatchVersion, BuildNumber, CodeCoverage, HasPassedCodeCoverageCheck, Branch from Package2Version '; + + constructor(private conn: Connection) {} + + /** + * Fetch Package2 versions by Package2 Id + * Sorts by semantic version, in descending order + * @param package2Id + * @param versionNumber + * @param isValidatedPackages + * @returns + */ + async fetchByPackage2Id( + package2Id: string, + versionNumber?: string, + isValidatedPackages?: boolean + ): Promise { + let query = this.query; + + let whereClause: string = `where Package2Id='${package2Id}' `; + + if (versionNumber) { + // TODO: validate version number + const versions = versionNumber.split('.'); + + if (versions[0]) whereClause += `and MajorVersion=${versions[0]} `; + if (versions[1]) whereClause += `and MinorVersion=${versions[1]} `; + if (versions[2]) whereClause += `and PatchVersion=${versions[2]} `; + if (versions[3]) whereClause += `and BuildNumber=${versions[3]} `; + } + + if (isValidatedPackages) whereClause += `and ValidationSkipped = false `; + + whereClause += `and IsDeprecated = false `; + query += whereClause; + + + const records = await QueryHelper.query(query, this.conn, true); + + + if (records.length > 1) { + return records.sort((a, b) => { + const v1 = `${a.MajorVersion}.${a.MinorVersion}.${a.PatchVersion}-${a.BuildNumber}`; + const v2 = `${b.MajorVersion}.${b.MinorVersion}.${b.PatchVersion}-${b.BuildNumber}`; + return semver.rcompare(v1, v2); + }); + } else return records; + } + + async fetchBySubscriberPackageVersionId(subscriberPackageVersionId: string): Promise { + let query = this.query; + + let whereClause: string = `where SubscriberPackageVersionId='${subscriberPackageVersionId}'`; + query += whereClause; + + const records = await QueryHelper.query(query, this.conn, true); + return records[0]; + } + + async fetchByPackageBranchAndName( + packageBranch: string, + packageName: string, + versionNumber?: string, + ): Promise { + + let query = this.query; + + let whereClause: string = `where Branch='${packageBranch}' and Package2.Name ='${packageName}' `; + if (versionNumber) { + // TODO: validate version number + const versions = versionNumber.split('.'); + if (versions[0]) whereClause += `and MajorVersion=${versions[0]} `; + if (versions[1]) whereClause += `and MinorVersion=${versions[1]} `; + if (versions[2]) whereClause += `and PatchVersion=${versions[2]} `; + } + query += whereClause; + + let orderByClause: string = `order by CreatedDate desc`; + query += orderByClause; + + const records = await QueryHelper.query(query, this.conn, true); + return records; + + } +} + +export interface Package2Version { + SubscriberPackageVersionId: string; + Package2Id: string; + Package2: { Name: string }; + IsPasswordProtected: boolean; + IsReleased: boolean; + MajorVersion: number; + MinorVersion: number; + PatchVersion: number; + BuildNumber: number; + CodeCoverage: { apexCodeCoveragePercentage: number }; + HasPassedCodeCoverageCheck: boolean; + Branch: string; +} diff --git a/packages/sfpowerscripts-cli/src/core/package/version/Package2VersionInstaller.ts b/packages/sfpowerscripts-cli/src/core/package/version/Package2VersionInstaller.ts new file mode 100644 index 000000000..906bb37d0 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/version/Package2VersionInstaller.ts @@ -0,0 +1,26 @@ +import { Logger, LoggerLevel } from '@flxblio/sfp-logger'; + +export default class Package2VersionInstaller { + public constructor( + logger: Logger, + logLevel: LoggerLevel, + working_directory: string, + private targetUserName: string, + private packageId: string, + private waitTime: string, + private publishWaitTime?: string, + private installationkey?: string, + private securityType?: string, + private upgradeType?: string, + private apiVersion?: string, + private apexCompile: string = 'package' + ) {} + + public setInstallationKey(installationKey: string) { + this.installationkey = installationKey; + } + + + + +} diff --git a/packages/sfpowerscripts-cli/src/core/package/version/PackageVersionLister.ts b/packages/sfpowerscripts-cli/src/core/package/version/PackageVersionLister.ts new file mode 100644 index 000000000..9075d5292 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/version/PackageVersionLister.ts @@ -0,0 +1,62 @@ +import { SfProject } from '@salesforce/core'; +import { Package } from '@salesforce/packaging'; +import SFPOrg from '../../org/SFPOrg'; + +export default class PackageVersionLister { + + constructor(private hubOrg:SFPOrg) + { + + } + + public async listAllReleasedVersions(projectDir: string) { + + const sfProject = await SfProject.resolve(projectDir); + + const records = await Package.listVersions(this.hubOrg.getConnection(), sfProject, { + createdLastDays: undefined, + concise: true, + modifiedLastDays: undefined, + packages: [], + isReleased: true, + orderBy: undefined, + verbose: false, + }); + + const results: any[] = []; + + if (records?.length > 0) { + records.forEach((record) => { + results.push({ + Package2Id: record.Package2Id, + Branch: record.Branch, + Tag: record.Tag, + MajorVersion: record.MajorVersion, + MinorVersion: record.MinorVersion, + PatchVersion: record.PatchVersion, + BuildNumber: record.BuildNumber, + Id: record.Id, + SubscriberPackageVersionId: record.SubscriberPackageVersionId, + ConvertedFromVersionId: record.ConvertedFromVersionId, + Name: record.Name, + NamespacePrefix: record.Package2.NamespacePrefix, + Package2Name: record.Package2.Name, + Version: [record.MajorVersion, record.MinorVersion, record.PatchVersion, record.BuildNumber].join( + '.' + ), + IsReleased: record.IsReleased, + CreatedDate: new Date(record.CreatedDate).toISOString().replace('T', ' ').substring(0, 16), + LastModifiedDate: new Date(record.LastModifiedDate) + .toISOString() + .replace('T', ' ') + .substring(0, 16), + ReleaseVersion: + record.ReleaseVersion == null ? '' : Number.parseFloat(record.ReleaseVersion).toFixed(1), + BuildDurationInSeconds: record.BuildDurationInSeconds == null ? '' : record.BuildDurationInSeconds, + }); + }); + } + + return results; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/package/version/PackageVersionUpdater.ts b/packages/sfpowerscripts-cli/src/core/package/version/PackageVersionUpdater.ts new file mode 100644 index 000000000..55786d1b1 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/package/version/PackageVersionUpdater.ts @@ -0,0 +1,18 @@ +import SfpPackage from '../SfpPackage'; + +export default class PackageVersionUpdater { + public constructor() {} + + public substituteBuildNumber(sfpPackage: SfpPackage, buildNumber: string):string { + if (!sfpPackage.versionNumber) { + throw new Error('The package doesnt have a version attribute, Please check your definition'); + } else { + let segments = sfpPackage.versionNumber.split('.'); + let numberToBeAppended = parseInt(buildNumber); + + if (isNaN(numberToBeAppended)) throw new Error('BuildNumber should be a number'); + else segments[3] = buildNumber; + return `${segments[0]}.${segments[1]}.${segments[2]}.${segments[3]}`; + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/permsets/AssignPermissionSets.ts b/packages/sfpowerscripts-cli/src/core/permsets/AssignPermissionSets.ts new file mode 100644 index 000000000..2ff6a45e1 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/permsets/AssignPermissionSets.ts @@ -0,0 +1,17 @@ +import { Connection } from '@salesforce/core'; +import { Logger } from '@flxblio/sfp-logger'; +import AssignPermissionSetsImpl from './AssignPermissionSetsImpl'; + +export default class AssignPermissionSets { + static async applyPermsets(permsets: string[], conn: Connection, sourceDirectory: string, logger: Logger) { + let assignPermissionSetsImpl: AssignPermissionSetsImpl = new AssignPermissionSetsImpl( + conn, + permsets, + sourceDirectory, + logger + ); + + let results = await assignPermissionSetsImpl.exec(); + if (results.failedAssignments.length > 0) throw new Error('Unable to assign permsets'); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/permsets/AssignPermissionSetsImpl.ts b/packages/sfpowerscripts-cli/src/core/permsets/AssignPermissionSetsImpl.ts new file mode 100644 index 000000000..ba0049e0f --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/permsets/AssignPermissionSetsImpl.ts @@ -0,0 +1,93 @@ +import { Connection } from '@salesforce/core'; +import child_process = require('child_process'); +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import PermissionSetFetcher from './PermissionSetFetcher'; +import { ZERO_BORDER_TABLE } from '../display/TableConstants'; +const Table = require('cli-table'); + +export default class AssignPermissionSetsImpl { + constructor( + private conn: Connection, + private permSets: string[], + private project_directory: string, + private packageLogger: Logger + ) {} + + public async exec(): Promise<{ + successfullAssignments: { + username: string; + permset: string; + }[]; + failedAssignments: { + username: string; + permset: string; + }[]; + }> { + let permsetListImpl: PermissionSetFetcher = new PermissionSetFetcher(this.conn.getUsername(), this.conn); + let assignedPermSets = await permsetListImpl.fetchAllPermsetAssignment(); + + let failedAssignments: { + username: string; + permset: string; + }[] = []; + let successfullAssignments: { + username: string; + permset: string; + }[] = []; + + for (let permSet of this.permSets) { + let permSetAssignmentMatch = assignedPermSets.find((record) => { + return record.PermissionSet.Name === permSet; + }); + + if (permSetAssignmentMatch !== undefined) { + // Treat permsets that have already been assigned as successes + successfullAssignments.push({ username: this.conn.getUsername(), permset: permSet }); + continue; + } + + try { + let permsetAssignmentJson: string = child_process.execSync( + `sf org assign permset -n ${permSet} -o ${this.conn.getUsername()} --json`, + { + cwd: this.project_directory, + encoding: 'utf8', + stdio: ['pipe', 'pipe', 'inherit'], + } + ); + + let permsetAssignment = JSON.parse(permsetAssignmentJson); + if (permsetAssignment.status === 0) + successfullAssignments.push({ username: this.conn.getUsername(), permset: permSet }); + else failedAssignments.push({ username: this.conn.getUsername(), permset: permSet }); + } catch (err) { + failedAssignments.push({ username: this.conn.getUsername(), permset: permSet }); + } + } + + if (successfullAssignments.length > 0) { + SFPLogger.log('Successful PermSet Assignments:', LoggerLevel.INFO, this.packageLogger); + this.printPermsetAssignments(successfullAssignments); + } + + if (failedAssignments.length > 0) { + SFPLogger.log('Failed PermSet Assignments', LoggerLevel.INFO, this.packageLogger); + this.printPermsetAssignments(failedAssignments); + } + + return { successfullAssignments, failedAssignments }; + } + + private printPermsetAssignments(assignments: { username: string; permset: string }[]) { + let table = new Table({ + head: ['Username', 'Permission Set Assignment'], + chars: ZERO_BORDER_TABLE + }); + + assignments.forEach((assignment) => { + table.push([assignment.username, assignment.permset]); + }); + + SFPLogger.log(table.toString(), LoggerLevel.INFO, this.packageLogger); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/permsets/PermissionSetFetcher.ts b/packages/sfpowerscripts-cli/src/core/permsets/PermissionSetFetcher.ts new file mode 100644 index 000000000..5133d9958 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/permsets/PermissionSetFetcher.ts @@ -0,0 +1,15 @@ +import { Connection } from '@salesforce/core'; +import QueryHelper from '../queryHelper/QueryHelper'; + +/* + * Retrieve Permsets for a user from a target org + */ +export default class PermissionSetFetcher { + constructor(private username: string, private conn: Connection) {} + + public async fetchAllPermsetAssignment() { + const query = `SELECT Id, PermissionSet.Name, Assignee.Username FROM PermissionSetAssignment WHERE Assignee.Username = '${this.username}'`; + + return QueryHelper.query(query, this.conn, false); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/permsets/PermissionSetGroupUpdateAwaiter.ts b/packages/sfpowerscripts-cli/src/core/permsets/PermissionSetGroupUpdateAwaiter.ts new file mode 100644 index 000000000..87d04b6d7 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/permsets/PermissionSetGroupUpdateAwaiter.ts @@ -0,0 +1,46 @@ +import { Connection } from '@salesforce/core'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import QueryHelper from '../queryHelper/QueryHelper'; +import { delay } from '../utils/Delay'; + +const psGroupQuery = `SELECT Id,MasterLabel,Status FROM PermissionSetGroup WHERE Status = 'Updating'`; + +export default class PermissionSetGroupUpdateAwaiter { + constructor(private connection: Connection, private logger: Logger, private intervalBetweenRepeats = 30000) {} + + async waitTillAllPermissionSetGroupIsUpdated() { + SFPLogger.log( + `Checking status of permission sets group..`, + LoggerLevel.INFO, + this.logger + ); + while (true) { + try { + let records = await QueryHelper.query(psGroupQuery, this.connection, false); + if (records.length > 0) { + SFPLogger.log( + `Pausing deployment as ${records.length} PermissionSetGroups are being updated`, + LoggerLevel.INFO, + this.logger + ); + SFPLogger.log( + `Retrying for status in next ${this.intervalBetweenRepeats / 1000} seconds`, + LoggerLevel.INFO, + this.logger + ); + await delay(this.intervalBetweenRepeats); + } else { + SFPLogger.log( + `Proceeding with deployment, as no PermissionSetGroups are being updated`, + LoggerLevel.INFO, + this.logger + ); + break; + } + } catch (error) { + SFPLogger.log(`Unable to fetch permission group status ${error}`, LoggerLevel.TRACE, this.logger); + throw error; + } + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/project/ProjectConfig.ts b/packages/sfpowerscripts-cli/src/core/project/ProjectConfig.ts new file mode 100644 index 000000000..ebd7e1e20 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/project/ProjectConfig.ts @@ -0,0 +1,281 @@ +const fs = require('fs-extra'); +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; +import _ from 'lodash'; +import { PackageType } from '../package/SfpPackage'; +let path = require('path'); + +/** + * Helper functions for retrieving info from project config + */ +export default class ProjectConfig { + /** + * Returns 0H Id of package from project config + * @param projectConfig + * @param sfdxPackage + */ + public static getPackageId(projectConfig: any, sfdxPackage: string) { + if (projectConfig['packageAliases']?.[sfdxPackage]) { + return projectConfig['packageAliases'][sfdxPackage]; + } else { + throw Error('No Package Id found in sfdx-project.json. Please ensure package alias have the package added'); + } + } + + /** + * Returns package names, as an array of strings + * @param projectDirectory + */ + public static getAllPackages(projectDirectory: string): string[] { + let projectConfig = ProjectConfig.getSFDXProjectConfig(projectDirectory); + let sfdxpackages = []; + projectConfig['packageDirectories'].forEach((pkg) => { + //Only push packages that have package and versionNumber, ignore everything else + if (pkg.package && pkg.versionNumber) sfdxpackages.push(pkg.package); + }); + return sfdxpackages; + } + + public static getAllExternalPackages( + projectConfig: any + ): { alias: string; Package2IdOrSubscriberPackageVersionId: string }[] { + let externalPackages: { alias: string; Package2IdOrSubscriberPackageVersionId: string }[] = []; + let packagesInCurrentDirectory = ProjectConfig.getAllPackageDirectoriesFromConfig(projectConfig); + const packageAliases = projectConfig.packageAliases || {}; + Object.entries(packageAliases).forEach(([key, value]) => { + if ( + !_.find( + packagesInCurrentDirectory, + (elem) => { + return elem.package == key; + }, + 0 + ) + ) + externalPackages.push({ alias: key, Package2IdOrSubscriberPackageVersionId: value as string }); + }); + return externalPackages; + } + + /** + * Returns package names from projectConfig, as an array of strings + * @param projectDirectory + */ + public static getAllPackagesFromProjectConfig(projectConfig: any): string[] { + let sfdxpackages = []; + projectConfig.packageDirectories.forEach((pkg) => { + //Only push packages that have package and versionNumber, ignore everything else + if (pkg.package && pkg.versionNumber) sfdxpackages.push(pkg.package); + }); + return sfdxpackages; + } + + public static getAllPackagesAndItsDependencies( + projectConfig: any + ): Map { + let pkgWithDependencies = new Map(); + let packages = ProjectConfig.getAllPackageDirectoriesFromConfig(projectConfig); + for (let pkg of packages) { + if (pkg.dependencies) { + pkgWithDependencies.set(pkg.package, pkg.dependencies); + } + } + return pkgWithDependencies; + } + + public static getAllPackageDirectoriesFromDirectory(projectDirectory?: string): any[] { + let projectConfig = ProjectConfig.getSFDXProjectConfig(projectDirectory); + let sfdxpackages = []; + projectConfig.packageDirectories?.forEach((pkg) => { + //Only push packages that have package and versionNumber, ignore everything else + if (pkg.package && pkg.versionNumber) sfdxpackages.push(pkg); + }); + return sfdxpackages; + } + + public static getAllPackageDirectoriesFromConfig(projectConfig: any): any[] { + let sfdxpackages = []; + projectConfig.packageDirectories?.forEach((pkg) => { + //Only push packages that have package and versionNumber, ignore everything else + if (pkg.package && pkg.versionNumber) sfdxpackages.push(pkg); + }); + return sfdxpackages; + } + + /** + * Returns package manifest as JSON object + * @param projectDirectory + */ + public static getSFDXProjectConfig(projectDirectory: string): any { + let projectConfigJSON: string; + + if (projectDirectory) { + projectConfigJSON = path.join(projectDirectory, 'sfdx-project.json'); + } else { + projectConfigJSON = 'sfdx-project.json'; + } + + try { + return JSON.parse(fs.readFileSync(projectConfigJSON, 'utf8')); + } catch (error) { + throw new Error(`sfdx-project.json doesn't exist or not readable at ${projectConfigJSON}`); + } + } + + /** + * Returns type of package + * @param projectConfig + * @param sfdxPackage + */ + public static getPackageType( + projectConfig: any, + sfdxPackage: string + ): PackageType.Unlocked | PackageType.Data | PackageType.Source | PackageType.Diff { + let packageDescriptor = ProjectConfig.getPackageDescriptorFromConfig(sfdxPackage, projectConfig); + + if (projectConfig['packageAliases']?.[sfdxPackage]) { + return PackageType.Unlocked; + } else { + if (packageDescriptor.type?.toLowerCase() === PackageType.Data) return PackageType.Data; + else if(packageDescriptor.type?.toLowerCase() === PackageType.Diff) return PackageType.Diff + else + return PackageType.Source; + } + } + + /** + * Returns package descriptor from package manifest at project directory + * @param projectDirectory + * @param sfdxPackage + */ + public static getSFDXPackageDescriptor(projectDirectory: string, sfdxPackage: string): any { + let projectConfig = ProjectConfig.getSFDXProjectConfig(projectDirectory); + + let sfdxPackageDescriptor = ProjectConfig.getPackageDescriptorFromConfig(sfdxPackage, projectConfig); + + return sfdxPackageDescriptor; + } + + /** + * Returns package descriptor from project config JSON object + * @param sfdxPackage + * @param projectConfig + */ + public static getPackageDescriptorFromConfig(sfdxPackage: string, projectConfig: any) { + let sfdxPackageDescriptor: any; + + if (sfdxPackage) { + projectConfig['packageDirectories'].forEach((pkg) => { + if (sfdxPackage == pkg['package']) { + sfdxPackageDescriptor = pkg; + } + }); + } + + if (sfdxPackageDescriptor == null) throw new Error(`Package ${sfdxPackage} does not exist,Please check inputs`); + + return sfdxPackageDescriptor; + } + + /** + * Returns descriptor of default package + * @param projectDirectory + */ + public static getDefaultSFDXPackageDescriptor(projectDirectory: string): any { + let packageDirectory: string; + let sfdxPackageDescriptor: any; + + let projectConfig = this.getSFDXProjectConfig(projectDirectory); + + //Return the default package directory + projectConfig['packageDirectories'].forEach((pkg) => { + if (pkg['default'] == true) { + packageDirectory = pkg['path']; + sfdxPackageDescriptor = pkg; + } + }); + + if (packageDirectory == null) throw new Error('Package or package directory not exist'); + else return sfdxPackageDescriptor; + } + + /** + * Returns pruned package manifest, containing sfdxPackage only + * @param projectDirectory + * @param sfdxPackage + */ + public static cleanupMPDFromProjectDirectory(projectDirectory: string, sfdxPackage: string): any { + const projectConfig = this.getSFDXProjectConfig(projectDirectory); + + return ProjectConfig.cleanupMPDFromProjectConfig(projectConfig, sfdxPackage); + } + + /** + * Returns pruned package manifest, containing sfdxPackage only + * @param projectConfig + * @param sfdxPackage + */ + public static cleanupMPDFromProjectConfig(projectConfig: any, sfdxPackage: string): any { + if (sfdxPackage) { + let i = projectConfig['packageDirectories'].length; + while (i--) { + if (sfdxPackage != projectConfig['packageDirectories'][i]['package']) { + projectConfig['packageDirectories'].splice(i, 1); + } + } + } else { + let i = projectConfig['packageDirectories'].length; + while (i--) { + if (!fs.existsSync(projectConfig['packageDirectories'][i]['path'])) { + projectConfig['packageDirectories'].splice(i, 1); + } + } + } + projectConfig['packageDirectories'][0]['default'] = true; //add default = true + return projectConfig; + } + + /** + * Returns pruned package manifest, containing sfdxPackages only + * @param projectConfig + * @param sfdxPackages + */ + public static cleanupPackagesFromProjectConfig(projectConfig: any, sfdxPackages: string[]): any { + let revisedPackageDirectory = []; + let originalPackageDirectory = projectConfig['packageDirectories']; + for (let pkg of originalPackageDirectory) { + for (const sfdxPackage of sfdxPackages) { + if (pkg.name == sfdxPackage) { + pkg.default = false; + revisedPackageDirectory.push(pkg); + } + } + } + projectConfig['packageDirectories'][0]['default'] = true; //add default = true + projectConfig.packageDirectories = revisedPackageDirectory; + return projectConfig; + } + + /** + * Returns pruned package manifest, containing sfdxPackages only + * @param projectConfig + * @param sfdxPackages + */ + public static cleanupPackagesFromProjectDirectory(projectDirectory: string, sfdxPackages: string[]): any { + const projectConfig = this.getSFDXProjectConfig(projectDirectory); + return ProjectConfig.cleanupPackagesFromProjectConfig(projectConfig, sfdxPackages); + } + + + + public static async updateProjectConfigWithDependencies( + projectConfig: any, + dependencyMap: Map + ) { + let updatedprojectConfig = await _.cloneDeep(projectConfig); + updatedprojectConfig.packageDirectories.map((pkg) => { + return Object.assign(pkg, { dependencies: dependencyMap.get(pkg.package) }); + }); + + return updatedprojectConfig; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/project/UserDefinedExternalDependency.ts b/packages/sfpowerscripts-cli/src/core/project/UserDefinedExternalDependency.ts new file mode 100644 index 000000000..a56301ad5 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/project/UserDefinedExternalDependency.ts @@ -0,0 +1,61 @@ +import SFPLogger from '@flxblio/sfp-logger'; +import { Connection, LoggerLevel } from '@salesforce/core'; +import _ from 'lodash'; +import ExternalPackage2DependencyResolver from '../package/dependencies/ExternalPackage2DependencyResolver'; + +/** + * Functions to deal with externalDependencyMap supplied by the user + * to aid in resolving transitive dependencies + */ +export default class UserDefinedExternalDependencyMap { + + + public fetchDependencyEntries(projectConfig: any) { + if (projectConfig.plugins?.sfp?.externalDependencyMap) { + let externalDependencyMap = projectConfig.plugins.sfp.externalDependencyMap; + SFPLogger.log(JSON.stringify(externalDependencyMap), LoggerLevel.DEBUG); + return externalDependencyMap; + } + else + return {}; + } + + public async addDependencyEntries(projectConfig: any, connToDevHub: Connection) { + let externalDependencies = []; + let updatedProjectConfig = await _.cloneDeep(projectConfig); + let externalPackageResolver = new ExternalPackage2DependencyResolver(connToDevHub, projectConfig, null); + + let externalDependencyMap = this.fetchDependencyEntries(projectConfig); + + let externalPackage2s = await externalPackageResolver.resolveExternalPackage2DependenciesToVersions(); + + for (let externalPackage2 of externalPackage2s) { + externalDependencies.push(externalPackage2.name); + } + for (let dependency of externalDependencies) { + if (!Object.keys(externalDependencyMap).includes(dependency)) { + externalDependencyMap[dependency] = [{ package: '', versionNumber: '' }]; + } + } + updatedProjectConfig.plugins.sfp.externalDependencyMap = externalDependencyMap; + return updatedProjectConfig; + } + + public async cleanupEntries(projectConfig: any) { + let updatedProjectConfig = await _.cloneDeep(projectConfig); + if (updatedProjectConfig?.plugins?.sfp?.externalDependencyMap) { + const externalDependencyMap = updatedProjectConfig.plugins.sfp.externalDependencyMap; + for (let externalPackage of Object.keys(externalDependencyMap)) { + if (externalDependencyMap[externalPackage][0].package == '') { + delete externalDependencyMap[externalPackage]; + } else if ( + externalDependencyMap[externalPackage][0].package != '' && + externalDependencyMap[externalPackage][0].versionNumber == '' + ) { + delete externalDependencyMap[externalPackage][0].versionNumber; + } + } + } + return updatedProjectConfig; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/queryHelper/ChunkCollection.ts b/packages/sfpowerscripts-cli/src/core/queryHelper/ChunkCollection.ts new file mode 100644 index 000000000..c6a6c9b7d --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/queryHelper/ChunkCollection.ts @@ -0,0 +1,38 @@ + + +/** + * Split values in SOQL WHERE clause into chunks to avoid exceeding max. URI length (16,000 chars) or max. WHERE clause length (4000 chars) + * @param collection values in SOQL WHERE clause + * @param chunkSize default is 4000 + * @param offset offset to account for keywords, fields, operators and literals in the query. Default is 1000 + */ +export default function chunkCollection(collection: string[], chunkSize: number = 4000, offset: number = 1000): string[][] { + const result: string[][] = []; + chunkSize = chunkSize - offset; + + let chunk: string[] = []; + let numberOfCharsInChunk: number = 0; + for (const elem of collection) { + if (elem.length + 2 > chunkSize) { + throw new Error(`Single value cannot exceed chunk size limit of ${chunkSize}`); + } + + const commasAndQuotes = 2*(chunk.length+1) + chunk.length; + if (numberOfCharsInChunk + elem.length + commasAndQuotes <= chunkSize) { + chunk.push(elem); + numberOfCharsInChunk += elem.length; + } else { + result.push(chunk); + + // Create new chunk + chunk = []; + numberOfCharsInChunk = 0; + chunk.push(elem); + numberOfCharsInChunk += elem.length; + } + } + + result.push(chunk); + + return result; +} \ No newline at end of file diff --git a/packages/sfpowerscripts-cli/src/core/queryHelper/QueryHelper.ts b/packages/sfpowerscripts-cli/src/core/queryHelper/QueryHelper.ts new file mode 100644 index 000000000..9b077895c --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/queryHelper/QueryHelper.ts @@ -0,0 +1,18 @@ +import { Connection } from '@salesforce/core'; + +const retry = require('async-retry'); + +export default class QueryHelper { + static async query(query: string, conn: Connection, isTooling: boolean): Promise { + return retry( + async (bail) => { + let records; + if (isTooling) records = (await conn.tooling.query(query)).records; + else records = (await conn.query(query)).records; + + return records; + }, + { retries: 3, minTimeout: 2000 } + ); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/PasswordGenerator.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/PasswordGenerator.ts new file mode 100644 index 000000000..482d5d529 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/PasswordGenerator.ts @@ -0,0 +1,41 @@ +import { Connection, User, AuthInfo, LoggerLevel } from '@salesforce/core'; +import SFPLogger from '@flxblio/sfp-logger'; + +export default class PasswordGenerator { + public async exec(userName: string) { + const query = `SELECT id FROM User WHERE username = '${userName}'`; + + const authInfo = await AuthInfo.create({ username: userName }); + const userConnection = await Connection.create({ authInfo: authInfo }); + let userRecord = (await userConnection.query(query)).records as any; + let passwordBuffer = User.generatePasswordUtf8(); + let pwd; + + await passwordBuffer.value(async (buffer: Buffer) => { + try { + pwd = buffer.toString('utf8'); + + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore TODO: expose `soap` on Connection however appropriate + const soap = userConnection.soap; + await soap.setPassword(userRecord[0].Id, pwd); + } catch (e) { + console.log(e); + pwd = undefined; + if (e.message === 'INSUFFICIENT_ACCESS: Cannot set password for self') { + SFPLogger.log( + `${e.message}. Incase of scratch org, Add "features": ["EnableSetPasswordInApi"] in your project-scratch-def.json then create your scratch org.`, + LoggerLevel.WARN + ); + } else { + SFPLogger.log(`${e.message}`, LoggerLevel.WARN); + } + } + }); + + return { + username: userName, + password: pwd, + }; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/ScratchOrg.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/ScratchOrg.ts new file mode 100644 index 000000000..8c700d757 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/ScratchOrg.ts @@ -0,0 +1,18 @@ +export default interface ScratchOrg { + failureMessage?: string; + tag?: string; + recordId?: string; + orgId?: string; + loginURL?: string; + signupEmail?: string; + username?: string; + alias?: string; + password?: string; + isScriptExecuted?: boolean; + expiryDate?: string; + accessToken?: string; + instanceURL?: string; + status?: string; + sfdxAuthUrl?: string; + elapsedTime?:number +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/ScratchOrgOperator.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/ScratchOrgOperator.ts new file mode 100644 index 000000000..f354fbbd8 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/ScratchOrgOperator.ts @@ -0,0 +1,149 @@ +import { AuthInfo, Org, StateAggregator } from '@salesforce/core'; +import ScratchOrg from './ScratchOrg'; +import PasswordGenerator from './PasswordGenerator'; +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; +import { Duration } from '@salesforce/kit'; +import { ScratchOrgRequest } from '@salesforce/core'; +import { COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; +import getFormattedTime from '../utils/GetFormattedTime'; +import SFPStatsSender from '../stats/SFPStatsSender'; +const retry = require('async-retry'); + +export default class ScratchOrgOperator { + constructor(private hubOrg: Org) {} + + public async create( + alias: string, + config_file_path: string, + expiry: number, + waitTime: number = 6 + ): Promise { + SFPLogger.log('Parameters: ' + alias + ' ' + config_file_path + ' ' + expiry + ' ', LoggerLevel.TRACE); + + let startTime = Date.now(); + SFPLogger.log(`Requesting Scratch Org ${alias}..`, LoggerLevel.INFO); + let scatchOrgResult = await this.requestAScratchOrg( + alias, + config_file_path, + Duration.days(expiry), + Duration.minutes(waitTime) + ); + SFPLogger.log(JSON.stringify(scatchOrgResult), LoggerLevel.TRACE); + + //create scratchOrg object + let scratchOrg: ScratchOrg = { + alias: alias, + orgId: scatchOrgResult.orgId, + username: scatchOrgResult.username, + loginURL: scatchOrgResult.loginURL, + elapsedTime: Date.now() - startTime, + }; + + try { + //Get Sfdx Auth URL + const authInfo = await AuthInfo.create({ username: scratchOrg.username }); + scratchOrg.sfdxAuthUrl = authInfo.getSfdxAuthUrl(); + } catch (error) { + throw new Error( + `Unable to set auth URL, Ignoring this scratch org, as its not suitable for pool due to ${error.message}` + ); + } + + //Generate Password + let passwordData = await new PasswordGenerator().exec(scratchOrg.username); + + scratchOrg.password = passwordData.password; + + if (!passwordData.password) { + throw new Error('Unable to setup password to scratch org'); + } else { + SFPLogger.log(`Password successfully set for ${scratchOrg.alias}`, LoggerLevel.DEBUG); + } + + SFPLogger.log( + `Creation request for Scratch Org ${scratchOrg.alias} is completed successfully in ${COLOR_KEY_MESSAGE( + getFormattedTime(scratchOrg.elapsedTime) + )}`, + LoggerLevel.INFO + ); + SFPStatsSender.logElapsedTime(`scratchorg.creation.time`,scratchOrg.elapsedTime) + return scratchOrg; + } + + public async delete(scratchOrgIds: string[]) { + let hubConn = this.hubOrg.getConnection(); + + await retry( + async (bail) => { + let result = await hubConn.del('ActiveScratchOrg', scratchOrgIds); + }, + { retries: 3, minTimeout: 3000 } + ); + } + + private async requestAScratchOrg(alias: string, definitionFile: string, expireIn: Duration, waitTime: Duration) { + const createCommandOptions: ScratchOrgRequest = { + durationDays: expireIn.days, + nonamespace: false, + noancestors: false, + wait: waitTime, + retry: 3, + definitionfile: definitionFile, + }; + + const { username, scratchOrgInfo, authFields, warnings } = await this.hubOrg.scratchOrgCreate( + createCommandOptions + ); + + await this.setAliasForUsername(username, alias); + + return { + username: username, + loginURL: scratchOrgInfo.LoginUrl, + warnings, + orgId: authFields.orgId, + }; + } + + public async shareScratchOrgThroughEmail(emailId: string, scratchOrg: ScratchOrg) { + let hubOrgUserName = this.hubOrg.getUsername(); + let apiVersion = this.hubOrg.getConnection().retrieveMaxApiVersion(); + let body = `${hubOrgUserName} has fetched a new scratch org from the Scratch Org Pool!\n + All the post scratch org scripts have been succesfully completed in this org!\n + The Login url for this org is : ${scratchOrg.loginURL}\n + Username: ${scratchOrg.username}\n + Password: ${scratchOrg.password}\n + Please use sfdx force:auth:web:login -r ${scratchOrg.loginURL} -a command to authenticate against this Scratch org

+ Thank you for using SFPLogger!`; + + const options = { + method: 'POST', + body: JSON.stringify({ + inputs: [ + { + emailBody: body, + emailAddresses: emailId, + emailSubject: `${hubOrgUserName} created you a new Salesforce org`, + senderType: 'CurrentUser', + }, + ], + }), + url: `/services/data/v${apiVersion}actions/standard/emailSimple`, + }; + + await retry( + async (bail) => { + await this.hubOrg.getConnection().requestPost(options.url, options.body); + }, + { retries: 3, minTimeout: 30000 } + ); + + SFPLogger.log(`Succesfully send email to ${emailId} for ${scratchOrg.username}`, LoggerLevel.INFO); + } + + private async setAliasForUsername(username: string, aliasToSet: string): Promise { + const stateAggregator = await StateAggregator.getInstance(); + stateAggregator.aliases.set(aliasToSet, { username: username }); + await stateAggregator.aliases.write(); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/ClientSourceTracking.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/ClientSourceTracking.ts new file mode 100644 index 000000000..806dd2de1 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/ClientSourceTracking.ts @@ -0,0 +1,205 @@ +const path = require('path'); +import * as fs from 'fs-extra'; +import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { Connection, SfProject } from '@salesforce/core'; +import SFPOrg from '../../org/SFPOrg'; +import { SourceTracking } from '@salesforce/source-tracking'; +import ProjectConfig from '../../project/ProjectConfig'; +import { ComponentSet } from '@salesforce/source-deploy-retrieve'; +import { EOL } from 'os'; +import { PackageType } from '../../package/SfpPackage'; +import Git from '../../git/Git'; + +const tmp = require('tmp'); + +export default class ClientSourceTracking { + private conn: Connection; + private org: SFPOrg; + private logger: Logger; + + private sfdxOrgIdDir; + + private constructor() {} + + static async create(conn: Connection, logger: Logger) { + const clientSourceTracking = new ClientSourceTracking(); + + clientSourceTracking.conn = conn; + + clientSourceTracking.org = await SFPOrg.create({ connection: clientSourceTracking.conn }); + clientSourceTracking.logger = logger; + + clientSourceTracking.sfdxOrgIdDir = `.sf/orgs/${clientSourceTracking.org.getOrgId()}`; + + return clientSourceTracking; + } + + async creatSourceTrackingFiles(): Promise { + await this.createRemoteSourceTracking(); + await this.createLocalSourceTracking(); + } + + private async createRemoteSourceTracking() { + const project = await SfProject.resolve(); + const tracking = await SourceTracking.create({ + org: this.org, + project: project, + }); + + tracking.resetRemoteTracking(); + } + + /** + * Create local source tracking from sfp artifacts installed in scratch org + */ + private async createLocalSourceTracking() { + + let git; + try { + git = await Git.initiateRepoAtTempLocation(this.logger); + + const sfpArtifacts = await this.org.getInstalledArtifacts(); + + if(sfpArtifacts.length==0) + throw new Error(`Unable to find any artifacts in the org`); + + //clean up MPD to just one package, so that source tracking lib + //does do a full scan and break + this.cleanupSFDXProjectJsonTonOnePackage(git.getRepositoryPath(), sfpArtifacts[0].Name); + + const project = await SfProject.resolve(git.getRepositoryPath()); + + // Create local source tracking files in temp repo + const tracking = await SourceTracking.create({ + org: this.org, + project: project, + }); + + + + SFPLogger.log( + `Total Artifacts to Analyze: ${sfpArtifacts.length}`, + LoggerLevel.INFO, + this.logger + ); + + let count = 1; + for (const artifact of sfpArtifacts) { + SFPLogger.log(EOL, LoggerLevel.INFO, this.logger); + SFPLogger.log( + COLOR_HEADER(`Package ${count} of ${sfpArtifacts.length}`), + LoggerLevel.INFO, + this.logger + ); + SFPLogger.log(`Analyzing package ${COLOR_KEY_MESSAGE(artifact.Name)}`, LoggerLevel.INFO, this.logger); + // Checkout version of source code from which artifact was created + await git.checkout(artifact.CommitId__c,true) + + SFPLogger.log( + `Version pushed while preparing this org is ${artifact.Version__c} with SHA ${artifact.CommitId__c}`, + LoggerLevel.INFO, + this.logger + ); + + //clean up MPD to per package, to speed up + this.cleanupSFDXProjectJsonTonOnePackage(git.getRepositoryPath(), artifact.Name); + + const projectConfig = ProjectConfig.getSFDXProjectConfig(git.getRepositoryPath()); + + try { + const packageType = ProjectConfig.getPackageType(projectConfig, artifact.Name); + if (packageType === PackageType.Unlocked || packageType === PackageType.Source) { + let componentSet = ComponentSet.fromSource( + path.join( + git.getRepositoryPath(), + ProjectConfig.getPackageDescriptorFromConfig(artifact.Name, projectConfig).path + ) + ); + let components = componentSet.getSourceComponents(); + + //Get all components in the directory + //Count for logging purposes. dont have to waste processing convering + //a lazy collection to array once again + let componentCount = 1; + let componentPaths: string[] = []; + for (const component of components) { + componentCount++; + componentPaths.push(component.xml); + if (component.content) componentPaths.push(component.content); + } + + await tracking.updateLocalTracking({ + files: componentPaths, + }); + SFPLogger.log( + `Updated source tracking for package: ${artifact.Name} with ${componentCount} items`, + LoggerLevel.INFO, + this.logger + ); + } else SFPLogger.log(`Encountered data package... skipping`, LoggerLevel.INFO, this.logger); + } catch (error) { + if(error.message.includes) + { + SFPLogger.log( + ` sfp is unable to sync the package ${artifact.name}${EOL}, + as it not able to find the find equivalent git references`, + LoggerLevel.ERROR, + this.logger); + } + else + SFPLogger.log( + `Unable to update local source tracking due to ${error.message}`, + LoggerLevel.INFO, + this.logger + ); + SFPLogger.log(`Skipping package.. ${artifact.Name}`, LoggerLevel.WARN, this.logger); + } + count++; + } + + SFPLogger.log(EOL, LoggerLevel.INFO, this.logger); + SFPLogger.log(`Copying the temporary repository over to original location`, LoggerLevel.INFO, this.logger); + // Copy source tracking files from temp repo to actual repo + fs.mkdirpSync(path.join(this.sfdxOrgIdDir, 'localSourceTracking')); + fs.copySync( + path.join(git.getRepositoryPath(), this.sfdxOrgIdDir, 'localSourceTracking'), + path.join(this.sfdxOrgIdDir, 'localSourceTracking') + ); + } catch (error) { + + if(error.message.includes(`reference is not a tree`)) + { + SFPLogger.log( + `sfp is unable to sync this repository, + as it not able to find the matching git references${EOL} + Are you sure this pool was created from the same repository?`, + LoggerLevel.ERROR, + this.logger); + } + else + SFPLogger.log( + `Unable to update local source tracking due to ${error.message}`, + LoggerLevel.ERROR, + this.logger + ); + } finally { + if(git) + git.deleteTempoRepoIfAny(); + } + } + + private cleanupSFDXProjectJsonTonOnePackage(projectDir: string, packageName: string) { + try { + let cleanedUpProjectManifest = ProjectConfig.cleanupMPDFromProjectDirectory(projectDir, packageName); + fs.writeJSONSync(path.join(projectDir, 'sfdx-project.json'), cleanedUpProjectManifest, { + spaces: 4, + }); + } catch (error) { + SFPLogger.log( + `sfdx-project.json not found/unable to write, skipping..` + error.message, + LoggerLevel.DEBUG, + this.logger + ); + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/OrphanedOrgsDeleteImpl.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/OrphanedOrgsDeleteImpl.ts new file mode 100644 index 000000000..d21987759 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/OrphanedOrgsDeleteImpl.ts @@ -0,0 +1,47 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { Org } from '@salesforce/core'; +import { PoolBaseImpl } from './PoolBaseImpl'; +import ScratchOrg from '../ScratchOrg'; +import ScratchOrgInfoFetcher from './services/fetchers/ScratchOrgInfoFetcher'; +import ScratchOrgOperator from '../ScratchOrgOperator'; + +export default class OrphanedOrgsDeleteImpl extends PoolBaseImpl { + public constructor(hubOrg: Org, private logger:Logger) { + super(hubOrg); + this.hubOrg = hubOrg; + } + + protected async onExec(): Promise { + const results = (await new ScratchOrgInfoFetcher(this.hubOrg).getOrphanedScratchOrgs()) as any; + + let scratchOrgToDelete: ScratchOrg[] = new Array(); + if (results.records.length > 0) { + let scrathOrgIds: string[] = []; + for (let element of results.records) { + if (element.Description?.includes(`"requestedBy":"sfp"`)) { + let soDetail: ScratchOrg = {}; + soDetail.orgId = element.ScratchOrg; + soDetail.username = element.SignupUsername; + soDetail.status = 'recovered'; + scratchOrgToDelete.push(soDetail); + scrathOrgIds.push(`'${element.Id}'`); + } + } + + if (scrathOrgIds.length > 0) { + let activeScrathOrgs = await new ScratchOrgInfoFetcher(this.hubOrg).getActiveScratchOrgsByInfoId( + scrathOrgIds.join(',') + ); + + if (activeScrathOrgs.records.length > 0) { + for (let scratchOrg of activeScrathOrgs.records) { + await new ScratchOrgOperator(this.hubOrg).delete(scratchOrg.Id); + SFPLogger.log(`Scratch org with username ${scratchOrg.SignupUsername} is recovered`,LoggerLevel.TRACE,this.logger); + } + } + } + } + + return scratchOrgToDelete; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolBaseImpl.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolBaseImpl.ts new file mode 100644 index 000000000..ba7ef6c41 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolBaseImpl.ts @@ -0,0 +1,22 @@ +import { Org } from '@salesforce/core'; +import { Result } from 'neverthrow'; +import ScratchOrg from '../ScratchOrg'; +import { PoolConfig } from './PoolConfig'; +import { PoolError } from './PoolError'; +import PreRequisiteCheck from './prequisitecheck/PreRequisiteCheck'; + +export abstract class PoolBaseImpl { + protected hubOrg: Org; + + constructor(hubOrg: Org) { + this.hubOrg = hubOrg; + } + + public async execute(): Promise|void> { + let prerequisiteCheck: PreRequisiteCheck = new PreRequisiteCheck(this.hubOrg); + await prerequisiteCheck.checkForPrerequisites(); + return this.onExec(); + } + + protected abstract onExec(): Promise|void>; +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolConfig.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolConfig.ts new file mode 100644 index 000000000..b18fc05f2 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolConfig.ts @@ -0,0 +1,40 @@ +import ScratchOrg from '../ScratchOrg'; + +export interface PoolConfig { + tag: string; + maxAllocation: number; + waitTime?: number; + expiry?: number; + batchSize?: number; + configFilePath: string; + releaseConfigFile?:string; + succeedOnDeploymentErrors?: boolean; + keys?: string; + installAll: boolean; + enableSourceTracking: boolean; + relaxAllIPRanges?: boolean; + ipRangesToBeRelaxed?: []; + retryOnFailure?: boolean; + fetchArtifacts: { + artifactFetchScript?: string; + npm?: { + npmrcPath?: string; + scope: string; + }; + }; + disableSourcePackageOverride?:boolean; + snapshotPool?:string; + postDeploymentScriptPath: string; + preDependencyInstallationScriptPath: string; + enableVlocity?: boolean; + min_allocation?: number; + current_allocation?: number; + to_allocate?: number; + to_satisfy_min?: number; + to_satisfy_max?: number; + scratchOrgs?: ScratchOrg[]; + failedToCreate?: number; + maxRetryCount?:number; + + +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolCreateImpl.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolCreateImpl.ts new file mode 100644 index 000000000..12c0bcadf --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolCreateImpl.ts @@ -0,0 +1,432 @@ +import { Org } from '@salesforce/core'; +import Bottleneck from 'bottleneck'; +import { PoolConfig } from './PoolConfig'; +import { PoolBaseImpl } from './PoolBaseImpl'; +import ScratchOrg from '../ScratchOrg'; +import ScratchOrgInfoFetcher from './services/fetchers/ScratchOrgInfoFetcher'; +import ScratchOrgLimitsFetcher from './services/fetchers/ScratchOrgLimitsFetcher'; +import ScratchOrgInfoAssigner from './services/updaters/ScratchOrgInfoAssigner'; +import * as rimraf from 'rimraf'; +import * as fs from 'fs-extra'; +import PoolJobExecutor, { ScriptExecutionResult } from './PoolJobExecutor'; +import { PoolError, PoolErrorCodes } from './PoolError'; +import SFPLogger, { COLOR_KEY_MESSAGE, LoggerLevel } from '@flxblio/sfp-logger'; +import { Result, ok, err } from 'neverthrow'; +import SFPStatsSender from '../../stats/SFPStatsSender'; +import { EOL } from 'os'; +import OrgDetailsFetcher from '../../org/OrgDetailsFetcher'; +import ScratchOrgOperator from '../ScratchOrgOperator'; +import PoolFetchImpl from './PoolFetchImpl'; +import { COLOR_SUCCESS } from '@flxblio/sfp-logger'; +import { COLOR_ERROR } from '@flxblio/sfp-logger'; +import getFormattedTime from '../../utils/GetFormattedTime'; +import path from 'path'; + +export default class PoolCreateImpl extends PoolBaseImpl { + private limiter; + private scriptExecutorWrappedForBottleneck; + private limits: any; + private scratchOrgInfoFetcher: ScratchOrgInfoFetcher; + private scratchOrgInfoAssigner: ScratchOrgInfoAssigner; + private scratchOrgOperator: ScratchOrgOperator; + private totalToBeAllocated: number; + private totalAllocated: number = 0; + + public constructor( + hubOrg: Org, + private pool: PoolConfig, + private poolScriptExecutor: PoolJobExecutor, + private logLevel: LoggerLevel + ) { + super(hubOrg); + this.limiter = new Bottleneck({ + maxConcurrent: this.pool.batchSize, + }); + + this.scriptExecutorWrappedForBottleneck = this.limiter.wrap(this.scriptExecutor); + } + + protected async onExec(): Promise> { + await this.hubOrg.refreshAuth(); + + const scriptExecPromises: Array> = []; + + + //fetch current status limits + this.limits = await new ScratchOrgLimitsFetcher(this.hubOrg).getScratchOrgLimits(); + + //Create Service classes + this.scratchOrgInfoFetcher = new ScratchOrgInfoFetcher(this.hubOrg); + this.scratchOrgInfoAssigner = new ScratchOrgInfoAssigner(this.hubOrg); + + //Create Operator + this.scratchOrgOperator = new ScratchOrgOperator(this.hubOrg); + + // Setup Logging Directory + rimraf.sync('script_exec_outputs'); + fs.mkdirpSync('script_exec_outputs'); + + //Compute allocation + try { + if (!this.pool.snapshotPool) { + SFPLogger.log(COLOR_KEY_MESSAGE('Computing Allocation..'), LoggerLevel.INFO); + try { + this.totalToBeAllocated = await this.computeAllocation(); + } catch (error) { + return err({ + success: 0, + failed: 0, + message: `Unable to access fields on ScratchOrgInfo, Please check the profile being used`, + errorCode: PoolErrorCodes.PrerequisiteMissing, + }); + } + + if (this.totalToBeAllocated === 0) { + if (this.limits.ActiveScratchOrgs.Remaining > 0) { + return err({ + success: 0, + failed: 0, + message: `The tag provided ${this.pool.tag} is currently at the maximum capacity , No scratch orgs will be allocated`, + errorCode: PoolErrorCodes.Max_Capacity, + }); + } else { + return err({ + success: 0, + failed: 0, + message: `There is no capacity to create a pool at this time, Please try again later`, + errorCode: PoolErrorCodes.No_Capacity, + }); + } + } + + //Generate Scratch Orgs + this.pool.scratchOrgs = await this.generateScratchOrgs( + this.pool, + this.scratchOrgOperator, + this.scratchOrgInfoAssigner + ); + } else { + this.pool.scratchOrgs = await this.fetchScratchOrgsFromSnapshotPool( + this.pool, + this.scratchOrgInfoFetcher, + this.scratchOrgInfoAssigner + ); + } + } catch (error) { + return err({ + success: 0, + failed: this.pool.failedToCreate, + message: `All requested scratch orgs failed to provision, Please check your code or config \n Failed with ${error.message}`, + errorCode: PoolErrorCodes.UnableToProvisionAny, + }); + } + + // Assign workers to executed scripts + for (const scratchOrg of this.pool.scratchOrgs) { + const result = this.scriptExecutorWrappedForBottleneck(scratchOrg, this.hubOrg.getUsername()); + scriptExecPromises.push(result); + } + + await Promise.all(scriptExecPromises); + + this.pool = await this.finalizeGeneratedScratchOrgs( + this.pool, + this.scratchOrgOperator, + this.scratchOrgInfoFetcher + ); + + if (!this.pool.scratchOrgs || this.pool.scratchOrgs.length == 0) { + return err({ + success: 0, + failed: this.pool.failedToCreate, + message: `All requested scratch orgs failed to provision, Please check your code or config`, + errorCode: PoolErrorCodes.UnableToProvisionAny, + }); + } + return ok(this.pool); + + + } + + private async computeAllocation(): Promise { + //Compute current pool requirement + const activeCount = await this.scratchOrgInfoFetcher.getCountOfActiveScratchOrgsByTag(this.pool.tag); + return this.allocateScratchOrgsPerTag(this.limits.ActiveScratchOrgs.Remaining, activeCount, this.pool); + } + + private allocateScratchOrgsPerTag( + remainingScratchOrgs: number, + countOfActiveScratchOrgs: number, + pool: PoolConfig + ) { + pool.current_allocation = countOfActiveScratchOrgs; + pool.to_allocate = 0; + pool.to_satisfy_max = + pool.maxAllocation - pool.current_allocation > 0 ? pool.maxAllocation - pool.current_allocation : 0; + + if (pool.to_satisfy_max > 0 && pool.to_satisfy_max <= remainingScratchOrgs) { + pool.to_allocate = pool.to_satisfy_max; + } else if (pool.to_satisfy_max > 0 && pool.to_satisfy_max > remainingScratchOrgs) { + pool.to_allocate = remainingScratchOrgs; + } + + SFPLogger.log( + `${EOL}Current Allocation of ScratchOrgs in the pool ${this.pool.tag}: ` + pool.current_allocation, + LoggerLevel.INFO + ); + SFPLogger.log('Remaining Active scratchOrgs in the org: ' + remainingScratchOrgs, LoggerLevel.INFO); + SFPLogger.log('ScratchOrgs to be allocated: ' + pool.to_allocate, LoggerLevel.INFO); + return pool.to_allocate; + } + + private async generateScratchOrgs( + pool: PoolConfig, + scratchOrgOperator: ScratchOrgOperator, + scratchOrgInfoAssigner: ScratchOrgInfoAssigner + ) { + //Generate Scratch Orgs + SFPLogger.log(COLOR_KEY_MESSAGE('Generate Scratch Orgs..'), LoggerLevel.INFO); + + const scratchOrgPromises = new Array>(); + + const scratchOrgCreationLimiter = new Bottleneck({ + maxConcurrent: pool.batchSize, + }); + + addDescriptionToScratchOrg(pool); + + const startTime = Date.now(); + for (let i = 1; i <= pool.to_allocate; i++) { + const scratchOrgPromise: Promise = scratchOrgCreationLimiter.schedule(() => + scratchOrgOperator.create(`SO` + i, this.pool.configFilePath, this.pool.expiry, this.pool.waitTime) + ); + scratchOrgPromises.push(scratchOrgPromise); + } + + SFPLogger.log(`Waiting for all scratch org request to complete, Please wait`); + //Wait for all orgs to be created + const scratchOrgCreationResults = await Promise.allSettled(scratchOrgPromises); + //Only worry about scrath orgs that have suceeded + const isFulfilled = (p: PromiseSettledResult): p is PromiseFulfilledResult => p.status === 'fulfilled'; + const isRejected = (p: PromiseSettledResult): p is PromiseRejectedResult => p.status === 'rejected'; + + let scratchOrgs = scratchOrgCreationResults.filter(isFulfilled).map((p) => p.value); + const rejectedScratchOrgs = scratchOrgCreationResults.filter(isRejected).map((p) => p.reason); + for (const reason of rejectedScratchOrgs) { + if (reason.message.includes(`The client has timed out`)) { + //Log how many we were able to create + const elapsedTime = Date.now() - startTime; + SFPLogger.log( + `A scratch org creation was rejected due to saleforce not responding within the set wait time of ${pool.waitTime} mins \n` + + `Time elasped so far ${COLOR_KEY_MESSAGE( + getFormattedTime(elapsedTime) + )},You might need to inrease the wait time further and rety ` + ); + } else SFPLogger.log(`A scratch org creation was rejected due to ${reason.message}`); + } + + //Log how many we were able to create + const elapsedTime = Date.now() - startTime; + SFPLogger.log( + `Created ${COLOR_SUCCESS(scratchOrgs.length)} of ${pool.to_allocate} successfully with ${COLOR_ERROR( + rejectedScratchOrgs.length + )} failures in ${COLOR_KEY_MESSAGE(getFormattedTime(elapsedTime))}` + ); + + SFPStatsSender.logElapsedTime(`pool.scratchorg.creation.time`, elapsedTime, { pool: pool.tag }); + if (scratchOrgs && scratchOrgs.length > 0) { + //Splice scratchorgs that are having incorrect status of deleted , Why salesforce why?? + let index = scratchOrgs.length; + while (index--) { + try { + const orgDetails = await new OrgDetailsFetcher(scratchOrgs[index].username).getOrgDetails(); + if (orgDetails.status === 'Deleted') { + throw new Error( + `Throwing away scratch org ${this.pool.scratchOrgs[index].alias} as it has a status of deleted` + ); + } + } catch (error) { + scratchOrgs.splice(index, 1); + } + } + + scratchOrgs = await this.scratchOrgInfoFetcher.getScratchOrgRecordId(scratchOrgs); + + const scratchOrgInprogress = []; + + scratchOrgs.forEach((scratchOrg) => { + scratchOrgInprogress.push({ + Id: scratchOrg.recordId, + Pooltag__c: this.pool.tag, + Password__c: scratchOrg.password, + SfdxAuthUrl__c: scratchOrg.sfdxAuthUrl, + Allocation_status__c: 'In Progress', + }); + }); + + if (scratchOrgInprogress.length > 0) { + //set pool tag + await scratchOrgInfoAssigner.setScratchOrgInfo(scratchOrgInprogress); + } + return scratchOrgs; + } else throw new Error(`No scratch orgs were sucesfully generated`); + + function addDescriptionToScratchOrg(pool: PoolConfig) { + + const configClonePath = path.join('.sfp','scratchorg-configs',`${ makeFileId(8)}.json`); + fs.mkdirpSync('.sfp/scratchorg-configs'); + fs.copyFileSync(pool.configFilePath,configClonePath); + + const scratchOrgDefn = fs.readJSONSync(configClonePath); + if (!scratchOrgDefn.description) + scratchOrgDefn.description = JSON.stringify({ + requestedBy: 'sfp', + pool: pool.tag, + requestedAt: new Date().toISOString(), + }); + else + scratchOrgDefn.description = scratchOrgDefn.description.concat( + ' ', + JSON.stringify({ + requestedBy: 'sfp', + pool: pool.tag, + requestedAt: new Date().toISOString(), + }) + ); + fs.writeJSONSync(configClonePath, scratchOrgDefn, { spaces: 4 }); + pool.configFilePath = configClonePath; + } + + function makeFileId(length): string { + let result = ''; + const characters = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; + } + } + + private async fetchScratchOrgsFromSnapshotPool( + pool: PoolConfig, + scratchOrgInfoFetcher: ScratchOrgInfoFetcher, + scratchOrgInfoAssigner: ScratchOrgInfoAssigner + ) { + //Generate Scratch Orgs + SFPLogger.log( + COLOR_KEY_MESSAGE(`Fetching Scratch Orgs from snapshot pool ${this.pool.snapshotPool}`), + LoggerLevel.INFO + ); + + let scratchOrgs = (await new PoolFetchImpl( + this.hubOrg, + this.pool.snapshotPool, + false, + true, + undefined, + undefined, + undefined, + true, + this.pool.maxAllocation + ).execute()) as ScratchOrg[]; + scratchOrgs = await scratchOrgInfoFetcher.getScratchOrgRecordId(scratchOrgs); + + const scratchOrgInprogress = []; + + if (scratchOrgs && scratchOrgs.length > 0) { + scratchOrgs.forEach((scratchOrg) => { + scratchOrgInprogress.push({ + Id: scratchOrg.recordId, + Pooltag__c: this.pool.tag, + Password__c: scratchOrg.password, + SfdxAuthUrl__c: scratchOrg.sfdxAuthUrl, + Allocation_status__c: 'In Progress', + }); + }); + + if (scratchOrgInprogress.length > 0) { + //set pool tag + await scratchOrgInfoAssigner.setScratchOrgInfo(scratchOrgInprogress); + } + return scratchOrgs; + } else { + throw new Error('No scratch orgs were found to be fetched'); + } + } + + private async finalizeGeneratedScratchOrgs( + pool: PoolConfig, + scratchOrgOperator: ScratchOrgOperator, + scratchOrgInfoFetcher: ScratchOrgInfoFetcher + ) { + pool.failedToCreate = 0; + for (let i = pool.scratchOrgs.length - 1; i >= 0; i--) { + const scratchOrg = pool.scratchOrgs[i]; + if (scratchOrg.isScriptExecuted) { + continue; + } + + SFPLogger.log( + `Failed to execute scripts for ${scratchOrg.username} with alias ${scratchOrg.alias} due to ${scratchOrg.failureMessage}`, + LoggerLevel.ERROR + ); + + try { + //Delete scratchorgs that failed to execute script + + const activeScratchOrgRecordId = await scratchOrgInfoFetcher.getActiveScratchOrgRecordIdGivenScratchOrg( + scratchOrg.orgId + ); + + await scratchOrgOperator.delete([activeScratchOrgRecordId]); + console.log(`Succesfully deleted scratchorg ${scratchOrg.username}`); + } catch (error) { + SFPLogger.log( + `Unable to delete the scratchorg ${scratchOrg.username}.. due to\n` + error, + LoggerLevel.ERROR + ); + } + + pool.failedToCreate += 1; + pool.scratchOrgs.splice(i, 1); + } + return pool; + } + + private async scriptExecutor(scratchOrg: ScratchOrg): Promise { + SFPLogger.log( + `Executing Preparation Job ${scratchOrg.alias} with username: ${scratchOrg.username}`, + LoggerLevel.INFO + ); + + const startTime = Date.now(); + const result = await this.poolScriptExecutor.execute(scratchOrg, this.hubOrg, this.logLevel); + + if (result.isOk()) { + scratchOrg.isScriptExecuted = true; + const submitInfoToPool = await this.scratchOrgInfoAssigner.setScratchOrgInfo({ + Id: scratchOrg.recordId, + Allocation_status__c: 'Available', + }); + if (!submitInfoToPool) { + scratchOrg.isScriptExecuted = false; + scratchOrg.failureMessage = 'Unable to set the scratch org record in Pool'; + SFPStatsSender.logCount('prepare.org.failed'); + } else { + SFPStatsSender.logCount('prepare.org.succeeded'); + } + + SFPStatsSender.logElapsedTime('prepare.org.singlejob.elapsed_time', Date.now() - startTime, { + poolname: this.pool.tag, + }); + } else { + scratchOrg.isScriptExecuted = false; + scratchOrg.failureMessage = result.error.message; + SFPStatsSender.logCount('prepare.org.failed'); + } + + return scratchOrg; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolDeleteImpl.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolDeleteImpl.ts new file mode 100644 index 000000000..1a6768542 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolDeleteImpl.ts @@ -0,0 +1,65 @@ +import SFPLogger from '@flxblio/sfp-logger'; +import { Org } from '@salesforce/core'; +import { PoolBaseImpl } from './PoolBaseImpl'; +import ScratchOrg from '../ScratchOrg'; +import ScratchOrgInfoFetcher from './services/fetchers/ScratchOrgInfoFetcher'; +import ScratchOrgOperator from '../ScratchOrgOperator'; +import { Logger } from '@flxblio/sfp-logger'; +import { LoggerLevel } from '@flxblio/sfp-logger'; + +export default class PoolDeleteImpl extends PoolBaseImpl { + private tag: string; + private mypool: boolean; + private allScratchOrgs: boolean; + private inprogressonly: boolean; + + public constructor(hubOrg: Org, tag: string, mypool: boolean, allScratchOrgs: boolean, inprogressonly: boolean,private logger:Logger) { + super(hubOrg); + this.hubOrg = hubOrg; + this.tag = tag; + this.mypool = mypool; + this.allScratchOrgs = allScratchOrgs; + this.inprogressonly = inprogressonly; + } + + protected async onExec(): Promise { + const results = (await new ScratchOrgInfoFetcher(this.hubOrg).getScratchOrgsByTag( + this.tag, + this.mypool, + !this.allScratchOrgs + )) as any; + + let scratchOrgToDelete: ScratchOrg[] = new Array(); + if (results.records.length > 0) { + let scrathOrgIds: string[] = []; + for (let element of results.records) { + if (!this.inprogressonly || element.Allocation_status__c === 'In Progress') { + let soDetail: ScratchOrg = {}; + soDetail.orgId = element.ScratchOrg; + soDetail.loginURL = element.LoginUrl; + soDetail.username = element.SignupUsername; + soDetail.expiryDate = element.ExpirationDate; + soDetail.status = 'Deleted'; + + scratchOrgToDelete.push(soDetail); + scrathOrgIds.push(`'${element.Id}'`); + } + } + + if (scrathOrgIds.length > 0) { + let activeScrathOrgs = await new ScratchOrgInfoFetcher(this.hubOrg).getActiveScratchOrgsByInfoId( + scrathOrgIds.join(',') + ); + + if (activeScrathOrgs.records.length > 0) { + for (let scratchOrg of activeScrathOrgs.records) { + await new ScratchOrgOperator(this.hubOrg).delete(scratchOrg.Id); + SFPLogger.log(`Scratch org with username ${scratchOrg.SignupUsername} is deleted successfully`,LoggerLevel.TRACE,this.logger); + } + } + } + } + + return scratchOrgToDelete; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolError.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolError.ts new file mode 100644 index 000000000..820346576 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolError.ts @@ -0,0 +1,13 @@ +export interface PoolError { + success: number; + failed: number; + errorCode: PoolErrorCodes; + message?: string; +} + +export enum PoolErrorCodes { + Max_Capacity = 'MaxCapacity', + No_Capacity = 'NoCapacity', + PrerequisiteMissing = 'PrerequisitesMissing', + UnableToProvisionAny = 'UnableToProvisionAny', +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolFetchImpl.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolFetchImpl.ts new file mode 100644 index 000000000..77a2c3566 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolFetchImpl.ts @@ -0,0 +1,244 @@ +import SFPLogger from '@flxblio/sfp-logger'; +import { AuthInfo, LoggerLevel, Org } from '@salesforce/core'; +import { PoolBaseImpl } from './PoolBaseImpl'; +import ScratchOrg from '../ScratchOrg'; +import { getUserEmail } from './services/fetchers/GetUserEmail'; +import ScratchOrgInfoFetcher from './services/fetchers/ScratchOrgInfoFetcher'; +import ScratchOrgInfoAssigner from './services/updaters/ScratchOrgInfoAssigner'; +import ClientSourceTracking from './ClientSourceTracking'; +import isValidSfdxAuthUrl from './prequisitecheck/IsValidSfdxAuthUrl'; +import ScratchOrgOperator from '../ScratchOrgOperator'; + +export default class PoolFetchImpl extends PoolBaseImpl { + private tag: string; + private mypool: boolean; + private sendToUser: string; + private alias: string; + private setdefaultusername: boolean; + private authURLEnabledScratchOrg: boolean; + private isSourceTrackingToBeSet: boolean = false; + + public constructor( + hubOrg: Org, + tag: string, + mypool: boolean, + authURLEnabledScratchOrg: boolean, + sendToUser?: string, + alias?: string, + setdefaultusername?: boolean, + private fetchAllScratchOrgs?: boolean, + private limitBy?:number + ) { + super(hubOrg); + this.tag = tag; + this.mypool = mypool; + this.authURLEnabledScratchOrg = authURLEnabledScratchOrg; + this.sendToUser = sendToUser; + this.alias = alias; + this.setdefaultusername = setdefaultusername; + } + + public setSourceTrackingOnFetch() { + this.isSourceTrackingToBeSet = true; + } + + protected async onExec(): Promise { + const results = (await new ScratchOrgInfoFetcher(this.hubOrg).getScratchOrgsByTag( + this.tag, + this.mypool, + true + )) as any; + + let availableSo = []; + if (results.records.length > 0) { + availableSo = results.records.filter((soInfo) => soInfo.Allocation_status__c === 'Available'); + } + if (availableSo.length == 0) { + throw new Error(`No scratch org available at the moment for ${this.tag}, try again in sometime.`); + } + + if (this.fetchAllScratchOrgs) { + return this.fetchAllScratchOrg(availableSo,this.limitBy); + } else return this.fetchSingleScratchOrg(availableSo); + } + + private async fetchAllScratchOrg(availableSo: any[],limitBy?:number): Promise { + let fetchedSOs: ScratchOrg[] = []; + + if (availableSo.length > 0) { + SFPLogger.log(`${this.tag} pool has ${availableSo.length} Scratch orgs available`, LoggerLevel.TRACE); + + let count = 1; + for (let element of availableSo) { + if (this.authURLEnabledScratchOrg) { + if (element.SfdxAuthUrl__c && !isValidSfdxAuthUrl(element.SfdxAuthUrl__c)) { + SFPLogger.log( + `Iterating through pool to find a scratch org with valid authURL`, + LoggerLevel.TRACE + ); + continue; + } + } + + + + SFPLogger.log( + `Scratch org ${element.SignupUsername} is allocated from the pool. Expiry date is ${element.ExpirationDate}`, + LoggerLevel.TRACE + ); + let soDetail: any = {}; + soDetail['Id'] = element.Id; + soDetail.orgId = element.ScratchOrg; + soDetail.loginURL = element.LoginUrl; + soDetail.username = element.SignupUsername; + soDetail.password = element.Password__c; + soDetail.expiryDate = element.ExpirationDate; + soDetail.sfdxAuthUrl = element.SfdxAuthUrl__c; + soDetail.status = 'Available'; + soDetail.alias = `SO` + count; + fetchedSOs.push(soDetail); + + + if(limitBy && count==limitBy) + break; + + count++; + } + } + + for (const soDetail of fetchedSOs) { + //Login to the org + let isLoginSuccessFull = await this.loginToScratchOrgIfSfdxAuthURLExists(soDetail); + if (!isLoginSuccessFull) { + SFPLogger.log(`Unable to login to scratchorg ${soDetail.username}}`, LoggerLevel.ERROR); + fetchedSOs = fetchedSOs.filter((item) => item.username !== soDetail.username); + } + } + + return fetchedSOs; + } + + private async fetchSingleScratchOrg(availableSo: any[]): Promise { + let soDetail: ScratchOrg; + + if (availableSo.length > 0) { + SFPLogger.log(`${this.tag} pool has ${availableSo.length} Scratch orgs available`, LoggerLevel.TRACE); + + for (let element of availableSo) { + if (this.authURLEnabledScratchOrg) { + if (element.SfdxAuthUrl__c && !isValidSfdxAuthUrl(element.SfdxAuthUrl__c)) { + SFPLogger.log( + `Iterating through pool to find a scratch org with valid authURL`, + LoggerLevel.TRACE + ); + continue; + } + } + + let allocateSO = await new ScratchOrgInfoAssigner(this.hubOrg).setScratchOrgInfo({ + Id: element.Id, + Allocation_status__c: 'Allocate', + }); + if (allocateSO === true) { + SFPLogger.log( + `Scratch org ${element.SignupUsername} is allocated from the pool. Expiry date is ${element.ExpirationDate}`, + LoggerLevel.TRACE + ); + soDetail = {}; + soDetail['Id'] = element.Id; + soDetail.orgId = element.ScratchOrg; + soDetail.loginURL = element.LoginUrl; + soDetail.username = element.SignupUsername; + soDetail.password = element.Password__c; + soDetail.expiryDate = element.ExpirationDate; + soDetail.sfdxAuthUrl = element.SfdxAuthUrl__c; + soDetail.status = 'Assigned'; + + break; + } else { + SFPLogger.log( + `Scratch org ${element.SignupUsername} allocation failed. trying to get another Scratch org from ${this.tag} pool`, + LoggerLevel.TRACE + ); + } + } + } + + if (availableSo.length == 0 || !soDetail) { + throw new Error(`No scratch org available at the moment for ${this.tag}, try again in sometime.`); + } + + if (this.sendToUser) { + //Fetch the email for user id + let emailId; + try { + emailId = await getUserEmail(this.sendToUser, this.hubOrg); + } catch (error) { + SFPLogger.log( + 'Unable to fetch details of the specified user, Check whether the user exists in the org ', + LoggerLevel.ERROR + ); + throw new Error('Failed to fetch user details'); + } + + try { + //Send an email for username + await new ScratchOrgOperator(this.hubOrg).shareScratchOrgThroughEmail(emailId, soDetail); + } catch (error) { + SFPLogger.log( + 'Unable to send the scratchorg details to specified user. Check whether the user exists in the org', + LoggerLevel.ERROR + ); + throw new Error( + 'Unable to send the scratchorg details to specified user. Check whether the user exists in the org' + ); + } + } else { + //Login to the org + let isLoginSuccessFull = await this.loginToScratchOrgIfSfdxAuthURLExists(soDetail); + //Attempt to Fetch Source Tracking Files and silently continue if it fails + if (isLoginSuccessFull && this.isSourceTrackingToBeSet) { + try { + const conn = (await Org.create({ aliasOrUsername: soDetail.username })).getConnection(); + const clientSourceTracking = await ClientSourceTracking.create(conn, null); + await clientSourceTracking.creatSourceTrackingFiles(); + } catch (error) { + SFPLogger.log('Retriveing Source Tracking skipped.. ' + error.message, LoggerLevel.TRACE); + } + } + } + return soDetail; + } + + private async loginToScratchOrgIfSfdxAuthURLExists(soDetail: ScratchOrg): Promise { + try { + if (soDetail.sfdxAuthUrl && isValidSfdxAuthUrl(soDetail.sfdxAuthUrl)) { + + + const oauth2Options = AuthInfo.parseSfdxAuthUrl(soDetail.sfdxAuthUrl); + const authInfo = await AuthInfo.create({ oauth2Options }); + await authInfo.save({...authInfo.getFields(),isScratch:true,devHubUsername:this.hubOrg.getUsername(),expirationDate:soDetail.expiryDate}); + + await authInfo.handleAliasAndDefaultSettings({ + alias: this.alias?this.alias:soDetail.alias, + setDefault: true, + setDefaultDevHub: false, + }); + + const result = authInfo.getFields(true); + // ensure the clientSecret field... even if it is empty + // as per https://github.com/salesforcecli/plugin-auth/blob/main/src/commands/auth/sfdxurl/store.ts + result.clientSecret = result.clientSecret ?? ''; + await AuthInfo.identifyPossibleScratchOrgs(result, authInfo); + + return true; + } else { + SFPLogger.log('Unable to autenticate to the scratch org', LoggerLevel.INFO); + return false; + } + } catch (error) { + SFPLogger.log('Unable to autenticate to the scratch org due ' + error.message, LoggerLevel.ERROR); + return false; + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolJobExecutor.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolJobExecutor.ts new file mode 100644 index 000000000..1d70e4aff --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolJobExecutor.ts @@ -0,0 +1,41 @@ +import { Org } from '@salesforce/core'; +import { PoolConfig } from './PoolConfig'; +import ScratchOrg from '../ScratchOrg'; +import { Result } from 'neverthrow'; +import * as fs from 'fs-extra'; +import { EOL } from 'os'; +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; + +export default abstract class PoolJobExecutor { + protected logToFilePath: string; + + constructor(protected pool: PoolConfig) {} + + async execute( + scratchOrg: ScratchOrg, + hubOrg: Org, + logLevel: LoggerLevel + ): Promise> { + this.logToFilePath = `.sfp/prepare_logs/${scratchOrg.alias}.log`; + //Create file logger + fs.outputFileSync(this.logToFilePath, `sfp--log${EOL}`); + SFPLogger.log(`Preparation Log files for ${scratchOrg.username} written to ${this.logToFilePath}`); + return this.executeJob(scratchOrg, hubOrg, this.logToFilePath, logLevel); + } + + abstract executeJob( + scratchOrg: ScratchOrg, + hubOrg: Org, + logToFilePath: string, + logLevel: LoggerLevel + ): Promise>; +} + +export interface ScriptExecutionResult { + scratchOrgUsername: string; +} + +export interface JobError { + message: string; + scratchOrgUsername: string; +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolListImpl.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolListImpl.ts new file mode 100644 index 000000000..bf5ad0d64 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolListImpl.ts @@ -0,0 +1,48 @@ +import { Org } from '@salesforce/core'; +import { PoolBaseImpl } from './PoolBaseImpl'; +import ScratchOrg from '../ScratchOrg'; +import ScratchOrgInfoFetcher from './services/fetchers/ScratchOrgInfoFetcher'; + +export default class PoolListImpl extends PoolBaseImpl { + private tag: string; + private allScratchOrgs: boolean; + + public constructor(hubOrg: Org, tag: string, allScratchOrgs: boolean) { + super(hubOrg); + this.hubOrg = hubOrg; + this.tag = tag; + this.allScratchOrgs = allScratchOrgs; + } + + protected async onExec(): Promise { + const results = (await new ScratchOrgInfoFetcher(this.hubOrg).getScratchOrgsByTag( + this.tag, + null, + !this.allScratchOrgs + )) as any; + + let scratchOrgList: ScratchOrg[] = new Array(); + if (results.records.length > 0) { + for (let element of results.records) { + let soDetail: ScratchOrg = {}; + soDetail.tag = element.Pooltag__c; + soDetail.orgId = element.ScratchOrg; + soDetail.loginURL = element.LoginUrl; + soDetail.username = element.SignupUsername; + soDetail.password = element.Password__c; + soDetail.expiryDate = element.ExpirationDate; + if (element.Allocation_status__c === 'Assigned') { + soDetail.status = 'In use'; + } else if (element.Allocation_status__c === 'Available') { + soDetail.status = 'Available'; + } else { + soDetail.status = 'Provisioning in progress'; + } + + scratchOrgList.push(soDetail); + } + } + + return scratchOrgList; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolOrgDeleteImpl.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolOrgDeleteImpl.ts new file mode 100644 index 000000000..435d6fac7 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/PoolOrgDeleteImpl.ts @@ -0,0 +1,26 @@ +import { Org } from '@salesforce/core'; +import { PoolBaseImpl } from './PoolBaseImpl'; +import ScratchOrgInfoFetcher from './services/fetchers/ScratchOrgInfoFetcher'; +import ScratchOrgOperator from '../ScratchOrgOperator'; + +export default class PoolOrgDeleteImpl extends PoolBaseImpl { + username: string; + + public constructor(hubOrg: Org, username: string) { + super(hubOrg); + this.hubOrg = hubOrg; + this.username = username; + } + + protected async onExec(): Promise { + try { + let scratchOrgId = await new ScratchOrgInfoFetcher(this.hubOrg).getScratchOrgIdGivenUserName(this.username); + await new ScratchOrgOperator(this.hubOrg).delete(scratchOrgId); + } catch (err) { + throw new Error( + `Either the scratch org doesn't exist or you do not have the correct permissions, Failed with ` + + err.message + ); + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/prequisitecheck/IsValidSfdxAuthUrl.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/prequisitecheck/IsValidSfdxAuthUrl.ts new file mode 100644 index 000000000..9cd50260a --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/prequisitecheck/IsValidSfdxAuthUrl.ts @@ -0,0 +1,19 @@ +export default function isValidSfdxAuthUrl(sfdxAuthUrl: string): boolean { + if (sfdxAuthUrl.match(/force:\/\/(?[a-zA-Z0-9._]+)@.+/)) { + return true; + } else { + let match = sfdxAuthUrl.match( + /force:\/\/(?[a-zA-Z0-9._=]+):(?[a-zA-Z0-9]*):(?[a-zA-Z0-9._=]+)@.+/ + ); + + if (match !== null) { + if (match.groups.refreshToken === 'undefined') { + return false; + } else { + return true; + } + } else { + return false; + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/prequisitecheck/PreRequisiteCheck.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/prequisitecheck/PreRequisiteCheck.ts new file mode 100644 index 000000000..18da896a9 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/prequisitecheck/PreRequisiteCheck.ts @@ -0,0 +1,63 @@ +import { Org } from '@salesforce/core'; +const retry = require('async-retry'); +import { Result, ok, err } from 'neverthrow'; +import { PoolConfig } from '../PoolConfig'; +import { PoolError, PoolErrorCodes } from '../PoolError'; + +export default class PreRequisiteCheck { + private static isPrerequisiteChecked: boolean = false; + private static isPrerequisiteMet = false; + private static describeResult; + + private hubOrg: Org; + + constructor(hubOrg: Org) { + this.hubOrg = hubOrg; + } + + public async checkForPrerequisites(): Promise { + let sfdxAuthUrlFieldExists = false; + let conn = this.hubOrg.getConnection(); + let expectedValues = ['In Progress', 'Available', 'Allocate', 'Assigned','Return']; + let availableValues: string[] = []; + if (!PreRequisiteCheck.isPrerequisiteChecked) { + await retry( + async (bail) => { + PreRequisiteCheck.describeResult = await conn.sobject('ScratchOrgInfo').describe(); + if (PreRequisiteCheck.describeResult) { + for (const field of PreRequisiteCheck.describeResult.fields) { + if (field.name === 'SfdxAuthUrl__c') { + sfdxAuthUrlFieldExists = true; + } + + if (field.name === 'Allocation_status__c' && field.picklistValues.length >= 4) { + for (let picklistValue of field.picklistValues) { + if (picklistValue.active) { + availableValues.push(picklistValue.value); + } + } + } + } + } + }, + { retries: 3, minTimeout: 30000 } + ); + + PreRequisiteCheck.isPrerequisiteChecked = true; + //If there are values returned, its not compatible + let statusValuesAvailable = + expectedValues.filter((item) => { + return !availableValues.includes(item); + }).length <= 1 + ? true + : false; + + if (sfdxAuthUrlFieldExists && statusValuesAvailable) PreRequisiteCheck.isPrerequisiteMet = true; + } + + if (!PreRequisiteCheck.isPrerequisiteMet) { + throw new Error( `Required Prerequisite values in ScratchOrgInfo is missing in the DevHub` + + `For more information Please refer https://sfp.flxblio.io/getting-started/prerequisites \n`); + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/fetchers/GetUserEmail.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/fetchers/GetUserEmail.ts new file mode 100644 index 000000000..e0c34bf9e --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/fetchers/GetUserEmail.ts @@ -0,0 +1,28 @@ +import { LoggerLevel, Org } from '@salesforce/core'; + +let retry = require('async-retry'); +import SFPLogger from '@flxblio/sfp-logger'; + +export async function getUserEmail(username: string, hubOrg: Org) { + let hubConn = hubOrg.getConnection(); + + return retry( + async (bail) => { + if (!username) { + bail(new Error('username cannot be null. provide a valid username')); + return; + } + let query = `SELECT email FROM user WHERE username='${username}'`; + + SFPLogger.log('QUERY:' + query, LoggerLevel.TRACE); + const results = (await hubConn.query(query)) as any; + + if (results.records.size < 1) { + bail(new Error(`No user found with username ${username} in devhub.`)); + return; + } + return results.records[0].Email; + }, + { retries: 3, minTimeout: 3000 } + ); +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/fetchers/ScratchOrgInfoFetcher.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/fetchers/ScratchOrgInfoFetcher.ts new file mode 100644 index 000000000..a89ad737a --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/fetchers/ScratchOrgInfoFetcher.ts @@ -0,0 +1,187 @@ +import SFPLogger, { LoggerLevel } from '@flxblio/sfp-logger'; +import { Org } from '@salesforce/core'; +import ScratchOrg from '../../../ScratchOrg'; +const retry = require('async-retry'); +const ORDER_BY_FILTER = ' ORDER BY CreatedDate DESC'; + +export default class ScratchOrgInfoFetcher { + constructor(private hubOrg: Org) {} + + public async getScratchOrgRecordId(scratchOrgs: ScratchOrg[]) { + if (scratchOrgs == undefined || scratchOrgs.length == 0) return; + + let hubConn = this.hubOrg.getConnection(); + + let scratchOrgIds = scratchOrgs + .map(function (scratchOrg) { + scratchOrg.orgId = scratchOrg.orgId.slice(0, 15); + return `'${scratchOrg.orgId}'`; + }) + .join(','); + + let query = `SELECT Id, ScratchOrg FROM ScratchOrgInfo WHERE ScratchOrg IN ( ${scratchOrgIds} )`; + SFPLogger.log('QUERY:' + query, LoggerLevel.TRACE); + + return retry( + async (bail) => { + const results = (await hubConn.query(query)) as any; + let resultAsObject = this.arrayToObject(results.records, 'ScratchOrg'); + + SFPLogger.log(JSON.stringify(resultAsObject), LoggerLevel.TRACE); + + scratchOrgs.forEach((scratchOrg) => { + scratchOrg.recordId = resultAsObject[scratchOrg.orgId]['Id']; + }); + + return scratchOrgs; + }, + { retries: 3, minTimeout: 3000 } + ); + } + + public async getScratchOrgsByTag(tag: string, isMyPool: boolean, unAssigned: boolean) { + let hubConn = this.hubOrg.getConnection(); + + return retry( + async (bail) => { + let query; + + if (tag) + query = `SELECT Pooltag__c, Id, CreatedDate, ScratchOrg, ExpirationDate, SignupUsername, SignupEmail, Password__c, Allocation_status__c, LoginUrl, SfdxAuthUrl__c FROM ScratchOrgInfo WHERE Pooltag__c = '${tag}' AND Status = 'Active' `; + else + query = `SELECT Pooltag__c, Id, CreatedDate, ScratchOrg, ExpirationDate, SignupUsername, SignupEmail, Password__c, Allocation_status__c, LoginUrl, SfdxAuthUrl__c FROM ScratchOrgInfo WHERE Pooltag__c != null AND Status = 'Active' `; + + if (isMyPool) { + query = query + ` AND createdby.username = '${this.hubOrg.getUsername()}' `; + } + if (unAssigned) { + // if new version compatible get Available / In progress + query = + query + `AND ( Allocation_status__c ='Available' OR Allocation_status__c = 'In Progress' ) `; + } + query = query + ORDER_BY_FILTER; + SFPLogger.log('QUERY:' + query, LoggerLevel.TRACE); + const results = (await hubConn.query(query)) as any; + return results; + }, + { retries: 3, minTimeout: 3000 } + ); + } + + public async getOrphanedScratchOrgs() { + let hubConn = this.hubOrg.getConnection(); + + return retry( + async (bail) => { + let query; + query = `SELECT Id, Pooltag__c, SignupUsername, Description, ScratchOrg FROM ScratchOrgInfo WHERE Pooltag__c = null AND Status = 'Active'`; + query = query + ORDER_BY_FILTER; + SFPLogger.log('QUERY:' + query, LoggerLevel.TRACE); + const results = (await hubConn.query(query)) as any; + return results; + }, + { retries: 3, minTimeout: 3000 } + ); + } + + public async getActiveScratchOrgsByInfoId(scrathOrgIds: string) { + let hubConn = this.hubOrg.getConnection(); + + return retry( + async (bail) => { + let query = `SELECT Id, SignupUsername FROM ActiveScratchOrg WHERE ScratchOrgInfoId IN (${scrathOrgIds}) `; + + SFPLogger.log('QUERY:' + query, LoggerLevel.TRACE); + const results = (await hubConn.query(query)) as any; + return results; + }, + { retries: 3, minTimeout: 3000 } + ); + } + + public async getCountOfActiveScratchOrgsByTag(tag: string): Promise { + let hubConn = this.hubOrg.getConnection(); + + return retry( + async (bail) => { + let query = `SELECT Id, CreatedDate, ScratchOrg, ExpirationDate, SignupUsername, SignupEmail, Password__c, Allocation_status__c, LoginUrl FROM ScratchOrgInfo WHERE Pooltag__c = '${tag}' AND Status = 'Active' `; + SFPLogger.log('QUERY:' + query, LoggerLevel.TRACE); + const results = (await hubConn.query(query)) as any; + SFPLogger.log('RESULT:' + JSON.stringify(results), LoggerLevel.TRACE); + return results.totalSize; + }, + { retries: 3, minTimeout: 3000 } + ); + } + + public async getCountOfActiveScratchOrgsByTagAndUsername(tag: string): Promise { + let hubConn = this.hubOrg.getConnection(); + + return retry( + async (bail) => { + let query = `SELECT Id, CreatedDate, ScratchOrg, ExpirationDate, SignupUsername, SignupEmail, Password__c, Allocation_status__c, LoginUrl FROM ScratchOrgInfo WHERE Pooltag__c = '${tag}' AND Status = 'Active' `; + SFPLogger.log('QUERY:' + query, LoggerLevel.TRACE); + const results = (await hubConn.query(query)) as any; + return results.totalSize; + }, + { retries: 3, minTimeout: 3000 } + ); + } + + public async getActiveScratchOrgRecordIdGivenScratchOrg(scratchOrgId: string): Promise { + let hubConn = this.hubOrg.getConnection(); + return retry( + async (bail) => { + let query = `SELECT Id FROM ActiveScratchOrg WHERE ScratchOrg = '${scratchOrgId}'`; + let records = (await hubConn.query(query)).records; + + SFPLogger.log('Retrieve Active ScratchOrg Id:' + JSON.stringify(records), LoggerLevel.TRACE); + return records[0].Id; + }, + { retries: 3, minTimeout: 3000 } + ); + } + + public async getActiveScratchOrgRecordsAsMapByUser(hubOrg: Org) { + let conn = this.hubOrg.getConnection(); + let query = + 'SELECT count(id) In_Use, SignupEmail FROM ActiveScratchOrg GROUP BY SignupEmail ORDER BY count(id) DESC'; + const results = (await conn.query(query)) as any; + SFPLogger.log(`Info Fetched: ${JSON.stringify(results)}`, LoggerLevel.DEBUG); + + let scratchOrgRecordAsMapByUser = this.arrayToObject(results.records, 'SignupEmail'); + return scratchOrgRecordAsMapByUser; + } + + public async getScratchOrgIdGivenUserName(username: string) { + let conn = this.hubOrg.getConnection(); + let query = `SELECT Id FROM ActiveScratchOrg WHERE SignupUsername = '${username}'`; + return retry( + async (bail) => { + SFPLogger.log('QUERY:' + query, LoggerLevel.TRACE); + const results = (await conn.query(query)) as any; + return results.records[0].Id; + }, + { retries: 3, minTimeout: 3000 } + ); + } + + public async getScratchOrgInfoIdGivenUserName(username: string) { + let conn = this.hubOrg.getConnection(); + let query = `SELECT Id FROM ScratchOrgInfo WHERE SignupUsername = '${username}'`; + return retry( + async (bail) => { + SFPLogger.log('QUERY:' + query, LoggerLevel.TRACE); + const results = (await conn.query(query)) as any; + return results.records[0].Id; + }, + { retries: 3, minTimeout: 3000 } + ); + } + + private arrayToObject = (array, keyfield) => + array.reduce((obj, item) => { + obj[item[keyfield]] = item; + return obj; + }, {}); +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/fetchers/ScratchOrgLimitsFetcher.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/fetchers/ScratchOrgLimitsFetcher.ts new file mode 100644 index 000000000..d8d54a45e --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/fetchers/ScratchOrgLimitsFetcher.ts @@ -0,0 +1,13 @@ +import { Org } from '@salesforce/core'; + +export default class ScratchOrgLimitsFetcher { + constructor(private hubOrg: Org) {} + + public async getScratchOrgLimits(): Promise { + let conn = this.hubOrg.getConnection(); + let apiVersion = await conn.retrieveMaxApiVersion(); + let query_uri = `${conn.instanceUrl}/services/data/v${apiVersion}/limits`; + const result = await conn.request(query_uri); + return result; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/updaters/ScratchOrgInfoAssigner.ts b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/updaters/ScratchOrgInfoAssigner.ts new file mode 100644 index 000000000..5055b8cc3 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scratchorg/pool/services/updaters/ScratchOrgInfoAssigner.ts @@ -0,0 +1,24 @@ +import { LoggerLevel, Org } from '@salesforce/core'; +let retry = require('async-retry'); +import SFPLogger from '@flxblio/sfp-logger'; +import ScratchOrgInfoFetcher from '../fetchers/ScratchOrgInfoFetcher'; +import ObjectCRUDHelper from '../../../../utils/ObjectCRUDHelper'; + +export default class ScratchOrgInfoAssigner { + constructor(private hubOrg: Org) {} + + public async setScratchOrgInfo(soInfo: any): Promise { + let hubConn = this.hubOrg.getConnection(); + let result = await ObjectCRUDHelper.updateRecord(hubConn,'ScratchOrgInfo',soInfo); + if(result) return true; + } + + public async setScratchOrgStatus(username: string, status: 'Allocate' | 'InProgress' | 'Return'): Promise { + let scratchOrgId = await new ScratchOrgInfoFetcher(this.hubOrg).getScratchOrgInfoIdGivenUserName(username); + + return this.setScratchOrgInfo({ + Id: scratchOrgId, + Allocation_status__c: status, + }); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/scriptExecutor/ScriptExecutorHelpers.ts b/packages/sfpowerscripts-cli/src/core/scriptExecutor/ScriptExecutorHelpers.ts new file mode 100644 index 000000000..0c7f92965 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/scriptExecutor/ScriptExecutorHelpers.ts @@ -0,0 +1,19 @@ +import ExecuteCommand from '@flxblio/sfdx-process-wrapper/lib/commandExecutor/ExecuteCommand'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import defaultShell from '../utils/DefaultShell'; + +export default class scriptExecutorHelpers { + static async executeScript(logger: Logger, ...args: string[]) { + let cmd: string; + let argStr =args.join(' '); + if (process.platform !== 'win32') { + cmd = `${defaultShell()} -e ${argStr}`; + } else { + cmd = `cmd.exe /c ${argStr}`; + } + + SFPLogger.log(`Executing command.. ${cmd}`,LoggerLevel.INFO,logger); + let scriptExecutor: ExecuteCommand = new ExecuteCommand(logger, LoggerLevel.INFO, true); + let result = await scriptExecutor.execCommand(cmd, null); + } + } diff --git a/packages/sfpowerscripts-cli/src/core/sfdmuwrapper/SFDMURunImpl.ts b/packages/sfpowerscripts-cli/src/core/sfdmuwrapper/SFDMURunImpl.ts new file mode 100644 index 000000000..4df46887d --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/sfdmuwrapper/SFDMURunImpl.ts @@ -0,0 +1,28 @@ +import { SFDXCommand } from '@flxblio/sfdx-process-wrapper/lib/SFDXCommand'; +import { Logger, LoggerLevel } from '@flxblio/sfp-logger'; + +export default class SFDMURunImpl extends SFDXCommand { + public constructor( + working_directory: string, + target_org: string, + private targetOrgDomain: string, + private packageDirectory: string, + logger: Logger, + logLevel: LoggerLevel + ) { + super(target_org, working_directory, logger, logLevel); + } + + getSFDXCommand(): string { + return 'sf sfdmu run'; + } + getCommandName(): string { + return 'sfdmu run'; + } + + getGeneratedParams(): string { + let command = `--path ${this.packageDirectory} -s csvfile -u ${this.target_org} --noprompt --canmodify ${this.targetOrgDomain}`; + if (this.logLevel) command += ` --loglevel ${LoggerLevel[this.logLevel]}`; + return command; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/stats/NativeMetricSender.ts b/packages/sfpowerscripts-cli/src/core/stats/NativeMetricSender.ts new file mode 100644 index 000000000..b68631635 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/stats/NativeMetricSender.ts @@ -0,0 +1,22 @@ +import { Logger } from '@flxblio/sfp-logger'; + +export abstract class NativeMetricSender { + constructor(protected logger: Logger) {} + + abstract initialize(apiHost: string, apiKey: string): void; + + abstract sendGaugeMetric(metric: string, value: number, tags: string[] | { [key: string]: string }): void; + + abstract sendCountMetric(metric: string, tags: string[] | { [key: string]: string }): void; + + protected transformTagsToStringArray(tags: { [key: string]: string } | string[]): string[] { + if (tags != null && !Array.isArray(tags)) { + let transformedTagArray: string[] = []; + for (const [key, value] of Object.entries(tags)) { + transformedTagArray.push(`${key}:${value}`); + } + return transformedTagArray; + } + return tags as string[]; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/stats/SFPStatsSender.ts b/packages/sfpowerscripts-cli/src/core/stats/SFPStatsSender.ts new file mode 100644 index 000000000..24e0bd165 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/stats/SFPStatsSender.ts @@ -0,0 +1,114 @@ +import StatsDClient, { ClientOptions, StatsD } from 'hot-shots'; +import * as fs from 'fs-extra'; +import { EOL } from 'os'; +import { NativeMetricSender } from './NativeMetricSender'; +import { DataDogMetricsSender } from './nativeMetricSenderImpl/DataDogMetricSender'; +import { Logger } from '@flxblio/sfp-logger'; +import { NewRelicMetricSender } from './nativeMetricSenderImpl/NewRelicMetricSender'; +import { SplunkMetricSender } from './nativeMetricSenderImpl/SplunkMetricSender'; + +export default class SFPStatsSender { + private static client: StatsD; + private static metricsLogger; + private static nativeMetricsSender: NativeMetricSender; + + static initialize(port: string, host: string, protocol: string) { + let options: ClientOptions = { + host: host, + port: port == null ? 8125 : Number(port), + protocol: protocol == 'tcp' ? 'tcp' : 'udp', + prefix: 'sfp.', + }; + SFPStatsSender.client = new StatsDClient(options); + } + + static initializeNativeMetrics(type: string, apiHost: string, apiKey: string, logger?: Logger) { + switch (type) { + case 'DataDog': + this.nativeMetricsSender = new DataDogMetricsSender(logger); + this.nativeMetricsSender.initialize(apiHost, apiKey); + break; + + case 'NewRelic': + this.nativeMetricsSender = new NewRelicMetricSender(logger); + this.nativeMetricsSender.initialize(apiHost, apiKey); + break; + + case 'Splunk': + this.nativeMetricsSender = new SplunkMetricSender(logger); + this.nativeMetricsSender.initialize(apiHost, apiKey); + break; + + default: + throw new Error('Invalid Metric Type'); + } + } + + static initializeLogBasedMetrics() { + try { + fs.mkdirpSync('.sfp/logs'); + SFPStatsSender.metricsLogger = `.sfp/logs/metrics.log`; + } catch (error) { + console.log('Unable to initiate Log based metrics', error); + } + } + + static logElapsedTime(metric: string, elapsedMilliSeconds: number, tags?: { [key: string]: string } | string[]) { + if (SFPStatsSender.client != null) SFPStatsSender.client.timing(metric, elapsedMilliSeconds, tags); + + //Native Datadog integration + if (SFPStatsSender.nativeMetricsSender != null) { + SFPStatsSender.nativeMetricsSender.sendGaugeMetric(metric, elapsedMilliSeconds, tags); + } + + let metrics = { + metric: `sfp.${metric}`, + type: `timers`, + value: elapsedMilliSeconds, + timestamp: Date.now(), + tags: tags, + }; + SFPStatsSender.logMetrics(metrics, SFPStatsSender.metricsLogger); + } + + static logGauge(metric: string, value: number, tags?: { [key: string]: string } | string[]) { + if (SFPStatsSender.client != null) SFPStatsSender.client.gauge(metric, value, tags); + + //Native Metrics integration + if (SFPStatsSender.nativeMetricsSender != null) { + SFPStatsSender.nativeMetricsSender.sendGaugeMetric(metric, value, tags); + } + + let metrics = { + metric: `sfp.${metric}`, + type: `guage`, + value: value, + timestamp: Date.now(), + tags: tags, + }; + SFPStatsSender.logMetrics(metrics, SFPStatsSender.metricsLogger); + } + + static logCount(metric: string, tags?: { [key: string]: string } | string[]) { + if (SFPStatsSender.client != null) SFPStatsSender.client.increment(metric, tags); + + //Native Metrics integration + if (SFPStatsSender.nativeMetricsSender != null) { + SFPStatsSender.nativeMetricsSender.sendCountMetric(metric, tags); + } + + let metrics = { + metric: `sfp.${metric}`, + type: `count`, + timestamp: Date.now(), + tags: tags, + }; + SFPStatsSender.logMetrics(metrics, SFPStatsSender.metricsLogger); + } + + static logMetrics(key: any, logger?: any) { + if (logger) { + fs.appendFileSync(logger, `${JSON.stringify(key)}${EOL}`, 'utf8'); + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/DataDogMetricSender.ts b/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/DataDogMetricSender.ts new file mode 100644 index 000000000..b1e6cb574 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/DataDogMetricSender.ts @@ -0,0 +1,52 @@ +import { BufferedMetricsLogger } from 'datadog-metrics'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { NativeMetricSender } from '../NativeMetricSender'; + +export class DataDogMetricsSender extends NativeMetricSender { + constructor(logger: Logger) { + super(logger); + } + + private nativeDataDogMetricsLogger: BufferedMetricsLogger; + + public initialize(apiHost: string, apiKey: string) { + try { + this.nativeDataDogMetricsLogger = new BufferedMetricsLogger({ + apiHost: apiHost, + apiKey: apiKey, + prefix: 'sfp.', + flushIntervalSeconds: 0, + }); + } catch (error) { + SFPLogger.log('Unable to intialize native datadog logger' + error, LoggerLevel.TRACE, this.logger); + } + } + + public sendGaugeMetric(metric: string, value: number, tags: string[] | { [key: string]: string }) { + try { + let transformedTags = this.transformTagsToStringArray(tags); + this.nativeDataDogMetricsLogger.gauge(metric, value, transformedTags); + this.nativeDataDogMetricsLogger.flush(); + } catch (error) { + SFPLogger.log( + `Unable to transmit metrics for metric ${metric} due to` + error, + LoggerLevel.TRACE, + this.logger + ); + } + } + + public sendCountMetric(metric: string, tags: string[] | { [key: string]: string }) { + try { + let transformedTags = this.transformTagsToStringArray(tags); + this.nativeDataDogMetricsLogger.increment(metric, 1, transformedTags); + this.nativeDataDogMetricsLogger.flush(); + } catch (error) { + SFPLogger.log( + `Unable to transmit metrics for metric ${metric} due to` + error, + LoggerLevel.TRACE, + this.logger + ); + } + } +} diff --git a/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/NewRelicMetricSender.ts b/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/NewRelicMetricSender.ts new file mode 100644 index 000000000..bc6d6130d --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/NewRelicMetricSender.ts @@ -0,0 +1,66 @@ +import { NativeMetricSender } from '../NativeMetricSender'; +import { telemetry } from '@newrelic/telemetry-sdk'; +import { + CountMetric, + GaugeMetric, + MetricBatch, + MetricClient, +} from '@newrelic/telemetry-sdk/dist/src/telemetry/metrics'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; + +export class NewRelicMetricSender extends NativeMetricSender { + constructor(logger: Logger) { + super(logger); + } + + private nrMetricClient: telemetry.metrics.MetricClient; + + //Ignore API Host, as newrelic sdk doesnt need it + public initialize(apiHost: string, apiKey: string) { + try { + this.nrMetricClient = new MetricClient({ apiKey: apiKey }); + } catch (error) { + SFPLogger.log(`Unable to intialize native newrelic metric logger ${error}`, LoggerLevel.WARN, this.logger); + } + } + + public sendGaugeMetric(metric: string, value: number, tags: string[] | { [key: string]: string }) { + metric = `sfp.${metric}`; + const guageMetric = new GaugeMetric(metric, value); + guageMetric.attributes = tags as { [key: string]: string }; + const batch = new MetricBatch({}, Date.now(), 1); + batch.addMetric(guageMetric); + this.nrMetricClient.send(batch, (error, response, body) => { + if (response) { + SFPLogger.log(`Transmitted metric ${metric} ${response.statusCode}`, LoggerLevel.TRACE, this.logger); + } + if (error) + SFPLogger.log( + `Unable to transmit metrics for metric ${metric} due to` + error, + LoggerLevel.WARN, + this.logger + ); + }); + } + + public sendCountMetric(metric: string, tags: string[] | { [key: string]: string }) { + metric = `sfp.${metric}`; + const countMetric = new CountMetric(metric); + countMetric.record(1); + countMetric.attributes = tags as { [key: string]: string }; + const batch = new MetricBatch({}, Date.now(), 1); + batch.addMetric(countMetric); + + this.nrMetricClient.send(batch, (error, response, body) => { + if (response) { + SFPLogger.log(`Transmitted metric ${metric} ${response.statusCode}`, LoggerLevel.TRACE, this.logger); + } + if (error) + SFPLogger.log( + `Unable to transmit metrics for metric ${metric} due to` + error, + LoggerLevel.WARN, + this.logger + ); + }); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/SplunkMetricSender.ts b/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/SplunkMetricSender.ts new file mode 100644 index 000000000..f92a5f5d8 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/stats/nativeMetricSenderImpl/SplunkMetricSender.ts @@ -0,0 +1,49 @@ +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { NativeMetricSender } from '../NativeMetricSender'; +import axios ,{AxiosInstance} from 'axios'; + + + +export class SplunkMetricSender extends NativeMetricSender { + constructor(logger: Logger) { + super(logger); + } + + private instance: AxiosInstance; + + public initialize(apiHost: string, apiKey: string) { + this.instance = axios.create({ + baseURL: apiHost, + headers: {'Authorization': apiKey, 'Content-Type': 'application/json'} + }); + } + + public sendGaugeMetric(metric: string, value: number, tags: string[] | { [key: string]: string }) { + metric = `sfp.${metric}`; + const payload = {source: "sfp",sourcetype: "metrics",event: {metric: metric, type: 'guage', value: value,tags: tags as { [key: string]: string },timestamp: Date.now()}}; + this.instance.post('', JSON.stringify(payload)) + .then((response) => {SFPLogger.log(`Transmitted metric ${metric} ${response.status}`, LoggerLevel.TRACE, this.logger)}) + .catch((error) => { + SFPLogger.log( + `Unable to transmit metrics for metric ${metric} due to` + error, + LoggerLevel.WARN, + this.logger + ); + }); + } + + public sendCountMetric(metric: string, tags: string[] | { [key: string]: string }) { + metric = `sfp.${metric}`; + const payload = {source: "sfp",sourcetype: "metrics",event: {metric: metric, type: 'count', tags: tags as { [key: string]: string },timestamp: Date.now()}}; + this.instance.post('', JSON.stringify(payload)) + .then((response) => {SFPLogger.log(`Transmitted metric ${metric} ${response.status}`, LoggerLevel.TRACE, this.logger)}) + .catch((error) => { + SFPLogger.log( + `Unable to transmit metrics for metric ${metric} due to` + error, + LoggerLevel.WARN, + this.logger + ); + }); + } +} + diff --git a/packages/sfpowerscripts-cli/src/core/utils/AliasList.ts b/packages/sfpowerscripts-cli/src/core/utils/AliasList.ts new file mode 100644 index 000000000..f7d41dbb3 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/AliasList.ts @@ -0,0 +1,16 @@ +import { StateAggregator } from '@salesforce/core'; + + +export async function convertAliasToUsername(alias: string) { + const stateAggregator = await StateAggregator.getInstance(); + await stateAggregator.orgs.readAll(); + return await stateAggregator.aliases.resolveUsername(alias) +} + +export async function convertUsernameToAlias(username: string) { + + const stateAggregator = await StateAggregator.getInstance(); + await stateAggregator.orgs.readAll(); + return await stateAggregator.aliases.resolveAlias(username) + +} diff --git a/packages/sfpowerscripts-cli/src/core/utils/ChunkArray.ts b/packages/sfpowerscripts-cli/src/core/utils/ChunkArray.ts new file mode 100644 index 000000000..7cd6fa0d8 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/ChunkArray.ts @@ -0,0 +1,11 @@ +export function chunkArray(perChunk: number, inputArray: any[]): Array { + let chunks = [], + i = 0, + n = inputArray.length; + + while (i < n) { + chunks.push(inputArray.slice(i, (i += perChunk))); + } + + return chunks; +} diff --git a/packages/sfpowerscripts-cli/src/core/utils/DefaultShell.ts b/packages/sfpowerscripts-cli/src/core/utils/DefaultShell.ts new file mode 100644 index 000000000..d8b4ae16a --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/DefaultShell.ts @@ -0,0 +1,7 @@ +const sfp_DEFAULT_SHELL = `sh`; + +export default function defaultShell(): string { + return process.env.sfp_DEFAULT_SHELL + ? process.env.sfp_DEFAULT_SHELL + : sfp_DEFAULT_SHELL; +} diff --git a/packages/sfpowerscripts-cli/src/core/utils/Delay.ts b/packages/sfpowerscripts-cli/src/core/utils/Delay.ts new file mode 100644 index 000000000..80eb87107 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/Delay.ts @@ -0,0 +1,3 @@ +export async function delay(ms: number = 0) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/packages/sfpowerscripts-cli/src/core/utils/FileSystem.ts b/packages/sfpowerscripts-cli/src/core/utils/FileSystem.ts new file mode 100644 index 000000000..d77a7744d --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/FileSystem.ts @@ -0,0 +1,52 @@ +import fs = require('fs-extra'); +import path = require('path'); + +export default class FileSystem { + /** + * List nested files within a directory + * @param directory + * @param includeDirectories + * @returns + */ + static readdirRecursive( + searchDirectory: string, + includeDirectories: boolean = false, + isAbsolute: boolean = false + ): string[] { + const result: string[] = []; + + if (!fs.lstatSync(searchDirectory).isDirectory()) throw new Error(`${searchDirectory} is not a directory`); + + (function readdirRecursiveHandler(directory: string): void { + const files: string[] = fs.readdirSync(directory); + + files.forEach((file) => { + let filepath: string; + if (isAbsolute) { + filepath = path.resolve(directory, file); + } else { + filepath = path.join(directory, file); + } + + if (fs.lstatSync(filepath).isDirectory()) { + if (includeDirectories) { + if (isAbsolute) { + result.push(path.resolve(filepath)); + } else { + result.push(path.relative(searchDirectory, filepath)); + } + } + readdirRecursiveHandler(filepath); + } else { + if (isAbsolute) { + result.push(path.resolve(filepath)); + } else { + result.push(path.relative(searchDirectory, filepath)); + } + } + }); + })(searchDirectory); + + return result; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/utils/Fileutils.ts b/packages/sfpowerscripts-cli/src/core/utils/Fileutils.ts new file mode 100644 index 000000000..b121332b0 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/Fileutils.ts @@ -0,0 +1,204 @@ +const fs = require('fs'); +const path = require('path'); +const _ = require('lodash'); +const os = require('os'); + +const SEP = /\/|\\/; + +export const PLUGIN_CACHE_FOLDER = 'sfpowerkit'; + +export default class FileUtils { + /** + * Delete file or directories recursively from the project + * @param deletedComponents Files or directories to delete + */ + public static deleteComponents(deletedComponents: string[]) { + deletedComponents.forEach((component) => { + if (fs.existsSync(component)) { + if (fs.lstatSync(component).isDirectory()) { + FileUtils.deleteFolderRecursive(component); + } else { + fs.unlinkSync(component); + } + } + }); + } + /** + * Load all files from the given folder with the given extension + * @param folder the folder from which files wille be loaded + * @param extension File extension to load. + */ + public static getAllFilesSync(folder: string, extension: string = '.xml'): string[] { + let result: string[] = []; + let pathExists = fs.existsSync(folder); + let folderName = path.basename(folder); + if (!pathExists) { + console.log('Folder does not exist:', folderName); + return result; + } + let content: string[] = fs.readdirSync(folder); + content.forEach((file) => { + let curFile = path.join(folder, file); + let stats = fs.statSync(curFile); + if (stats.isFile()) { + if (extension.indexOf(path.extname(curFile)) != -1 || extension === '') { + result.push(curFile); + } + } else if (stats.isDirectory()) { + let files: string[] = this.getAllFilesSync(curFile, extension); + result = _.concat(result, files); + } + }); + return result; + } + + public static getGlobalCacheDir() { + let homedir = os.homedir(); + let configDir = homedir + path.sep + PLUGIN_CACHE_FOLDER; + if (!fs.existsSync(configDir)) { + console.log('Config folder does not exists, Creating Folder'); + fs.mkdirSync(configDir); + } + + return configDir; + } + + /** + * Get the cache path for the given cache file name + * @param fileName + */ + public static getGlobalCachePath(fileName: string) { + let homedir = os.homedir(); + let configDir = homedir + path.sep + PLUGIN_CACHE_FOLDER; + if (!fs.existsSync(configDir)) { + console.log('Config folder does not exists, Creating Folder'); + fs.mkdirSync(configDir); + } + return configDir + path.sep + fileName; + } + + /** + * Create a folder path recursively + * @param targetDir + * @param param1 + */ + public static mkDirByPathSync(targetDir: string, { isRelativeToScript = false } = {}) { + const sep = path.sep; + const initDir = path.isAbsolute(targetDir) ? sep : ''; + const baseDir = isRelativeToScript ? __dirname : '.'; + + targetDir.split(sep).reduce((parentDir, childDir) => { + const curDir = path.resolve(baseDir, parentDir, childDir); + try { + fs.mkdirSync(curDir); + } catch (err) { + if (err.code !== 'EEXIST' && err.code !== 'EPERM' && err.code !== 'EISDIR') { + throw err; + } + } + return curDir; + }, initDir); + } + /** + * Get the file name withoud extension + * @param filePath file path + * @param extension extension + */ + public static getFileNameWithoutExtension(filePath: string, extension?: string): string { + let fileParts = filePath.split(SEP); + let fileName = fileParts[fileParts.length - 1]; + if (extension) { + fileName = fileName.substr(0, fileName.lastIndexOf(extension)); + } else { + fileName = fileName.substr(0, fileName.indexOf('.')); + } + return fileName; + } + + /** + * Copu folder recursively + * @param src source folder to copy + * @param dest destination folder + */ + public static copyRecursiveSync(src, dest) { + let exists = fs.existsSync(src); + if (exists) { + let stats = fs.statSync(src); + let isDirectory = stats.isDirectory(); + if (isDirectory) { + exists = fs.existsSync(dest); + if (!exists) { + fs.mkdirSync(dest); + } + fs.readdirSync(src).forEach(function (childItemName) { + FileUtils.copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName)); + }); + } else { + fs.copyFileSync(src, dest); + } + } + } + /** + * Get path to a given folder base on the parent folder + * @param src Parent folder + * @param foldername folder to build the path to + */ + public static getFolderPath(src, foldername) { + let exists = fs.existsSync(src); + let toReturn = ''; + if (exists) { + let stats = fs.statSync(src); + let isDirectory = stats.isDirectory(); + if (isDirectory) { + let childs = fs.readdirSync(src); + for (let i = 0; i < childs.length; i++) { + let childItemName = childs[i]; + if (childItemName === foldername) { + toReturn = path.join(src, childItemName); + } else { + let childStat = fs.statSync(path.join(src, childItemName)); + if (childStat.isDirectory()) { + toReturn = FileUtils.getFolderPath(path.join(src, childItemName), foldername); + } + } + if (toReturn !== '') { + break; + } + } + } + } + return toReturn; + } + + /** + * Delete a folder and its content recursively + * @param folder folder to delete + */ + public static deleteFolderRecursive(folder) { + if (fs.existsSync(folder)) { + fs.readdirSync(folder).forEach(function (file, index) { + let curPath = path.join(folder, file); + if (fs.lstatSync(curPath).isDirectory()) { + // recurse + //console.log("Delete recursively"); + FileUtils.deleteFolderRecursive(curPath); + } else { + // delete file + //console.log("Delete file "+ curPath); + fs.unlinkSync(curPath); + } + }); + //console.log("delete folder "+ folder); + fs.rmdirSync(folder); + } + } + public static makefolderid(length): string { + var result = ''; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charactersLength = characters.length; + for (var i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/utils/GetFormattedTime.ts b/packages/sfpowerscripts-cli/src/core/utils/GetFormattedTime.ts new file mode 100644 index 000000000..0c4fd9057 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/GetFormattedTime.ts @@ -0,0 +1,6 @@ +export default function getFormattedTime(milliseconds: number): string { + let date = new Date(0); + date.setMilliseconds(milliseconds); + let timeString = date.toISOString().substr(11, 12); + return timeString; +} diff --git a/packages/sfpowerscripts-cli/src/core/utils/ObjectCRUDHelper.ts b/packages/sfpowerscripts-cli/src/core/utils/ObjectCRUDHelper.ts new file mode 100644 index 000000000..e4db23cc5 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/ObjectCRUDHelper.ts @@ -0,0 +1,38 @@ +import { Connection } from '@salesforce/core'; +import { Record, SaveResult } from 'jsforce'; +import { isArray } from 'lodash'; + +const retry = require('async-retry'); + +export default class ObjectCRUDHelper { + static async updateRecord(conn: Connection, sObject: string, record: Record): Promise { + return retry( + async (bail) => { + let result = await conn.update(sObject, record); + if (isArray(result)) { + let isAllRecordsSucceeded = true; + for (const individualResult of result as SaveResult[]) { + if (!individualResult.success) { + isAllRecordsSucceeded = false; + } + } + if (isAllRecordsSucceeded) return 'All records updated'; + else throw new Error('Some records have been failed to update'); + } else if ((result as SaveResult).success) return (result as SaveResult).id; + else bail(); + }, + { retries: 3, minTimeout: 2000 } + ); + } + + static async createRecord(conn: Connection, sObject: string, record: Record): Promise { + return retry( + async (bail) => { + let result = await conn.create(sObject, record); + if (result.success) return result.id; + else bail(); + }, + { retries: 3, minTimeout: 2000 } + ); + } +} diff --git a/packages/sfpowerscripts-cli/src/core/utils/OnExit.ts b/packages/sfpowerscripts-cli/src/core/utils/OnExit.ts new file mode 100644 index 000000000..5daeff556 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/OnExit.ts @@ -0,0 +1,17 @@ +import { ChildProcess } from 'child_process'; + +export async function onExit(childProcess: ChildProcess, message?: string): Promise<{}> { + return new Promise((resolve, reject) => { + childProcess.once('close', (code: number, signal: string) => { + if (code === 0 || (code === null && signal === 'SIGTERM')) { + resolve(undefined); + } else { + reject(new Error(message ? message : `Exit with error code ${code}`)); + } + }); + + childProcess.once('error', (err: Error) => { + reject(new Error(message ? message : err.message)); + }); + }); +} diff --git a/packages/sfpowerscripts-cli/src/core/utils/RandomId.ts b/packages/sfpowerscripts-cli/src/core/utils/RandomId.ts new file mode 100644 index 000000000..36d0d7272 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/RandomId.ts @@ -0,0 +1,9 @@ +export function makeRandomId(length): string { + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} diff --git a/packages/sfpowerscripts-cli/src/core/utils/VersionNumberConverter.ts b/packages/sfpowerscripts-cli/src/core/utils/VersionNumberConverter.ts new file mode 100644 index 000000000..b86c233eb --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/VersionNumberConverter.ts @@ -0,0 +1,33 @@ +/** + * Converts build-number dot delimeter to hyphen + * If dot delimeter does not exist, returns input + * @param version + */ +export default function convertBuildNumDotDelimToHyphen(version: string) { + let convertedVersion = version; + + let indexOfBuildNumDelimiter = getIndexOfBuildNumDelimeter(version); + if (version[indexOfBuildNumDelimiter] === '.') { + convertedVersion = + version.substring(0, indexOfBuildNumDelimiter) + '-' + version.substring(indexOfBuildNumDelimiter + 1); + } + return convertedVersion; +} + +/** + * Get the index of the build-number delimeter + * Returns null if unable to find index of delimeter + * @param version + */ +function getIndexOfBuildNumDelimeter(version: string) { + let numOfDelimetersTraversed: number = 0; + for (let i = 0; i < version.length; i++) { + if (!Number.isInteger(parseInt(version[i], 10))) { + numOfDelimetersTraversed++; + } + if (numOfDelimetersTraversed === 3) { + return i; + } + } + return null; +} diff --git a/packages/sfpowerscripts-cli/src/core/utils/extractDomainFromUrl.ts b/packages/sfpowerscripts-cli/src/core/utils/extractDomainFromUrl.ts new file mode 100644 index 000000000..1f7844797 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/extractDomainFromUrl.ts @@ -0,0 +1,10 @@ +/** + * Extracts domain name from full URL string + * @param url + * @returns + */ +export default function extractDomainFromUrl(url: string): string { + if (!url) return url; + const matches = url.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i); + return matches && matches[1]; +} diff --git a/packages/sfpowerscripts-cli/src/core/utils/xml2json.ts b/packages/sfpowerscripts-cli/src/core/utils/xml2json.ts new file mode 100644 index 000000000..9fdeb2d78 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/utils/xml2json.ts @@ -0,0 +1,10 @@ +const xmlParser = require('xml2js').Parser({ explicitArray: false }); + +export default function xml2json(xml) { + return new Promise((resolve, reject) => { + xmlParser.parseString(xml, function (err, json) { + if (err) reject(err); + else resolve(json); + }); + }); +} diff --git a/packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityInitialInstall.ts b/packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityInitialInstall.ts new file mode 100644 index 000000000..981f942a5 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityInitialInstall.ts @@ -0,0 +1,21 @@ +import { SFDXCommand } from '@flxblio/sfdx-process-wrapper/lib/SFDXCommand'; +import { Logger, LoggerLevel } from '@flxblio/sfp-logger'; + +export default class VlocityInitialInstall extends SFDXCommand { + public constructor(project_directory: string, target_org: string, logger: Logger, logLevel: LoggerLevel) { + super(target_org, project_directory, logger, logLevel); + } + + getSFDXCommand(): string { + return 'vlocity'; + } + getCommandName(): string { + return 'vlocity:packUpdateSettings'; + } + + getGeneratedParams(): string { + let command = `-sfdx.username ${this.target_org} --nojob installVlocityInitial`; + if (this.logLevel) command += ` -verbose`; + return command; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityPackDeployImpl.ts b/packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityPackDeployImpl.ts new file mode 100644 index 000000000..2cdc70087 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityPackDeployImpl.ts @@ -0,0 +1,31 @@ +import path from 'path'; +import { SFDXCommand } from '@flxblio/sfdx-process-wrapper/lib/SFDXCommand'; +import { Logger, LoggerLevel } from '@flxblio/sfp-logger'; + +export default class VlocityPackDeployImpl extends SFDXCommand { + public constructor( + project_directory: string, + target_org: string, + private packageDirectory: string, + logger: Logger, + logLevel: LoggerLevel + ) { + super(target_org, project_directory, logger, logLevel); + } + + getSFDXCommand(): string { + return 'vlocity'; + } + getCommandName(): string { + return 'vlocity:packDeploy'; + } + + getGeneratedParams(): string { + let command = `-sfdx.username ${this.target_org} -job ${path.join( + this.packageDirectory, + 'VlocityComponents.yaml' + )} packDeploy`; + if (this.logLevel) command += ` -verbose`; + return command; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityPackUpdateSettings.ts b/packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityPackUpdateSettings.ts new file mode 100644 index 000000000..6d4f6584c --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityPackUpdateSettings.ts @@ -0,0 +1,21 @@ +import { SFDXCommand } from '@flxblio/sfdx-process-wrapper/lib/SFDXCommand'; +import { Logger, LoggerLevel } from '@flxblio/sfp-logger'; + +export default class VlocityPackUpdateSettings extends SFDXCommand { + public constructor(project_directory: string, target_org: string, logger: Logger, logLevel: LoggerLevel) { + super(target_org, project_directory, logger, logLevel); + } + + getSFDXCommand(): string { + return 'vlocity'; + } + getCommandName(): string { + return 'vlocity:packUpdateSettings'; + } + + getGeneratedParams(): string { + let command = `-sfdx.username ${this.target_org} --nojob packUpdateSettings`; + if (this.logLevel) command += ` -verbose`; + return command; + } +} diff --git a/packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityRefreshBase.ts b/packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityRefreshBase.ts new file mode 100644 index 000000000..b31df3640 --- /dev/null +++ b/packages/sfpowerscripts-cli/src/core/vlocitywrapper/VlocityRefreshBase.ts @@ -0,0 +1,31 @@ +import path from 'path'; +import { SFDXCommand } from '@flxblio/sfdx-process-wrapper/lib/SFDXCommand'; +import { Logger, LoggerLevel } from '@flxblio/sfp-logger'; + +export default class VlocityRefreshBase extends SFDXCommand { + public constructor( + project_directory: string, + target_org: string, + private packageDirectory: string, + logger: Logger, + logLevel: LoggerLevel + ) { + super(target_org, project_directory, logger, logLevel); + } + + getSFDXCommand(): string { + return 'vlocity'; + } + getCommandName(): string { + return 'vlocity:refreshVlocityBase'; + } + + getGeneratedParams(): string { + let command = `-sfdx.username ${this.target_org} -job ${path.join( + this.packageDirectory, + 'VlocityComponents.yaml' + )} refreshVlocityBase`; + if (this.logLevel) command += ` -verbose`; + return command; + } +} diff --git a/packages/sfpowerscripts-cli/src/errors/ReleaseError.ts b/packages/sfpowerscripts-cli/src/errors/ReleaseError.ts index 4381e1070..334f3c552 100644 --- a/packages/sfpowerscripts-cli/src/errors/ReleaseError.ts +++ b/packages/sfpowerscripts-cli/src/errors/ReleaseError.ts @@ -1,8 +1,8 @@ -import SfpowerscriptsError from './SfpowerscriptsError'; +import SfpError from './SfpError'; import { ReleaseResult } from '../impl/release/ReleaseImpl'; -export default class ReleaseError extends SfpowerscriptsError { +export default class ReleaseError extends SfpError { /** * Payload for the results of the release */ diff --git a/packages/sfpowerscripts-cli/src/errors/SfpowerscriptsError.ts b/packages/sfpowerscripts-cli/src/errors/SfpError.ts similarity index 86% rename from packages/sfpowerscripts-cli/src/errors/SfpowerscriptsError.ts rename to packages/sfpowerscripts-cli/src/errors/SfpError.ts index 7463428c0..2665ed5c3 100644 --- a/packages/sfpowerscripts-cli/src/errors/SfpowerscriptsError.ts +++ b/packages/sfpowerscripts-cli/src/errors/SfpError.ts @@ -1,4 +1,4 @@ -export default abstract class SfpowerscriptsError extends Error { +export default abstract class SfpError extends Error { readonly message: string; readonly code: string; /** diff --git a/packages/sfpowerscripts-cli/src/errors/ValidateError.ts b/packages/sfpowerscripts-cli/src/errors/ValidateError.ts index a8aa5e5ae..5890a1ec6 100644 --- a/packages/sfpowerscripts-cli/src/errors/ValidateError.ts +++ b/packages/sfpowerscripts-cli/src/errors/ValidateError.ts @@ -1,7 +1,7 @@ -import SfpowerscriptsError from './SfpowerscriptsError'; +import SfpError from './SfpError'; import ValidateResult from '../impl/validate/ValidateResult'; -export default class ValidateError extends SfpowerscriptsError { +export default class ValidateError extends SfpError { /** * Payload for the results of the release */ diff --git a/packages/sfpowerscripts-cli/src/flags/duration.ts b/packages/sfpowerscripts-cli/src/flags/duration.ts index ee3984a55..af0994e29 100644 --- a/packages/sfpowerscripts-cli/src/flags/duration.ts +++ b/packages/sfpowerscripts-cli/src/flags/duration.ts @@ -9,7 +9,7 @@ import { Messages } from '@salesforce/core'; import { Duration } from '@salesforce/kit'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'core-messages'); +const messages = Messages.loadMessages('@flxblio/sfp', 'core-messages'); type DurationUnit = Lowercase; diff --git a/packages/sfpowerscripts-cli/src/flags/orgApiVersion.ts b/packages/sfpowerscripts-cli/src/flags/orgApiVersion.ts index cb0ffd8c2..92ca9ac74 100644 --- a/packages/sfpowerscripts-cli/src/flags/orgApiVersion.ts +++ b/packages/sfpowerscripts-cli/src/flags/orgApiVersion.ts @@ -8,7 +8,7 @@ import { Flags } from '@oclif/core'; import { Messages, Lifecycle, OrgConfigProperties, validateApiVersion } from '@salesforce/core'; Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'core-messages'); +const messages = Messages.loadMessages('@flxblio/sfp', 'core-messages'); // versions below this are retired export const minValidApiVersion = 21; @@ -16,7 +16,7 @@ export const minValidApiVersion = 21; export const maxDeprecated = 30; export const maxDeprecatedUrl = 'https://help.salesforce.com/s/articleView?id=000354473&type=1;'; -/**const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'core-messages'); +/**const messages = Messages.loadMessages('@flxblio/sfp', 'core-messages'); * apiVersion for a salesforce org's rest api. * Will validate format and that the api version is still supported. * Will default to the version specified in Config, if it exists (and will provide an override warning) diff --git a/packages/sfpowerscripts-cli/src/flags/sfdxflags.ts b/packages/sfpowerscripts-cli/src/flags/sfdxflags.ts index 54694ac7f..9d73f98da 100644 --- a/packages/sfpowerscripts-cli/src/flags/sfdxflags.ts +++ b/packages/sfpowerscripts-cli/src/flags/sfdxflags.ts @@ -1,6 +1,6 @@ /* - * Modified from sfdx-plugins-core to meet sfpowerscripts requirment - * sfpowerscripts is not moving to the new style immediately + * Modified from sfdx-plugins-core to meet sfp requirment + * sfp is not moving to the new style immediately * to reduce migration efforts in pipelines * * @@ -25,7 +25,7 @@ export const orgApiVersionFlagSfdxStyle = orgApiVersionFlag({ }); Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'core-messages'); +const messages = Messages.loadMessages('@flxblio/sfp', 'core-messages'); export const loglevel = Flags.string({ description: 'logging level for this command invocation', diff --git a/packages/sfpowerscripts-cli/src/impl/artifacts/FetchAnArtifactFromNPM.ts b/packages/sfpowerscripts-cli/src/impl/artifacts/FetchAnArtifactFromNPM.ts index 5185af3b9..8e5cead2d 100644 --- a/packages/sfpowerscripts-cli/src/impl/artifacts/FetchAnArtifactFromNPM.ts +++ b/packages/sfpowerscripts-cli/src/impl/artifacts/FetchAnArtifactFromNPM.ts @@ -2,7 +2,7 @@ import * as fs from 'fs-extra'; import child_process = require('child_process'); import path = require('path'); import FetchAnArtifact from './FetchAnArtifact'; -import SFPLogger, { COLOR_WARNING } from '@dxatscale/sfp-logger'; +import SFPLogger, { COLOR_WARNING } from '@flxblio/sfp-logger'; export class FetchAnArtifactFromNPM implements FetchAnArtifact { constructor(private scope: string, private npmrcPath: string) { @@ -40,8 +40,8 @@ export class FetchAnArtifactFromNPM implements FetchAnArtifact { packageName = packageName.toLowerCase(); let cmd: string; - if (this.scope) cmd = `npm pack @${this.scope.toLowerCase()}/${packageName}_sfpowerscripts_artifact`; - else cmd = `npm pack ${packageName}_sfpowerscripts_artifact`; + if (this.scope) cmd = `npm pack @${this.scope.toLowerCase()}/${packageName}_sfp_artifact`; + else cmd = `npm pack ${packageName}_sfp_artifact`; cmd += `@${version}`; diff --git a/packages/sfpowerscripts-cli/src/impl/artifacts/FetchAnArtifactUsingScript.ts b/packages/sfpowerscripts-cli/src/impl/artifacts/FetchAnArtifactUsingScript.ts index 56563dc67..abd9fd714 100644 --- a/packages/sfpowerscripts-cli/src/impl/artifacts/FetchAnArtifactUsingScript.ts +++ b/packages/sfpowerscripts-cli/src/impl/artifacts/FetchAnArtifactUsingScript.ts @@ -1,8 +1,8 @@ -import SFPLogger, { COLOR_WARNING, LoggerLevel } from '@dxatscale/sfp-logger'; +import SFPLogger, { COLOR_WARNING, LoggerLevel } from '@flxblio/sfp-logger'; const fs = require('fs-extra'); import child_process = require('child_process'); import FetchAnArtifact from './FetchAnArtifact'; -import defaultShell from '@dxatscale/sfpowerscripts.core/lib/utils/DefaultShell'; +import defaultShell from '../../core/utils/DefaultShell'; export class FetchAnArtifactUsingScript implements FetchAnArtifact { constructor(private scriptPath: string) {} diff --git a/packages/sfpowerscripts-cli/src/impl/artifacts/FetchArtifactsError.ts b/packages/sfpowerscripts-cli/src/impl/artifacts/FetchArtifactsError.ts index 5a9a392ff..dcf60ac28 100644 --- a/packages/sfpowerscripts-cli/src/impl/artifacts/FetchArtifactsError.ts +++ b/packages/sfpowerscripts-cli/src/impl/artifacts/FetchArtifactsError.ts @@ -1,9 +1,9 @@ -import SfpowerscriptsError from '../../errors/SfpowerscriptsError'; +import SfpError from '../../errors/SfpError'; import { ArtifactVersion } from './FetchImpl'; -export default class FetchArtifactsError extends SfpowerscriptsError { +export default class FetchArtifactsError extends SfpError { /** * Payload consisting of artifacts that succeeded and failed to fetch */ diff --git a/packages/sfpowerscripts-cli/src/impl/artifacts/FetchImpl.ts b/packages/sfpowerscripts-cli/src/impl/artifacts/FetchImpl.ts index f32b11960..33df9a75c 100644 --- a/packages/sfpowerscripts-cli/src/impl/artifacts/FetchImpl.ts +++ b/packages/sfpowerscripts-cli/src/impl/artifacts/FetchImpl.ts @@ -1,14 +1,14 @@ import * as fs from 'fs-extra'; -import Git from '@dxatscale/sfpowerscripts.core/lib/git/Git'; -import GitTags from '@dxatscale/sfpowerscripts.core/lib/git/GitTags'; +import Git from '../../core/git/Git'; +import GitTags from '../../core/git/GitTags'; import ReleaseDefinitionSchema from '../release/ReleaseDefinitionSchema'; import FetchArtifactsError from './FetchArtifactsError'; import * as rimraf from 'rimraf'; import FetchArtifactSelector from './FetchArtifactSelector'; import _ from 'lodash'; import path from 'path'; -import FileUtils from '@dxatscale/sfpowerscripts.core/lib/utils/Fileutils'; -import SFPLogger, { Logger, LoggerLevel } from '@dxatscale/sfp-logger'; +import FileUtils from '../../core/utils/Fileutils'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; export default class FetchImpl { constructor( diff --git a/packages/sfpowerscripts-cli/src/impl/changelog/ChangelogImpl.ts b/packages/sfpowerscripts-cli/src/impl/changelog/ChangelogImpl.ts index 4923e3222..1326bec90 100644 --- a/packages/sfpowerscripts-cli/src/impl/changelog/ChangelogImpl.ts +++ b/packages/sfpowerscripts-cli/src/impl/changelog/ChangelogImpl.ts @@ -1,4 +1,4 @@ -import ArtifactFetcher, { Artifact } from '@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactFetcher'; +import ArtifactFetcher, { Artifact } from '../../core/artifacts/ArtifactFetcher'; import { ReleaseChangelog } from './ReleaseChangelog'; import ChangelogMarkdownGenerator from './ChangelogMarkdownGenerator'; import ReleaseChangelogUpdater from './ReleaseChangelogUpdater'; @@ -8,10 +8,10 @@ import { marked } from 'marked'; const TerminalRenderer = require('marked-terminal'); const retry = require('async-retry'); import { GitError } from 'simple-git'; -import SfpPackage from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; -import SFPLogger, { LoggerLevel, ConsoleLogger, Logger } from '@dxatscale/sfp-logger'; -import SfpPackageBuilder from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageBuilder'; -import Git from '@dxatscale/sfpowerscripts.core/lib/git/Git'; +import SfpPackage from '../../core/package/SfpPackage'; +import SFPLogger, { LoggerLevel, ConsoleLogger, Logger } from '@flxblio/sfp-logger'; +import SfpPackageBuilder from '../../core/package/SfpPackageBuilder'; +import Git from '../../core/git/Git'; import FileOutputHandler from '../../outputs/FileOutputHandler'; @@ -97,7 +97,7 @@ export default class ChangelogImpl { } else { console.log(`${sfpPackage.packageName} artifact is missing branch information`); console.log( - `This will cause an error in the future. Re-create the artifact using the latest version of sfpowerscripts to maintain compatibility.` + `This will cause an error in the future. Re-create the artifact using the latest version of sfp to maintain compatibility.` ); } } diff --git a/packages/sfpowerscripts-cli/src/impl/changelog/CommitUpdater.ts b/packages/sfpowerscripts-cli/src/impl/changelog/CommitUpdater.ts index d17e50a37..e20879963 100644 --- a/packages/sfpowerscripts-cli/src/impl/changelog/CommitUpdater.ts +++ b/packages/sfpowerscripts-cli/src/impl/changelog/CommitUpdater.ts @@ -1,5 +1,5 @@ import { Release } from './ReleaseChangelog'; -import { Changelog as PackageChangelog } from '@dxatscale/sfpowerscripts.core/lib/changelog/interfaces/GenericChangelogInterfaces'; +import { Changelog as PackageChangelog } from '../../core/changelog/interfaces/GenericChangelogInterfaces'; import ReadPackageChangelog from './ReadPackageChangelog'; export default class CommitUpdater { diff --git a/packages/sfpowerscripts-cli/src/impl/changelog/ReadPackageChangelog.ts b/packages/sfpowerscripts-cli/src/impl/changelog/ReadPackageChangelog.ts index 64090d316..2fa69ae3c 100644 --- a/packages/sfpowerscripts-cli/src/impl/changelog/ReadPackageChangelog.ts +++ b/packages/sfpowerscripts-cli/src/impl/changelog/ReadPackageChangelog.ts @@ -1,4 +1,4 @@ -import { Changelog as PackageChangelog } from '@dxatscale/sfpowerscripts.core/lib/changelog/interfaces/GenericChangelogInterfaces'; +import { Changelog as PackageChangelog } from '../../core/changelog/interfaces/GenericChangelogInterfaces'; export default interface ReadPackageChangelog { (changelogFilePath: string): PackageChangelog; diff --git a/packages/sfpowerscripts-cli/src/impl/changelog/ReleaseChangelog.ts b/packages/sfpowerscripts-cli/src/impl/changelog/ReleaseChangelog.ts index 8851d9d32..456d8544f 100644 --- a/packages/sfpowerscripts-cli/src/impl/changelog/ReleaseChangelog.ts +++ b/packages/sfpowerscripts-cli/src/impl/changelog/ReleaseChangelog.ts @@ -1,4 +1,4 @@ -import { Changelog, Commit } from '@dxatscale/sfpowerscripts.core/lib/changelog/interfaces/GenericChangelogInterfaces'; +import { Changelog, Commit } from '../../core/changelog/interfaces/GenericChangelogInterfaces'; export class ReleaseChangelog { releases: Release[]; diff --git a/packages/sfpowerscripts-cli/src/impl/changelog/ReleaseChangelogUpdater.ts b/packages/sfpowerscripts-cli/src/impl/changelog/ReleaseChangelogUpdater.ts index 2b3fe3850..4a8808c4b 100644 --- a/packages/sfpowerscripts-cli/src/impl/changelog/ReleaseChangelogUpdater.ts +++ b/packages/sfpowerscripts-cli/src/impl/changelog/ReleaseChangelogUpdater.ts @@ -4,7 +4,7 @@ import WorkItemUpdater from './WorkItemUpdater'; import OrgsUpdater from './OrgsUpdater'; import ReadPackageChangelog from './ReadPackageChangelog'; import * as fs from 'fs-extra'; -import SfpPackage from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; +import SfpPackage from '../../core/package/SfpPackage'; var hash = require('object-hash'); export default class ReleaseChangelogUpdater { diff --git a/packages/sfpowerscripts-cli/src/impl/changelog/WorkItemUpdater.ts b/packages/sfpowerscripts-cli/src/impl/changelog/WorkItemUpdater.ts index 7faef8fbd..71c9eec7e 100644 --- a/packages/sfpowerscripts-cli/src/impl/changelog/WorkItemUpdater.ts +++ b/packages/sfpowerscripts-cli/src/impl/changelog/WorkItemUpdater.ts @@ -1,4 +1,4 @@ -import SFPLogger, { Logger, LoggerLevel } from '@dxatscale/sfp-logger'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; import { Release } from './ReleaseChangelog'; diff --git a/packages/sfpowerscripts-cli/src/impl/demoreelplayer/DemoReelPlayer.ts b/packages/sfpowerscripts-cli/src/impl/demoreelplayer/DemoReelPlayer.ts index 9428f9f9b..daf033582 100644 --- a/packages/sfpowerscripts-cli/src/impl/demoreelplayer/DemoReelPlayer.ts +++ b/packages/sfpowerscripts-cli/src/impl/demoreelplayer/DemoReelPlayer.ts @@ -2,7 +2,7 @@ import path = require('path'); import * as fs from 'fs-extra'; import { marked } from 'marked'; const TerminalRenderer = require('marked-terminal'); -import { delay } from '@dxatscale/sfpowerscripts.core/lib/utils/Delay'; +import { delay } from '../../core/utils/Delay'; export default class DemoReelPlayer { public async execute(demoReelFolderPath: string) { diff --git a/packages/sfpowerscripts-cli/src/impl/dependency/ShrinkImpl.ts b/packages/sfpowerscripts-cli/src/impl/dependency/ShrinkImpl.ts index 7df3bf7b1..cf58a7187 100644 --- a/packages/sfpowerscripts-cli/src/impl/dependency/ShrinkImpl.ts +++ b/packages/sfpowerscripts-cli/src/impl/dependency/ShrinkImpl.ts @@ -1,10 +1,10 @@ -import TransitiveDependencyResolver from '@dxatscale/sfpowerscripts.core/lib/package/dependencies/TransitiveDependencyResolver'; -import { COLOR_HEADER, COLOR_KEY_MESSAGE, COLOR_SUCCESS, COLOR_ERROR } from '@dxatscale/sfp-logger'; -import SFPLogger, { LoggerLevel, Logger } from '@dxatscale/sfp-logger'; +import TransitiveDependencyResolver from '../../core/package/dependencies/TransitiveDependencyResolver'; +import { COLOR_HEADER, COLOR_KEY_MESSAGE, COLOR_SUCCESS, COLOR_ERROR } from '@flxblio/sfp-logger'; +import SFPLogger, { LoggerLevel, Logger } from '@flxblio/sfp-logger'; import _ from 'lodash'; import { Connection } from '@salesforce/core'; const Table = require('cli-table'); -import UserDefinedExternalDependency from '@dxatscale/sfpowerscripts.core/lib/project/UserDefinedExternalDependency'; +import UserDefinedExternalDependency from '../../core/project/UserDefinedExternalDependency'; export default class ShrinkImpl { private dependencyMap; diff --git a/packages/sfpowerscripts-cli/src/impl/deploy/DeployImpl.ts b/packages/sfpowerscripts-cli/src/impl/deploy/DeployImpl.ts index 4e83da473..98851aaad 100644 --- a/packages/sfpowerscripts-cli/src/impl/deploy/DeployImpl.ts +++ b/packages/sfpowerscripts-cli/src/impl/deploy/DeployImpl.ts @@ -1,28 +1,28 @@ -import ArtifactFetcher, { Artifact } from '@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactFetcher'; -import SFPLogger, { COLOR_ERROR, COLOR_SUCCESS, FileLogger, Logger, LoggerLevel } from '@dxatscale/sfp-logger'; +import ArtifactFetcher, { Artifact } from '../../core/artifacts/ArtifactFetcher'; +import SFPLogger, { COLOR_ERROR, COLOR_SUCCESS, FileLogger, Logger, LoggerLevel } from '@flxblio/sfp-logger'; import { EOL } from 'os'; import { Stage } from '../Stage'; -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; +import ProjectConfig from '../../core/project/ProjectConfig'; import semver = require('semver'); -import PromoteUnlockedPackageImpl from '@dxatscale/sfpowerscripts.core/lib/package/promote/PromoteUnlockedPackageImpl'; -import { DeploymentType } from '@dxatscale/sfpowerscripts.core/lib/deployers/DeploymentExecutor'; -import { COLOR_KEY_MESSAGE,COLOR_KEY_VALUE,COLOR_HEADER } from '@dxatscale/sfp-logger'; +import PromoteUnlockedPackageImpl from '../../core/package/promote/PromoteUnlockedPackageImpl'; +import { DeploymentType } from '../../core/deployers/DeploymentExecutor'; +import { COLOR_KEY_MESSAGE,COLOR_KEY_VALUE,COLOR_HEADER } from '@flxblio/sfp-logger'; import { PackageInstallationResult, PackageInstallationStatus, -} from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/PackageInstallationResult'; -import SFPOrg from '@dxatscale/sfpowerscripts.core/lib/org/SFPOrg'; -import SfpPackage, { PackageType } from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; -import SfpPackageInquirer from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageInquirer'; +} from '../../core/package/packageInstallers/PackageInstallationResult'; +import SFPOrg from '../../core/org/SFPOrg'; +import SfpPackage, { PackageType } from '../../core/package/SfpPackage'; +import SfpPackageInquirer from '../../core/package/SfpPackageInquirer'; import { PostDeployHook } from './PostDeployHook'; import { PreDeployHook } from './PreDeployHook'; -import SfpPackageBuilder from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageBuilder'; -import SfpPackageInstaller from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackageInstaller'; -import { SfpPackageInstallationOptions } from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/InstallPackage'; +import SfpPackageBuilder from '../../core/package/SfpPackageBuilder'; +import SfpPackageInstaller from '../../core/package/SfpPackageInstaller'; +import { SfpPackageInstallationOptions } from '../../core/package/packageInstallers/InstallPackage'; import * as _ from 'lodash'; import GroupConsoleLogs from '../../ui/GroupConsoleLogs'; import { ZERO_BORDER_TABLE } from '../../ui/TableConstants'; -import convertBuildNumDotDelimToHyphen from '@dxatscale/sfpowerscripts.core/lib/utils/VersionNumberConverter'; +import convertBuildNumDotDelimToHyphen from '../../core/utils/VersionNumberConverter'; import ReleaseConfig from '../release/ReleaseConfig'; import fs from 'fs-extra'; import { Align, getMarkdownTable } from 'markdown-table-ts'; diff --git a/packages/sfpowerscripts-cli/src/impl/deploy/PostDeployHook.ts b/packages/sfpowerscripts-cli/src/impl/deploy/PostDeployHook.ts index 2b6e3e324..7f73cf3ba 100644 --- a/packages/sfpowerscripts-cli/src/impl/deploy/PostDeployHook.ts +++ b/packages/sfpowerscripts-cli/src/impl/deploy/PostDeployHook.ts @@ -1,6 +1,6 @@ -import { Logger } from '@dxatscale/sfp-logger'; -import { PackageInstallationResult } from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/PackageInstallationResult'; -import SfpPackage from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; +import { Logger } from '@flxblio/sfp-logger'; +import { PackageInstallationResult } from '../../core/package/packageInstallers/PackageInstallationResult'; +import SfpPackage from '../../core/package/SfpPackage'; export interface PostDeployHook { postDeployPackage( diff --git a/packages/sfpowerscripts-cli/src/impl/deploy/PreDeployHook.ts b/packages/sfpowerscripts-cli/src/impl/deploy/PreDeployHook.ts index 5f94ca2b1..58294b363 100644 --- a/packages/sfpowerscripts-cli/src/impl/deploy/PreDeployHook.ts +++ b/packages/sfpowerscripts-cli/src/impl/deploy/PreDeployHook.ts @@ -1,5 +1,5 @@ -import { Logger } from '@dxatscale/sfp-logger'; -import SfpPackage from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; +import { Logger } from '@flxblio/sfp-logger'; +import SfpPackage from '../../core/package/SfpPackage'; export interface PreDeployHook { preDeployPackage( diff --git a/packages/sfpowerscripts-cli/src/impl/impact/ImpactedPackagesResolver.ts b/packages/sfpowerscripts-cli/src/impl/impact/ImpactedPackagesResolver.ts index a3640b6c9..0d00338bb 100644 --- a/packages/sfpowerscripts-cli/src/impl/impact/ImpactedPackagesResolver.ts +++ b/packages/sfpowerscripts-cli/src/impl/impact/ImpactedPackagesResolver.ts @@ -1,9 +1,9 @@ -import PackageDiffImpl, { PackageDiffOptions } from '@dxatscale/sfpowerscripts.core/lib/package/diff/PackageDiffImpl'; +import PackageDiffImpl, { PackageDiffOptions } from '../../core/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 ProjectConfig from '../../core/project/ProjectConfig'; +import { PackageType } from '../../core/package/SfpPackage'; import * as fs from 'fs-extra'; -import { Logger } from '@dxatscale/sfp-logger'; +import { Logger } from '@flxblio/sfp-logger'; import BuildCollections from '../parallelBuilder/BuildCollections'; export interface ImpactedPackageProps { @@ -44,7 +44,7 @@ export default class ImpactedPackageResolver { private getPathToForceIgnoreForCurrentStage(projectConfig: any, currentStage: Stage): string { let stageForceIgnorePath: string; - let ignoreFiles: { [key in Stage]: string } = projectConfig.plugins?.sfpowerscripts?.ignoreFiles; + let ignoreFiles: { [key in Stage]: string } = projectConfig.plugins?.sfp?.ignoreFiles; if (ignoreFiles) { Object.keys(ignoreFiles).forEach((key) => { if (key.toLowerCase() == currentStage) { diff --git a/packages/sfpowerscripts-cli/src/impl/parallelBuilder/BuildCollections.ts b/packages/sfpowerscripts-cli/src/impl/parallelBuilder/BuildCollections.ts index 7962ef12f..7d3e3b5dc 100644 --- a/packages/sfpowerscripts-cli/src/impl/parallelBuilder/BuildCollections.ts +++ b/packages/sfpowerscripts-cli/src/impl/parallelBuilder/BuildCollections.ts @@ -1,5 +1,5 @@ import UndirectedGraph from './UndirectedGraph'; -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; +import ProjectConfig from '../../core/project/ProjectConfig'; /** * Class for the manipulation of package build collections diff --git a/packages/sfpowerscripts-cli/src/impl/parallelBuilder/BuildImpl.ts b/packages/sfpowerscripts-cli/src/impl/parallelBuilder/BuildImpl.ts index aa11d3f68..5bc6a3e82 100644 --- a/packages/sfpowerscripts-cli/src/impl/parallelBuilder/BuildImpl.ts +++ b/packages/sfpowerscripts-cli/src/impl/parallelBuilder/BuildImpl.ts @@ -3,12 +3,12 @@ import DependencyHelper from "./DependencyHelper"; import Bottleneck from "bottleneck"; import PackageDiffImpl, { PackageDiffOptions, -} from "@dxatscale/sfpowerscripts.core/lib/package/diff/PackageDiffImpl"; +} from "../../core/package/diff/PackageDiffImpl"; import { EOL } from "os"; -import SFPStatsSender from "@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender"; +import SFPStatsSender from "../../core/stats/SFPStatsSender"; import { Stage } from "../Stage"; import * as fs from "fs-extra"; -import ProjectConfig from "@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig"; +import ProjectConfig from "../../core/project/ProjectConfig"; import BuildCollections from "./BuildCollections"; const Table = require("cli-table"); import SFPLogger, { @@ -17,26 +17,26 @@ import SFPLogger, { FileLogger, LoggerLevel, VoidLogger, -} from "@dxatscale/sfp-logger"; -import { COLOR_KEY_MESSAGE } from "@dxatscale/sfp-logger"; -import { COLOR_HEADER } from "@dxatscale/sfp-logger"; -import { COLOR_ERROR } from "@dxatscale/sfp-logger"; +} from "@flxblio/sfp-logger"; +import { COLOR_KEY_MESSAGE } from "@flxblio/sfp-logger"; +import { COLOR_HEADER } from "@flxblio/sfp-logger"; +import { COLOR_ERROR } from "@flxblio/sfp-logger"; import SfpPackage, { PackageType, -} from "@dxatscale/sfpowerscripts.core/lib/package/SfpPackage"; -import SfpPackageBuilder from "@dxatscale/sfpowerscripts.core/lib/package/SfpPackageBuilder"; -import getFormattedTime from "@dxatscale/sfpowerscripts.core/lib/utils/GetFormattedTime"; +} from "../../core/package/SfpPackage"; +import SfpPackageBuilder from "../../core/package/SfpPackageBuilder"; +import getFormattedTime from "../../core/utils/GetFormattedTime"; import { COLON_MIDDLE_BORDER_TABLE, ZERO_BORDER_TABLE, } from "../../ui/TableConstants"; -import PackageDependencyResolver from "@dxatscale/sfpowerscripts.core/lib/package/dependencies/PackageDependencyResolver"; -import SFPOrg from "@dxatscale/sfpowerscripts.core/lib/org/SFPOrg"; -import Git from "@dxatscale/sfpowerscripts.core/lib/git/Git"; -import TransitiveDependencyResolver from "@dxatscale/sfpowerscripts.core/lib/package/dependencies/TransitiveDependencyResolver"; +import PackageDependencyResolver from "../../core/package/dependencies/PackageDependencyResolver"; +import SFPOrg from "../../core/org/SFPOrg"; +import Git from "../../core/git/Git"; +import TransitiveDependencyResolver from "../../core/package/dependencies/TransitiveDependencyResolver"; import GroupConsoleLogs from "../../ui/GroupConsoleLogs"; -import UserDefinedExternalDependency from "@dxatscale/sfpowerscripts.core/lib/project/UserDefinedExternalDependency"; -import PackageDependencyDisplayer from "@dxatscale/sfpowerscripts.core/lib/display/PackageDependencyDisplayer"; +import UserDefinedExternalDependency from "../../core/project/UserDefinedExternalDependency"; +import PackageDependencyDisplayer from "../../core/display/PackageDependencyDisplayer"; const PRIORITY_UNLOCKED_PKG_WITH_DEPENDENCY = 1; const PRIORITY_UNLOCKED_PKG_WITHOUT_DEPENDENCY = 3; @@ -445,11 +445,11 @@ export default class BuildImpl { SFPLogger.log(COLOR_ERROR(`Package Creation Failed for ${pkg}, Here are the details:`)); try { // Append error to log file - fs.appendFileSync(`.sfpowerscripts/logs/${pkg}`, reason.message, "utf8"); - let data = fs.readFileSync(`.sfpowerscripts/logs/${pkg}`, "utf8"); + fs.appendFileSync(`.sfp/logs/${pkg}`, reason.message, "utf8"); + let data = fs.readFileSync(`.sfp/logs/${pkg}`, "utf8"); - const pathToMarkDownFile = `.sfpowerscripts/outputs/build-error-info.md`; - fs.mkdirpSync(".sfpowerscripts/outputs"); + const pathToMarkDownFile = `.sfp/outputs/build-error-info.md`; + fs.mkdirpSync(".sfp/outputs"); fs.createFileSync(pathToMarkDownFile); fs.appendFileSync(pathToMarkDownFile, `\nPlease find the errors observed during build\n\n`); fs.appendFileSync(pathToMarkDownFile, `## ${pkg}\n\n`); @@ -755,7 +755,7 @@ export default class BuildImpl { return SfpPackageBuilder.buildPackageFromProjectDirectory( - new FileLogger(`.sfpowerscripts/logs/${sfdx_package}`), + new FileLogger(`.sfp/logs/${sfdx_package}`), this.props.projectDirectory, sfdx_package, { @@ -800,7 +800,7 @@ export default class BuildImpl { let stageForceIgnorePath: string; let ignoreFiles: { [key in Stage]: string } = - projectConfig.plugins?.sfpowerscripts?.ignoreFiles; + projectConfig.plugins?.sfp?.ignoreFiles; if (ignoreFiles) { Object.keys(ignoreFiles).forEach((key) => { if (key.toLowerCase() == currentStage) { @@ -823,11 +823,11 @@ export default class BuildImpl { projectConfig: any, ): { [key: string]: any }[] { this.isMultiConfigFilesEnabled = - this.projectConfig?.plugins?.sfpowerscripts?.scratchOrgDefFilePaths?.enableMultiDefinitionFiles; + this.projectConfig?.plugins?.sfp?.scratchOrgDefFilePaths?.enableMultiDefinitionFiles; let configFiles: { [key: string]: any }[]; if (this.isMultiConfigFilesEnabled) { configFiles = - this.projectConfig?.plugins?.sfpowerscripts?.scratchOrgDefFilePaths + this.projectConfig?.plugins?.sfp?.scratchOrgDefFilePaths ?.packages; } return configFiles; @@ -835,7 +835,7 @@ export default class BuildImpl { private async resolvePackageDependencies(projectConfig: any) { let isDependencyResolverEnabled = - !projectConfig?.plugins?.sfpowerscripts + !projectConfig?.plugins?.sfp ?.disableTransitiveDependencyResolver; if (isDependencyResolverEnabled) { diff --git a/packages/sfpowerscripts-cli/src/impl/parallelBuilder/DependencyHelper.ts b/packages/sfpowerscripts-cli/src/impl/parallelBuilder/DependencyHelper.ts index 7549e809e..9e2164912 100644 --- a/packages/sfpowerscripts-cli/src/impl/parallelBuilder/DependencyHelper.ts +++ b/packages/sfpowerscripts-cli/src/impl/parallelBuilder/DependencyHelper.ts @@ -1,4 +1,4 @@ -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; +import ProjectConfig from '../../core/project/ProjectConfig'; export default class DependencyHelper { static getParentsToBeFullFilled(packagesWithParents: AdjacentList, packages: string[]): any { for (let [pkgName, parents] of Object.entries(packagesWithParents)) { diff --git a/packages/sfpowerscripts-cli/src/impl/prepare/PrepareImpl.ts b/packages/sfpowerscripts-cli/src/impl/prepare/PrepareImpl.ts index 98b976b19..1087c6367 100644 --- a/packages/sfpowerscripts-cli/src/impl/prepare/PrepareImpl.ts +++ b/packages/sfpowerscripts-cli/src/impl/prepare/PrepareImpl.ts @@ -1,33 +1,33 @@ import { Org } from '@salesforce/core'; -import { PoolConfig } from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolConfig'; -import isValidSfdxAuthUrl from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/prequisitecheck/IsValidSfdxAuthUrl'; -import SFPLogger, { COLOR_KEY_MESSAGE, COLOR_WARNING, ConsoleLogger, Logger, LoggerLevel } from '@dxatscale/sfp-logger'; -import ArtifactGenerator from '@dxatscale/sfpowerscripts.core/lib/artifacts/generators/ArtifactGenerator'; -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; +import { PoolConfig } from '../../core/scratchorg/pool/PoolConfig'; +import isValidSfdxAuthUrl from '../../core/scratchorg/pool/prequisitecheck/IsValidSfdxAuthUrl'; +import SFPLogger, { COLOR_KEY_MESSAGE, COLOR_WARNING, ConsoleLogger, Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import ArtifactGenerator from '../../core/artifacts/generators/ArtifactGenerator'; +import ProjectConfig from '../../core/project/ProjectConfig'; import { Result } from 'neverthrow'; import FetchAnArtifact from '../artifacts/FetchAnArtifact'; import FetchArtifactSelector from '../artifacts/FetchArtifactSelector'; import BuildImpl, { BuildProps } from '../parallelBuilder/BuildImpl'; -import PoolCreateImpl from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolCreateImpl'; -import { PoolError } from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolError'; +import PoolCreateImpl from '../../core/scratchorg/pool/PoolCreateImpl'; +import { PoolError } from '../../core/scratchorg/pool/PoolError'; import { Stage } from '../Stage'; import PrepareOrgJob from './PrepareOrgJob'; import * as rimraf from 'rimraf'; import * as fs from 'fs-extra'; -import Git from '@dxatscale/sfpowerscripts.core/lib/git/Git'; -import GitTags from '@dxatscale/sfpowerscripts.core/lib/git/GitTags'; -import OrgDetailsFetcher from '@dxatscale/sfpowerscripts.core/lib/org/OrgDetailsFetcher'; -import SFPOrg from '@dxatscale/sfpowerscripts.core/lib/org/SFPOrg'; +import Git from '../../core/git/Git'; +import GitTags from '../../core/git/GitTags'; +import OrgDetailsFetcher from '../../core/org/OrgDetailsFetcher'; +import SFPOrg from '../../core/org/SFPOrg'; import { EOL } from 'os'; -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; -import ExternalPackage2DependencyResolver from '@dxatscale/sfpowerscripts.core/lib/package/dependencies/ExternalPackage2DependencyResolver'; -import ExternalDependencyDisplayer from '@dxatscale/sfpowerscripts.core/lib/display/ExternalDependencyDisplayer'; +import SFPStatsSender from '../../core/stats/SFPStatsSender'; +import ExternalPackage2DependencyResolver from '../../core/package/dependencies/ExternalPackage2DependencyResolver'; +import ExternalDependencyDisplayer from '../../core/display/ExternalDependencyDisplayer'; import ReleaseDefinitionGenerator from '../release/ReleaseDefinitionGenerator'; import ReleaseDefinitionSchema from '../release/ReleaseDefinitionSchema'; import { ZERO_BORDER_TABLE } from '../../ui/TableConstants'; import GroupConsoleLogs from '../../ui/GroupConsoleLogs'; import ReleaseConfig from '../release/ReleaseConfig'; -import { COLOR_KEY_VALUE } from '@dxatscale/sfp-logger'; +import { COLOR_KEY_VALUE } from '@flxblio/sfp-logger'; const Table = require('cli-table'); diff --git a/packages/sfpowerscripts-cli/src/impl/prepare/PrepareOrgJob.ts b/packages/sfpowerscripts-cli/src/impl/prepare/PrepareOrgJob.ts index 2a38eb9b9..eae028d18 100644 --- a/packages/sfpowerscripts-cli/src/impl/prepare/PrepareOrgJob.ts +++ b/packages/sfpowerscripts-cli/src/impl/prepare/PrepareOrgJob.ts @@ -1,31 +1,31 @@ import DeployImpl, { DeploymentMode, DeployProps, DeploymentResult } from '../deploy/DeployImpl'; -import SFPLogger, { LoggerLevel, Logger, COLOR_KEY_MESSAGE, ConsoleLogger } from '@dxatscale/sfp-logger'; +import SFPLogger, { LoggerLevel, Logger, COLOR_KEY_MESSAGE, ConsoleLogger } from '@flxblio/sfp-logger'; import { Stage } from '../Stage'; -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; -import ScratchOrg from '@dxatscale/sfpowerscripts.core/lib/scratchorg/ScratchOrg'; +import SFPStatsSender from '../../core/stats/SFPStatsSender'; +import ScratchOrg from '../../core/scratchorg/ScratchOrg'; import { Result, ok, err } from 'neverthrow'; import PoolJobExecutor, { JobError, ScriptExecutionResult, -} from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolJobExecutor'; +} from '../../core/scratchorg/pool/PoolJobExecutor'; import { Connection, Org } from '@salesforce/core'; -import { PoolConfig } from '@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolConfig'; -import VlocityPackUpdateSettings from '@dxatscale/sfpowerscripts.core/lib/vlocitywrapper/VlocityPackUpdateSettings'; -import VlocityInitialInstall from '@dxatscale/sfpowerscripts.core/lib/vlocitywrapper/VlocityInitialInstall'; -import ScriptExecutor from '@dxatscale/sfpowerscripts.core/lib/scriptExecutor/ScriptExecutorHelpers'; -import DeploymentSettingsService from '@dxatscale/sfpowerscripts.core/lib/deployers/DeploymentSettingsService'; -import PackageDetails from '@dxatscale/sfpowerscripts.core/lib/package/Package2Detail'; -import InstallUnlockedPackageCollection from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/InstallUnlockedPackageCollection'; -import SFPOrg from '@dxatscale/sfpowerscripts.core/lib/org/SFPOrg'; +import { PoolConfig } from '../../core/scratchorg/pool/PoolConfig'; +import VlocityPackUpdateSettings from '../../core/vlocitywrapper/VlocityPackUpdateSettings'; +import VlocityInitialInstall from '../../core/vlocitywrapper/VlocityInitialInstall'; +import ScriptExecutor from '../../core/scriptExecutor/ScriptExecutorHelpers'; +import DeploymentSettingsService from '../../core/deployers/DeploymentSettingsService'; +import PackageDetails from '../../core/package/Package2Detail'; +import InstallUnlockedPackageCollection from '../../core/package/packageInstallers/InstallUnlockedPackageCollection'; +import SFPOrg from '../../core/org/SFPOrg'; import { PreDeployHook } from '../deploy/PreDeployHook'; -import SfpPackage from '@dxatscale/sfpowerscripts.core/lib/package/SfpPackage'; -import ExternalPackage2DependencyResolver from '@dxatscale/sfpowerscripts.core/lib/package/dependencies/ExternalPackage2DependencyResolver'; -import ExternalDependencyDisplayer from '@dxatscale/sfpowerscripts.core/lib/display/ExternalDependencyDisplayer'; -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; -import { FileLogger } from '@dxatscale/sfp-logger'; +import SfpPackage from '../../core/package/SfpPackage'; +import ExternalPackage2DependencyResolver from '../../core/package/dependencies/ExternalPackage2DependencyResolver'; +import ExternalDependencyDisplayer from '../../core/display/ExternalDependencyDisplayer'; +import ProjectConfig from '../../core/project/ProjectConfig'; +import { FileLogger } from '@flxblio/sfp-logger'; const fs = require('fs-extra'); -const SFPOWERSCRIPTS_ARTIFACT_PACKAGE = '04t1P000000ka9mQAA'; +const sfp_ARTIFACT_PACKAGE = '04t1P000000ka9mQAA'; export default class PrepareOrgJob extends PoolJobExecutor implements PreDeployHook { public constructor( protected pool: PoolConfig, @@ -57,8 +57,8 @@ export default class PrepareOrgJob extends PoolJobExecutor implements PreDeployH individualSODeploymentActivityLogger ); - //Install sfpowerscripts package - await this.installSfPowerscriptsArtifactPackage( + //Install sfp package + await this.installsfpArtifactPackage( scratchOrg, individualSODeploymentActivityLogger, packageCollectionInstaller @@ -140,27 +140,27 @@ export default class PrepareOrgJob extends PoolJobExecutor implements PreDeployH return deploymentSucceed; } - private async installSfPowerscriptsArtifactPackage( + private async installsfpArtifactPackage( scratchOrg: ScratchOrg, logger: Logger, packageCollectionInstaller: InstallUnlockedPackageCollection ) { - SFPLogger.log(`Installing sfpowerscripts_artifact package to the ${scratchOrg.alias}`, null, logger); + SFPLogger.log(`Installing sfp_artifact package to the ${scratchOrg.alias}`, null, logger); - //Install sfpowerscripts artifact package + //Install sfp artifact package await packageCollectionInstaller.install( [ { - name: 'sfpowerscripts_artifact2', - subscriberPackageVersionId: process.env.SFPOWERSCRIPTS_ARTIFACT_PACKAGE - ? process.env.SFPOWERSCRIPTS_ARTIFACT_PACKAGE - : SFPOWERSCRIPTS_ARTIFACT_PACKAGE, + name: 'sfp_artifact2', + subscriberPackageVersionId: process.env.sfp_ARTIFACT_PACKAGE + ? process.env.sfp_ARTIFACT_PACKAGE + : sfp_ARTIFACT_PACKAGE, }, ], true ); - SFPLogger.log(`Suscessfully Installed sfpowerscripts_artifact package to the ${scratchOrg.alias}`, null, logger); + SFPLogger.log(`Suscessfully Installed sfp_artifact package to the ${scratchOrg.alias}`, null, logger); } private async invokeDeployImpl( diff --git a/packages/sfpowerscripts-cli/src/impl/release/ReleaseConfig.ts b/packages/sfpowerscripts-cli/src/impl/release/ReleaseConfig.ts index 299152d12..dfa6f13cf 100644 --- a/packages/sfpowerscripts-cli/src/impl/release/ReleaseConfig.ts +++ b/packages/sfpowerscripts-cli/src/impl/release/ReleaseConfig.ts @@ -1,10 +1,10 @@ import * as fs from 'fs-extra'; -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; +import ProjectConfig from '../../core/project/ProjectConfig'; import Ajv, { _ } from 'ajv'; import ReleaseDefinitionGeneratorConfigSchema from './ReleaseDefinitionGeneratorConfigSchema'; import lodash = require('lodash'); const yaml = require('js-yaml'); -import { Logger } from '@dxatscale/sfp-logger'; +import { Logger } from '@flxblio/sfp-logger'; const path = require('path'); export default class ReleaseConfig { diff --git a/packages/sfpowerscripts-cli/src/impl/release/ReleaseDefinition.ts b/packages/sfpowerscripts-cli/src/impl/release/ReleaseDefinition.ts index 27e383be1..accfb708b 100644 --- a/packages/sfpowerscripts-cli/src/impl/release/ReleaseDefinition.ts +++ b/packages/sfpowerscripts-cli/src/impl/release/ReleaseDefinition.ts @@ -3,8 +3,8 @@ import Ajv from 'ajv'; const yaml = require('js-yaml'); import lodash = require('lodash'); import get18DigitSalesforceId from '../../utils/Get18DigitSalesforceId'; -import Git from '@dxatscale/sfpowerscripts.core/lib/git/Git'; -import { ConsoleLogger } from '@dxatscale/sfp-logger'; +import Git from '../../core/git/Git'; +import { ConsoleLogger } from '@flxblio/sfp-logger'; const fs = require('fs-extra'); const path = require('path'); diff --git a/packages/sfpowerscripts-cli/src/impl/release/ReleaseDefinitionGenerator.ts b/packages/sfpowerscripts-cli/src/impl/release/ReleaseDefinitionGenerator.ts index 473cfb90a..1b283240b 100644 --- a/packages/sfpowerscripts-cli/src/impl/release/ReleaseDefinitionGenerator.ts +++ b/packages/sfpowerscripts-cli/src/impl/release/ReleaseDefinitionGenerator.ts @@ -1,14 +1,14 @@ import { GitError } from 'simple-git'; import * as fs from 'fs-extra'; import ReleaseDefinitionSchema from './ReleaseDefinitionSchema'; -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; +import ProjectConfig from '../../core/project/ProjectConfig'; import Ajv, { _ } from 'ajv'; -import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE, Logger } from '@dxatscale/sfp-logger'; +import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE, Logger } from '@flxblio/sfp-logger'; import ReleaseDefinitionGeneratorConfigSchema from './ReleaseDefinitionGeneratorConfigSchema'; import lodash = require('lodash'); -import { LoggerLevel } from '@dxatscale/sfp-logger'; -import Git from '@dxatscale/sfpowerscripts.core/lib/git/Git'; -import GitTags from '@dxatscale/sfpowerscripts.core/lib/git/GitTags'; +import { LoggerLevel } from '@flxblio/sfp-logger'; +import Git from '../../core/git/Git'; +import GitTags from '../../core/git/GitTags'; const retry = require('async-retry'); const yaml = require('js-yaml'); const path = require('path'); diff --git a/packages/sfpowerscripts-cli/src/impl/release/ReleaseImpl.ts b/packages/sfpowerscripts-cli/src/impl/release/ReleaseImpl.ts index dc7eddbff..c2d7c7fd5 100644 --- a/packages/sfpowerscripts-cli/src/impl/release/ReleaseImpl.ts +++ b/packages/sfpowerscripts-cli/src/impl/release/ReleaseImpl.ts @@ -1,16 +1,16 @@ import ReleaseDefinitionSchema from './ReleaseDefinitionSchema'; import DeployImpl, { DeployProps, DeploymentMode, DeploymentResult } from '../deploy/DeployImpl'; -import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE, ConsoleLogger, Logger, LoggerLevel } from '@dxatscale/sfp-logger'; +import SFPLogger, { COLOR_HEADER, COLOR_KEY_MESSAGE, ConsoleLogger, Logger, LoggerLevel } from '@flxblio/sfp-logger'; import { Stage } from '../Stage'; import ReleaseError from '../../errors/ReleaseError'; import ChangelogImpl from '../../impl/changelog/ChangelogImpl'; -import SFPStatsSender from '@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender'; +import SFPStatsSender from '../../core/stats/SFPStatsSender'; import { Release } from '../changelog/ReleaseChangelog'; -import SFPOrg from '@dxatscale/sfpowerscripts.core/lib/org/SFPOrg'; +import SFPOrg from '../../core/org/SFPOrg'; import path = require('path'); import { EOL } from 'os'; -import Package2Detail from '@dxatscale/sfpowerscripts.core/lib/package/Package2Detail'; -import InstallUnlockedPackageCollection from '@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/InstallUnlockedPackageCollection'; +import Package2Detail from '../../core/package/Package2Detail'; +import InstallUnlockedPackageCollection from '../../core/package/packageInstallers/InstallUnlockedPackageCollection'; import FetchImpl from '../artifacts/FetchImpl'; import GroupConsoleLogs from '../../ui/GroupConsoleLogs'; diff --git a/packages/sfpowerscripts-cli/src/impl/repo/AlignImpl.ts b/packages/sfpowerscripts-cli/src/impl/repo/AlignImpl.ts index a8ad16c43..d12cf51fa 100644 --- a/packages/sfpowerscripts-cli/src/impl/repo/AlignImpl.ts +++ b/packages/sfpowerscripts-cli/src/impl/repo/AlignImpl.ts @@ -1,4 +1,4 @@ -import { Logger } from "@dxatscale/sfp-logger"; +import { Logger } from "@flxblio/sfp-logger"; export interface AlignRepoProps { artifactDirectory: string; diff --git a/packages/sfpowerscripts-cli/src/impl/validate/Analyzer.ts b/packages/sfpowerscripts-cli/src/impl/validate/Analyzer.ts index c986f644f..09170a45a 100644 --- a/packages/sfpowerscripts-cli/src/impl/validate/Analyzer.ts +++ b/packages/sfpowerscripts-cli/src/impl/validate/Analyzer.ts @@ -1,5 +1,5 @@ -import ChangedComponentsFetcher from "@dxatscale/sfpowerscripts.core/lib/dependency/ChangedComponentsFetcher"; -import Component from "@dxatscale/sfpowerscripts.core/lib/dependency/Component"; +import ChangedComponentsFetcher from "../../core/dependency/ChangedComponentsFetcher"; +import Component from "../../core/dependency/Component"; export class Analyzer { diff --git a/packages/sfpowerscripts-cli/src/impl/validate/ApexTestValidator.ts b/packages/sfpowerscripts-cli/src/impl/validate/ApexTestValidator.ts index 555af29c7..1b4e650a6 100644 --- a/packages/sfpowerscripts-cli/src/impl/validate/ApexTestValidator.ts +++ b/packages/sfpowerscripts-cli/src/impl/validate/ApexTestValidator.ts @@ -1,8 +1,8 @@ -import SFPLogger, { COLOR_HEADER, Logger } from "@dxatscale/sfp-logger"; -import { CoverageOptions } from "@dxatscale/sfpowerscripts.core/lib/apex/coverage/IndividualClassCoverage"; -import { TestOptions, RunAllTestsInPackageOptions, RunSpecifiedTestsOption } from "@dxatscale/sfpowerscripts.core/lib/apextest/TestOptions"; -import TriggerApexTests from "@dxatscale/sfpowerscripts.core/lib/apextest/TriggerApexTests"; -import SfpPackage, { PackageType } from "@dxatscale/sfpowerscripts.core/lib/package/SfpPackage"; +import SFPLogger, { COLOR_HEADER, Logger } from "@flxblio/sfp-logger"; +import { CoverageOptions } from "../../core/apex/coverage/IndividualClassCoverage"; +import { TestOptions, RunAllTestsInPackageOptions, RunSpecifiedTestsOption } from "../../core/apextest/TestOptions"; +import TriggerApexTests from "../../core/apextest/TriggerApexTests"; +import SfpPackage, { PackageType } from "../../core/package/SfpPackage"; import { LoggerLevel } from "@salesforce/core"; import { ValidationMode } from "./ValidateImpl"; diff --git a/packages/sfpowerscripts-cli/src/impl/validate/ValidateImpl.ts b/packages/sfpowerscripts-cli/src/impl/validate/ValidateImpl.ts index c01d52f7f..ff804e593 100644 --- a/packages/sfpowerscripts-cli/src/impl/validate/ValidateImpl.ts +++ b/packages/sfpowerscripts-cli/src/impl/validate/ValidateImpl.ts @@ -4,7 +4,7 @@ import DeployImpl, { DeployProps, DeploymentResult, } from "../deploy/DeployImpl"; -import ArtifactGenerator from "@dxatscale/sfpowerscripts.core/lib/artifacts/generators/ArtifactGenerator"; +import ArtifactGenerator from "../../core/artifacts/generators/ArtifactGenerator"; import { Stage } from "../Stage"; import SFPLogger, { COLOR_KEY_VALUE, @@ -12,40 +12,40 @@ import SFPLogger, { ConsoleLogger, Logger, LoggerLevel, -} from "@dxatscale/sfp-logger"; +} from "@flxblio/sfp-logger"; import { PackageInstallationResult, PackageInstallationStatus, -} from "@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/PackageInstallationResult"; -import { PackageDiffOptions } from "@dxatscale/sfpowerscripts.core/lib/package/diff/PackageDiffImpl"; -import PoolFetchImpl from "@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolFetchImpl"; +} from "../../core/package/packageInstallers/PackageInstallationResult"; +import { PackageDiffOptions } from "../../core/package/diff/PackageDiffImpl"; +import PoolFetchImpl from "../../core/scratchorg/pool/PoolFetchImpl"; import { Org } from "@salesforce/core"; -import InstalledArtifactsDisplayer from "@dxatscale/sfpowerscripts.core/lib/display/InstalledArtifactsDisplayer"; +import InstalledArtifactsDisplayer from "../../core/display/InstalledArtifactsDisplayer"; import ValidateError from "../../errors/ValidateError"; -import ScratchOrg from "@dxatscale/sfpowerscripts.core/lib/scratchorg/ScratchOrg"; -import { COLOR_KEY_MESSAGE } from "@dxatscale/sfp-logger"; -import { COLOR_WARNING } from "@dxatscale/sfp-logger"; -import { COLOR_ERROR } from "@dxatscale/sfp-logger"; -import { COLOR_HEADER } from "@dxatscale/sfp-logger"; -import { COLOR_SUCCESS } from "@dxatscale/sfp-logger"; -import { COLOR_TIME } from "@dxatscale/sfp-logger"; -import SFPStatsSender from "@dxatscale/sfpowerscripts.core/lib/stats/SFPStatsSender"; -import ScratchOrgInfoFetcher from "@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/services/fetchers/ScratchOrgInfoFetcher"; -import ScratchOrgInfoAssigner from "@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/services/updaters/ScratchOrgInfoAssigner"; +import ScratchOrg from "../../core/scratchorg/ScratchOrg"; +import { COLOR_KEY_MESSAGE } from "@flxblio/sfp-logger"; +import { COLOR_WARNING } from "@flxblio/sfp-logger"; +import { COLOR_ERROR } from "@flxblio/sfp-logger"; +import { COLOR_HEADER } from "@flxblio/sfp-logger"; +import { COLOR_SUCCESS } from "@flxblio/sfp-logger"; +import { COLOR_TIME } from "@flxblio/sfp-logger"; +import SFPStatsSender from "../../core/stats/SFPStatsSender"; +import ScratchOrgInfoFetcher from "../../core/scratchorg/pool/services/fetchers/ScratchOrgInfoFetcher"; +import ScratchOrgInfoAssigner from "../../core/scratchorg/pool/services/updaters/ScratchOrgInfoAssigner"; import ValidateResult from "./ValidateResult"; -import PoolOrgDeleteImpl from "@dxatscale/sfpowerscripts.core/lib/scratchorg/pool/PoolOrgDeleteImpl"; -import SFPOrg from "@dxatscale/sfpowerscripts.core/lib/org/SFPOrg"; +import PoolOrgDeleteImpl from "../../core/scratchorg/pool/PoolOrgDeleteImpl"; +import SFPOrg from "../../core/org/SFPOrg"; import SfpPackage, { PackageType, -} from "@dxatscale/sfpowerscripts.core/lib/package/SfpPackage"; +} from "../../core/package/SfpPackage"; -import getFormattedTime from "@dxatscale/sfpowerscripts.core/lib/utils/GetFormattedTime"; +import getFormattedTime from "../../core/utils/GetFormattedTime"; import { PostDeployHook } from "../deploy/PostDeployHook"; import * as rimraf from "rimraf"; -import ProjectConfig from "@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig"; -import InstallUnlockedPackageCollection from "@dxatscale/sfpowerscripts.core/lib/package/packageInstallers/InstallUnlockedPackageCollection"; -import ExternalPackage2DependencyResolver from "@dxatscale/sfpowerscripts.core/lib/package/dependencies/ExternalPackage2DependencyResolver"; -import ExternalDependencyDisplayer from "@dxatscale/sfpowerscripts.core/lib/display/ExternalDependencyDisplayer"; +import ProjectConfig from "../../core/project/ProjectConfig"; +import InstallUnlockedPackageCollection from "../../core/package/packageInstallers/InstallUnlockedPackageCollection"; +import ExternalPackage2DependencyResolver from "../../core/package/dependencies/ExternalPackage2DependencyResolver"; +import ExternalDependencyDisplayer from "../../core/display/ExternalDependencyDisplayer"; import { PreDeployHook } from "../deploy/PreDeployHook"; import GroupConsoleLogs from "../../ui/GroupConsoleLogs"; import ReleaseConfig from "../release/ReleaseConfig"; @@ -107,9 +107,9 @@ export default class ValidateImpl implements PostDeployHook, PreDeployHook { } else if ( this.props.validateAgainst === ValidateAgainst.PRECREATED_POOL ) { - if (process.env.SFPOWERSCRIPTS_DEBUG_PREFETCHED_SCRATCHORG) + if (process.env.sfp_DEBUG_PREFETCHED_SCRATCHORG) scratchOrgUsername = - process.env.SFPOWERSCRIPTS_DEBUG_PREFETCHED_SCRATCHORG; + process.env.sfp_DEBUG_PREFETCHED_SCRATCHORG; else scratchOrgUsername = await this.fetchScratchOrgFromPool( this.props.pools, @@ -138,7 +138,7 @@ export default class ValidateImpl implements PostDeployHook, PreDeployHook { let installedArtifacts = await this.orgAsSFPOrg.getInstalledArtifacts(); if (installedArtifacts.length == 0) { SFPLogger.log( - COLOR_ERROR("Failed to query org for Sfpowerscripts Artifacts"), + COLOR_ERROR("Failed to query org for sfp Artifacts"), ); } packagesInstalledInOrgMappedToCommits = diff --git a/packages/sfpowerscripts-cli/src/impl/validate/ValidateResult.ts b/packages/sfpowerscripts-cli/src/impl/validate/ValidateResult.ts index c25c30cc0..a8c360812 100644 --- a/packages/sfpowerscripts-cli/src/impl/validate/ValidateResult.ts +++ b/packages/sfpowerscripts-cli/src/impl/validate/ValidateResult.ts @@ -1,5 +1,5 @@ import { DeploymentResult, PackageInfo } from '../deploy/DeployImpl'; -import DependencyViolation from '@dxatscale/sfpowerscripts.core/lib/dependency/DependencyViolation'; +import DependencyViolation from '../../core/dependency/DependencyViolation'; export default interface ValidateResult { deploymentResult?: DeploymentResult; diff --git a/packages/sfpowerscripts-cli/src/outputs/FileOutputHandler.ts b/packages/sfpowerscripts-cli/src/outputs/FileOutputHandler.ts index cba026c60..1d473cf3d 100644 --- a/packages/sfpowerscripts-cli/src/outputs/FileOutputHandler.ts +++ b/packages/sfpowerscripts-cli/src/outputs/FileOutputHandler.ts @@ -7,7 +7,7 @@ export default class FileOutputHandler { public static getInstance() { if (!FileOutputHandler.instance) - FileOutputHandler.instance = new FileOutputHandler('.sfpowerscripts/outputs'); + FileOutputHandler.instance = new FileOutputHandler('.sfp/outputs'); return FileOutputHandler.instance; } diff --git a/packages/sfpowerscripts-cli/src/ui/GroupConsoleLogs.ts b/packages/sfpowerscripts-cli/src/ui/GroupConsoleLogs.ts index fee2cb14b..42f284f3c 100644 --- a/packages/sfpowerscripts-cli/src/ui/GroupConsoleLogs.ts +++ b/packages/sfpowerscripts-cli/src/ui/GroupConsoleLogs.ts @@ -1,5 +1,5 @@ -import SFPLogger, { Logger, LoggerLevel } from '@dxatscale/sfp-logger'; -import { COLOR_KEY_MESSAGE } from '@dxatscale/sfp-logger'; +import SFPLogger, { Logger, LoggerLevel } from '@flxblio/sfp-logger'; +import { COLOR_KEY_MESSAGE } from '@flxblio/sfp-logger'; import { EOL } from 'os'; //TODO: Move to sfpconsole package diff --git a/packages/sfpowerscripts-cli/src/ui/OrgInfoDisplayer.ts b/packages/sfpowerscripts-cli/src/ui/OrgInfoDisplayer.ts index 228aca8b0..f3ef01d3e 100644 --- a/packages/sfpowerscripts-cli/src/ui/OrgInfoDisplayer.ts +++ b/packages/sfpowerscripts-cli/src/ui/OrgInfoDisplayer.ts @@ -1,10 +1,10 @@ -import SFPLogger, { COLOR_HEADER, COLOR_KEY_VALUE, COLOR_KEY_MESSAGE, COLOR_TRACE } from "@dxatscale/sfp-logger"; -import SFPOrg from "@dxatscale/sfpowerscripts.core/lib/org/SFPOrg"; +import SFPLogger, { COLOR_HEADER, COLOR_KEY_VALUE, COLOR_KEY_MESSAGE, COLOR_TRACE } from "@flxblio/sfp-logger"; +import SFPOrg from "../core/org/SFPOrg"; const Table = require("cli-table"); import { LoggerLevel } from "@salesforce/core"; import GroupConsoleLogs from "./GroupConsoleLogs"; import { COLON_MIDDLE_BORDER_TABLE } from "./TableConstants"; -import ScratchOrg from "@dxatscale/sfpowerscripts.core/lib/scratchorg/ScratchOrg"; +import ScratchOrg from "../core/scratchorg/ScratchOrg"; import { Align, getMarkdownTable } from "markdown-table-ts"; import fs from "fs-extra"; import FileOutputHandler from "../outputs/FileOutputHandler"; diff --git a/packages/sfpowerscripts-cli/tests/ProjectValidation.test.ts b/packages/sfpowerscripts-cli/tests/ProjectValidation.test.ts index 3f131d071..a3a2f6da0 100644 --- a/packages/sfpowerscripts-cli/tests/ProjectValidation.test.ts +++ b/packages/sfpowerscripts-cli/tests/ProjectValidation.test.ts @@ -1,9 +1,9 @@ -import ProjectConfig from '@dxatscale/sfpowerscripts.core/lib/project/ProjectConfig'; +import ProjectConfig from '../src/core/project/ProjectConfig'; import { jest, expect } from '@jest/globals'; import ProjectValidation from '../src/ProjectValidation'; describe('Given a sfdx-project.json, it should be validated against the scehma', () => { - it('should not throw an error for a valid sfdx-project.json without any sfpowerscripts decorators', () => { + it('should not throw an error for a valid sfdx-project.json without any sfp decorators', () => { let sfdx_project = { packageDirectories: [ { @@ -108,7 +108,7 @@ describe('Given a sfdx-project.json, it should be validated against the scehma', }).not.toThrow(); }); - it('should not throw an error for a sfdx-project.json where various sfpowerscripts orchestrator properties are used', () => { + it('should not throw an error for a sfdx-project.json where various sfp orchestrator properties are used', () => { let sfdx_project = { packageDirectories: [ { @@ -184,7 +184,7 @@ describe('Given a sfdx-project.json, it should be validated against the scehma', }).not.toThrow(); }); - it('should not throw an error for a sfdx-project.json where various sfpowerscripts orchestrator properties are incorrectly used', () => { + it('should not throw an error for a sfdx-project.json where various sfp orchestrator properties are incorrectly used', () => { //As the errors are moved to warning, it will not throw an error let sfdx_project = { packageDirectories: [ diff --git a/packages/sfpowerscripts-cli/tests/core/apextest/ApexTestSuite.test.ts b/packages/sfpowerscripts-cli/tests/core/apextest/ApexTestSuite.test.ts new file mode 100644 index 000000000..ac9e026b3 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/apextest/ApexTestSuite.test.ts @@ -0,0 +1,77 @@ +import { jest, expect } from '@jest/globals'; +const fs = require('fs-extra'); +import ApexTestSuite from '../../../src/core/apextest/ApexTestSuite'; +import * as globSync from 'glob'; + + + +describe('Provided an apex test suite from a source directory', () => { + it('should return all the apexclasses', () => { + + + jest.spyOn(globSync, 'globSync').mockImplementationOnce((pattern: string | string[], options: any) => { + return new Array('/path/to/test.testSuite-meta.xml'); + }); + + + const fsReadMock = jest.spyOn(fs, 'readFileSync'); + fsReadMock.mockImplementationOnce(() => { + return ` + + + AccountAccountRelationTriggerTest + AccountContactRelationTriggerTest + AccountTeamMemberTriggerTest + AccountTriggerTest + ContactTriggerTest + + `; + }); + + let resultTestClasses = new Array(); + resultTestClasses.push(`AccountAccountRelationTriggerTest`); + resultTestClasses.push(`AccountContactRelationTriggerTest`); + resultTestClasses.push(`AccountTeamMemberTriggerTest`); + resultTestClasses.push(`AccountTriggerTest`); + resultTestClasses.push(`ContactTriggerTest`); + + let apexTestSuite = new ApexTestSuite(`dir`, `test`); + expect(apexTestSuite.getConstituentClasses()).resolves.toStrictEqual(resultTestClasses); + }); + + it('should throw an error if apex test suite is not avaiable in the directory', async () => { + + jest.spyOn(globSync, 'globSync').mockImplementationOnce((pattern: string | string[], options: any) => { + return []; + }); + + + + let apexTestSuite = new ApexTestSuite(`dir`, `test`); + + expect(apexTestSuite.getConstituentClasses()).rejects.toThrowError(); + }); + + it('should return apexclass even if there is only one', () => { + + jest.spyOn(globSync, 'globSync').mockImplementationOnce((pattern: string | string[], options: any) => { + return new Array('/path/to/test.testSuite-meta.xml');; + }); + + const fsReadMock = jest.spyOn(fs, 'readFileSync'); + fsReadMock.mockImplementationOnce(() => { + return ` + + + AccountAccountRelationTriggerTest + + `; + }); + + let resultTestClasses = new Array(); + resultTestClasses.push(`AccountAccountRelationTriggerTest`); + + let apexTestSuite = new ApexTestSuite(`dir`, `test`); + expect(apexTestSuite.getConstituentClasses()).resolves.toStrictEqual(resultTestClasses); + }); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/artifacts/ArtifactsFromFileSystem.test.ts b/packages/sfpowerscripts-cli/tests/core/artifacts/ArtifactsFromFileSystem.test.ts new file mode 100644 index 000000000..f30a3ab4f --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/artifacts/ArtifactsFromFileSystem.test.ts @@ -0,0 +1,52 @@ +import { jest, expect } from '@jest/globals'; +import ArtifactFetcher from '../../../src/core/artifacts/ArtifactFetcher'; +import * as globSync from 'glob'; + +describe('Provided a path to the artifacts folder containing sfp artifact', () => { + it('should return all the artifacts, if a package name is not provided', () => { + + jest.spyOn(globSync, 'globSync').mockImplementationOnce((pattern: string | string[], options: any) => { + return [ + '/path/to/core_sfp_artifact_1.0.0-2.zip', + '/path/to/core2_sfp_artifact_1.0.0-2.zip', + '/path/to/core3_sfp_artifact_1.0.0-3.zip', + '/path/to/my-package_sfp_artifact_3.30.53-NEXT.tgz' + ]; + }); + + + let artifacts = ArtifactFetcher.findArtifacts('artifacts'); + expect(artifacts).toEqual( + [ + '/path/to/core_sfp_artifact_1.0.0-2.zip', + '/path/to/core2_sfp_artifact_1.0.0-2.zip', + '/path/to/core3_sfp_artifact_1.0.0-3.zip', + '/path/to/my-package_sfp_artifact_3.30.53-NEXT.tgz' + ] + ); + }); + + it('provided only one artifact exists for a package and a package name is provided, it should just return the one artifact', () => { + + jest.spyOn(globSync, 'globSync').mockImplementationOnce((pattern: string | string[], options: any) => { + return new Array('/path/to/core_sfp_artifact_1.0.0-2.zip'); + }); + + let artifacts = ArtifactFetcher.findArtifacts('artifacts', 'core'); + expect(artifacts).toEqual(new Array('/path/to/core_sfp_artifact_1.0.0-2.zip')); + }); + + it('provided multiple artifacts of the same package exists and a package name is provied, it should return the latest', () => { + + jest.spyOn(globSync, 'globSync').mockImplementationOnce((pattern: string | string[], options: any) => { + return [ + '/path/to/core_sfp_artifact_1.0.0-2.zip', + '/path/to/core_sfp_artifact_1.0.0-3.zip', + '/path/to/core_sfp_artifact_1.0.0-4.zip', + '/path/to/core_sfp_artifact_1.0.0-5.tgz' + ]; + }); + let artifacts = ArtifactFetcher.findArtifacts('artifacts', 'core'); + expect(artifacts).toEqual(new Array('/path/to/core_sfp_artifact_1.0.0-5.tgz')); + }); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/coverage/IndividualClassCoverage.test.ts b/packages/sfpowerscripts-cli/tests/core/coverage/IndividualClassCoverage.test.ts new file mode 100644 index 000000000..f2be12f57 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/coverage/IndividualClassCoverage.test.ts @@ -0,0 +1,167 @@ +import IndividualClassCoverage from '../../../src/core/apex/coverage/IndividualClassCoverage'; +import { expect } from '@jest/globals'; +import { ConsoleLogger } from '@flxblio/sfp-logger'; + +describe('Given a test coverage report', () => { + it('should be able to get a list of all classes and its test coverage', () => { + let individualClasCoverage: IndividualClassCoverage = new IndividualClassCoverage( + testCoverage, + new ConsoleLogger() + ); + let expectedValue = [ + { name: 'CustomerServices', coveredPercent: 87 }, + { name: 'MarketServices', coveredPercent: 100 }, + { name: 'ReservationManagerController', coveredPercent: 72 }, + { name: 'ReservationManager', coveredPercent: 93 }, + ]; + expect(individualClasCoverage.getIndividualClassCoverage()).toEqual(expectedValue); + }); + + it('given a coverage threshold, provide a list of classes that do not satisfy the threshold', () => { + let individualClasCoverage: IndividualClassCoverage = new IndividualClassCoverage( + testCoverage, + new ConsoleLogger() + ); + let validationResult = individualClasCoverage.validateIndividualClassCoverage( + individualClasCoverage.getIndividualClassCoverage(), + 75 + ); + expect(validationResult.classesWithInvalidCoverage).toContainEqual({ + name: 'ReservationManagerController', + coveredPercent: 72, + }); + }); +}); + +let testCoverage = [ + { + id: '01p0w000001qr8HAAQ', + name: 'CustomerServices', + totalLines: 31, + lines: { + '3': 1, + '4': 1, + '5': 1, + '13': 1, + '15': 1, + '16': 1, + '17': 1, + '18': 1, + '19': 1, + '20': 1, + '21': 1, + '22': 1, + '25': 1, + '31': 1, + '34': 1, + '37': 1, + '40': 1, + '43': 0, + '46': 0, + '49': 1, + '57': 1, + '58': 1, + '59': 1, + '60': 1, + '61': 1, + '62': 1, + '63': 1, + '64': 1, + '65': 0, + '66': 1, + '67': 0, + }, + totalCovered: 27, + coveredPercent: 87, + }, + { + id: '01p0w000001qr8JAAQ', + name: 'MarketServices', + totalLines: 3, + lines: { + '3': 1, + '4': 1, + '16': 1, + }, + totalCovered: 3, + coveredPercent: 100, + }, + { + id: '01p0w000001qr8NAAQ', + name: 'ReservationManagerController', + totalLines: 32, + lines: { + '4': 1, + '7': 1, + '8': 1, + '17': 1, + '22': 1, + '23': 1, + '25': 1, + '26': 1, + '27': 1, + '28': 1, + '29': 1, + '30': 1, + '31': 1, + '32': 1, + '33': 1, + '34': 1, + '35': 1, + '36': 1, + '37': 1, + '39': 1, + '41': 1, + '42': 0, + '43': 0, + '44': 0, + '45': 0, + '46': 0, + '47': 0, + '48': 0, + '50': 0, + '52': 0, + '56': 1, + '57': 1, + }, + totalCovered: 23, + coveredPercent: 72, + }, + { + id: '01p0w000001qr8MAAQ', + name: 'ReservationManager', + totalLines: 28, + lines: { + '3': 1, + '6': 1, + '7': 1, + '8': 1, + '9': 1, + '10': 1, + '12': 1, + '13': 1, + '15': 1, + '20': 1, + '24': 1, + '25': 1, + '26': 1, + '27': 1, + '29': 1, + '30': 1, + '31': 1, + '34': 1, + '35': 1, + '36': 1, + '37': 1, + '39': 1, + '40': 1, + '41': 1, + '42': 1, + '43': 0, + '44': 0, + '48': 1, + }, + totalCovered: 26, + coveredPercent: 93, + }, +]; diff --git a/packages/sfpowerscripts-cli/tests/core/git/GitTags.test.ts b/packages/sfpowerscripts-cli/tests/core/git/GitTags.test.ts new file mode 100644 index 000000000..e87764aaa --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/git/GitTags.test.ts @@ -0,0 +1,83 @@ +import { jest, expect } from '@jest/globals'; +import GitTags from '../../../src/core/git/GitTags'; +import Git from '../../../src/core/git/Git'; + +import child_process = require('child_process'); + +let tags: string[]; +jest.mock('../../../src/core/git/Git', () => { + class Git { + tag = jest.fn().mockReturnValue(tags); + log = jest.fn().mockReturnValue(gitLog); + getRepositoryPath() + { + return process.cwd(); + } + static async initiateRepo(){ + return new Git(); + } + } + + return Git; +}); + +describe('Given a package, listTagsOnBranch', () => { + beforeEach(() => { + const childProcessMock = jest.spyOn(child_process, 'execSync'); + childProcessMock.mockImplementation(() => showRefs); + }); + + it('should return tags belonging to package, on current branch', async () => { + tags = coreTags; + let git: Git = await Git.initiateRepo(); + const gitTags: GitTags = new GitTags(git, 'core'); + expect(await gitTags.listTagsOnBranch()).toEqual(coreTags.slice(0, 4)); + }); + + it('should return an empty array if there are no tags', async () => { + tags = []; + let git: Git = await Git.initiateRepo(); + const gitTags: GitTags = new GitTags(git, 'core'); + expect(await gitTags.listTagsOnBranch()).toEqual([]); + }); + + it('should return an empty array if there are no tags belonging to package, on current branch', async () => { + tags = coreTags.slice(4); + let git: Git = await Git.initiateRepo(); + const gitTags: GitTags = new GitTags(git, 'core'); + expect(await gitTags.listTagsOnBranch()).toEqual([]); + }); +}); + +// Last two tags are not found on the current branch +const coreTags = [ + 'core_v1.0.0.11', + 'core_v1.0.0.43', + 'core_v1.0.0.48', + 'core_v1.0.0.53', + 'core_v1.0.0.85', + 'core_v1.0.0.163', +]; + +// Commits on current branch +const gitLog = [ + '9d7795b9e2391a93b72ae7cf391f55eac5a869c1', + '65ed6f19bb87d31e56efd49cd50a6a19ba172626', + '9e244f0048f53858fe5e5aff210805389f10e523', + '544b52bea434aed68770adb23c168bb89a35b031', +]; + +const showRefs = Buffer.from( + 'fc29c8bedb5cc32b425825aeec6c5ae054704b85 refs/tags/core_v1.0.0.11\n' + + '9d7795b9e2391a93b72ae7cf391f55eac5a869c1 refs/tags/core_v1.0.0.11^{}\n' + + '4fcb4b948d174e721093ff63ffff59cb220ddd7b refs/tags/core_v1.0.0.43\n' + + '65ed6f19bb87d31e56efd49cd50a6a19ba172626 refs/tags/core_v1.0.0.43^{}\n' + + 'ed45cbda7daee5152db1353960fe0ae3b8ad5ed2 refs/tags/core_v1.0.0.48\n' + + '9e244f0048f53858fe5e5aff210805389f10e523 refs/tags/core_v1.0.0.48^{}\n' + + '9eb7e59ef46890495b4c7d9e6cfb2c5e2ef85851 refs/tags/core_v1.0.0.53\n' + + '544b52bea434aed68770adb23c168bb89a35b031 refs/tags/core_v1.0.0.53^{}\n' + + '4af7e0c6b1f663e5b1c2ecc9e424fba2af8e0d63 refs/tags/core__v1.0.0.85\n' + + '46dd375e91d5c00a0f9b64ee38350171f9cf4e50 refs/tags/core_v1.0.0.85^{}\n' + + '1a5c15c8decb0a939447aebf057e1d0889f4eeb6 refs/tags/core_v1.0.0.163\n' + + '86f2f2d107564b053c40abe66555c354f3b7f0f8 refs/tags/core_v1.0.0.163^{}\n' +); diff --git a/packages/sfpowerscripts-cli/tests/core/org/ArtifactsToOrg.test.ts b/packages/sfpowerscripts-cli/tests/core/org/ArtifactsToOrg.test.ts new file mode 100644 index 000000000..d4b739399 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/org/ArtifactsToOrg.test.ts @@ -0,0 +1,214 @@ +import { expect } from '@jest/globals'; +import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup'; +import { ConsoleLogger, VoidLogger } from '@flxblio/sfp-logger'; +import { AnyJson, ensureJsonMap, JsonMap, ensureString } from '@salesforce/ts-types'; +import SFPOrg from '../../../src/core/org/SFPOrg'; +import SfpPackage from '../../../src/core/package/SfpPackage'; + + +const $$ = new TestContext(); +const createOrg = async () => { + + const testData = new MockTestOrgData(); + await $$.stubAuths(testData); + $$.setConfigStubContents('AuthInfoConfig', { + contents: await testData.getConfig(), + }); + + + return await SFPOrg.create({ aliasOrUsername: testData.username }); +}; + +describe('Fetch a list of sfp artifacts from an org', () => { + it('Return a blank list of sfp artifact, if there are no previously installed artifacts ', async () => { + let org = await createOrg(); + + let records = { records: [] }; + $$.fakeConnectionRequest = (request) => { + return Promise.resolve(records); + }; + + let artifacts = await org.getInstalledArtifacts(); + expect(artifacts).toEqual([]); + }); + + it('Return a list of sfp artifact, if there are previously installed artifacts ', async () => { + let org = await createOrg(); + + let records = { records:[ + { + Id: 'a0zR0000003F1FuIAK', + Name: 'sfp-package', + CommitId__c: '0a516404aa92f02866f9d2725bda5b1b3f23547e', + Version__c: '1.0.0.NEXT', + Tag__c: 'undefined', + }, + ]}; + + $$.fakeConnectionRequest = (request) => { + return Promise.resolve(records); + }; + + let artifacts = await org.getInstalledArtifacts(); + let expectedpackage = { + Id: 'a0zR0000003F1FuIAK', + Name: 'sfp-package', + CommitId__c: '0a516404aa92f02866f9d2725bda5b1b3f23547e', + Version__c: '1.0.0.NEXT', + Tag__c: 'undefined', + }; + expect(artifacts).toEqual([expectedpackage]); + }); + + it('When unable to fetch, it should return a blank list', async () => { + let org = await createOrg(); + + $$.fakeConnectionRequest = (request) => { + return Promise.reject('Failed'); + }; + + let artifacts = await org.getInstalledArtifacts(); + expect(artifacts).toEqual([]); + },45000); +}); + +describe('Update a sfp artifact to an org', () => { + it('Update a sfp artifact, installing it the first time', async () => { + let org = await createOrg(); + + let records = { records: [] }; + + let pushResult = { + id: 'a0zR0000003F1FuIAK', + success: true, + errors: [], + }; + + $$.fakeConnectionRequest = (request) => { + const _request = ensureJsonMap(request); + if (_request.method == `GET`) return Promise.resolve(records); + else return Promise.resolve(pushResult); + }; + + let sfpPackage: SfpPackage = { + package_name: 'core', + repository_url: 'https://example.com', + package_version_number: '1.0.0.NEXT', + sourceVersion: '3232x232xc3e', + projectDirectory: '', + workingDirectory: '', + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: '', + toJSON: function () { + throw new Error('Function not implemented.'); + }, + }; + + let result = await org.updateArtifactInOrg(new VoidLogger(), sfpPackage); + expect(result).toEqual(pushResult.id); + }); + + it('Update a sfp artifact, installing a newer version of it', async () => { + let org = await createOrg(); + + let records = { records : [ + { + Id: 'a0zR0000003F1FuIAK', + Name: 'core', + CommitId__c: '0a516404aa92f02866f9d2725bda5b1b3f23547e', + Version__c: '1.0.0.NEXT', + Tag__c: 'undefined', + } + ]}; + + let pushResult: AnyJson = { + id: 'a0zR0000003F1FuIAK', + success: true, + errors: [], + }; + + $$.fakeConnectionRequest = (request) => { + const _request: JsonMap = ensureJsonMap(request); + if (request && ensureString(_request.method) == `GET`) return Promise.resolve(records); + else return Promise.resolve(pushResult); + }; + + let sfpPackage: SfpPackage = { + package_name: 'core', + repository_url: 'https://example.com', + package_version_number: '1.0.0.NEXT', + sourceVersion: '3232x232xc3e', + projectDirectory: '', + workingDirectory: '', + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: '', + toJSON: function (): any { + throw new Error('Function not implemented.'); + }, + }; + + let result = await org.updateArtifactInOrg(new ConsoleLogger(), sfpPackage); + + expect(result).toEqual(pushResult.id); + }); + + it('Update a sfp artifact and resulting an error,should throw an exception', async () => { + let org = await createOrg(); + + let records={ records : [ + { + Id: 'a0zR0000003F1FuIAK', + Name: 'core', + CommitId__c: '0a516404aa92f02866f9d2725bda5b1b3f23547e', + Version__c: '1.0.0.NEXT', + Tag__c: 'undefined', + }, + ]}; + + let pushResult: AnyJson = { + success: false, + errors: [], + }; + + $$.fakeConnectionRequest = (request) => { + const _request: JsonMap = ensureJsonMap(request); + if (request && ensureString(_request.method) == `GET`) return Promise.resolve(records); + else return Promise.resolve(pushResult); + }; + + let sfpPackage: SfpPackage = { + package_name: 'core', + repository_url: 'https://example.com', + package_version_number: '1.0.0.NEXT', + sourceVersion: '3232x232xc3e', + projectDirectory: '', + workingDirectory: '', + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: '', + toJSON: function () { + throw new Error('Function not implemented.'); + }, + }; + + try { + await org.updateArtifactInOrg(new VoidLogger(), sfpPackage); + } catch (error) { + expect(error.message).toContain('Aborted'); + } + }); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/org/ListAllPackages.test.ts b/packages/sfpowerscripts-cli/tests/core/org/ListAllPackages.test.ts new file mode 100644 index 000000000..0c6af8b2b --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/org/ListAllPackages.test.ts @@ -0,0 +1,59 @@ +import { expect } from '@jest/globals'; +import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup'; +import { AnyJson } from '@salesforce/ts-types'; +import SFPOrg from '../../../src/core/org/SFPOrg'; +import { OrgConfigProperties } from '@salesforce/core'; + +const $$ = new TestContext(); + +describe('Retrieve all packages from devhub', () => { + it('should return all the packages provided a devhub', async () => { + + const testData = new MockTestOrgData(); + testData.makeDevHub(); + await $$.stubConfig({ [OrgConfigProperties.TARGET_ORG]: testData.username }); + $$.setConfigStubContents('AuthInfoConfig', { + contents: await testData.getConfig(), + }); + + let records: AnyJson = { + records: [ + { + attributes: { + type: 'Package2', + url: '/services/data/v53.0/tooling/sobjects/Package2/0Ho1P005000k9bNSXQ', + }, + Id: '0Ho1P005000k9bNSXQ', + Name: 'async-framework', + Description: null, + NamespacePrefix: null, + ContainerOptions: 'Unlocked', + IsOrgDependent: false, + }, + { + attributes: { + type: 'Package2', + url: '/services/data/v53.0/tooling/sobjects/Package2/0Ho1P005100k9bNSXQ', + }, + Id: '0Ho1P005100k9bNSXQ', + Name: 'async-framework2', + Description: null, + NamespacePrefix: null, + ContainerOptions: 'Unlocked', + IsOrgDependent: true, + }, + ], + }; + $$.fakeConnectionRequest = (request: AnyJson): Promise => { + return Promise.resolve(records); + }; + const org: SFPOrg = await SFPOrg.create({ aliasOrUsername: testData.username }); + + let packages = await org.listAllPackages(); + expect(packages).toHaveLength(2); + expect(packages[0].Name).toMatch('async-framework'); + expect(packages[0].Id).toMatch('0Ho1P005000k9bNSXQ'); + expect(packages[0].IsOrgDependent).toMatch('No'); + expect(packages[1].IsOrgDependent).toMatch('Yes'); //Translate true to Yes + }); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/package/Package2VersionFetcher.test.ts b/packages/sfpowerscripts-cli/tests/core/package/Package2VersionFetcher.test.ts new file mode 100644 index 000000000..2b7589393 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/Package2VersionFetcher.test.ts @@ -0,0 +1,79 @@ +import { expect } from '@jest/globals'; +import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup'; +import { Connection, AuthInfo } from '@salesforce/core'; +import Package2VersionFetcher from '../../../src/core/package/version/Package2VersionFetcher'; +import { AnyJson } from '@salesforce/ts-types'; + +const $$ = new TestContext(); + +let conn: Connection; + +describe("Given a PackageDependencyResolver", () => { + + beforeEach(async () => { + const testData = new MockTestOrgData(); + + testData.makeDevHub(); + $$.setConfigStubContents('AuthInfoConfig', { + contents: await testData.getConfig(), + }); + + let records: any = { + records: [ + { + attributes: { + type: 'Package2Version', + url: '/services/data/v57.0/tooling/sobjects/Package2Version/05i5i000000TNPWAA4' + }, + SubscriberPackageVersionId: '04t5i000000V2DiAAK', + Package2Id: '0Ho5i000000sYaWCAU', + Package2: { attributes: [Object], Name: 'core' }, + IsPasswordProtected: false, + IsReleased: false, + MajorVersion: 0, + MinorVersion: 1, + PatchVersion: 0, + BuildNumber: 17, + CodeCoverage: { apexCodeCoveragePercentage: 100 }, + HasPassedCodeCoverageCheck: true, + Branch: 'king' + }, + { + attributes: { + type: 'Package2Version', + url: '/services/data/v57.0/tooling/sobjects/Package2Version/05i5i000000TNOiAAO' + }, + SubscriberPackageVersionId: '04t5i000000UyCJAA0', + Package2Id: '0Ho5i000000sYaWCAU', + Package2: { attributes: [Object], Name: 'core' }, + IsPasswordProtected: false, + IsReleased: false, + MajorVersion: 0, + MinorVersion: 1, + PatchVersion: 0, + BuildNumber: 16, + CodeCoverage: { apexCodeCoveragePercentage: 100 }, + HasPassedCodeCoverageCheck: true, + Branch: 'king' + } + ], + }; + $$.fakeConnectionRequest = (request: any): Promise => { + return Promise.resolve(records); + }; + conn = await Connection.create({ + authInfo: await AuthInfo.create({username: testData.username}) + }); + }) + it('should return an array of Package2Version records if matching records found', async () => { + // Mock the query method in QueryHelper to return some dummy data + const package2VersionFetcher = new Package2VersionFetcher(conn); + + const result = await package2VersionFetcher.fetchByPackageBranchAndName('king', 'core'); + + expect(result[0].Package2.Name).toEqual('core'); + expect(result[0].Branch).toEqual('king'); + }); + +}); + diff --git a/packages/sfpowerscripts-cli/tests/core/package/PackageDiffImpl.test.ts b/packages/sfpowerscripts-cli/tests/core/package/PackageDiffImpl.test.ts new file mode 100644 index 000000000..75a8ed5e4 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/PackageDiffImpl.test.ts @@ -0,0 +1,295 @@ +import { jest, expect } from '@jest/globals'; +const fs = require('fs'); +import { ConsoleLogger } from '@flxblio/sfp-logger'; +import PackageDiffImpl, { PackageDiffOptions } from '../../../src/core/package/diff/PackageDiffImpl'; +import ProjectConfig from '../../../src/core/project/ProjectConfig'; + +let gitTags: string[] = []; +let gitDiff: string[] = []; +let gitShow: string = ''; + +jest.mock('../../../src/core/git/Git', () => { + class Git { + diff = jest.fn().mockReturnValue(gitDiff); + show = jest.fn().mockReturnValue(gitShow); + static async initiateRepo(){ + return new Git(); + } + } + + return Git; +}); + +jest.mock('../../../src/core/git/GitTags', () => { + class GitTags { + async listTagsOnBranch(): Promise { + return gitTags; + } + } + + return GitTags; +}); + +let ignoreFilterResult: string[] = []; +jest.mock('../../../src/core/ignore/IgnoreFiles', () => { + class IgnoreFiles { + filter = jest.fn().mockReturnValue(ignoreFilterResult); + } + + return IgnoreFiles; +}); + +describe('Determines whether a given package has changed', () => { + beforeEach(() => { + const projectConfigMock = jest.spyOn(ProjectConfig, 'getSFDXProjectConfig'); + projectConfigMock.mockImplementation(() => { + return JSON.parse(packageConfigJson); + }); + + const fsMock = jest.spyOn(fs, 'readFileSync'); + fsMock.mockImplementation(() => { + return '**README.md'; + }); + }); + + it('should throw error if package does not exist', () => { + let packageDiffImpl: PackageDiffImpl = new PackageDiffImpl( + new ConsoleLogger(), + 'UNKNOWN-PACKAGE', + null, + ); + expect(() => packageDiffImpl.exec()).rejects.toThrowError(); + }); + + it('should return true if package metadata has changed', async () => { + gitTags = coreTags; + gitDiff = [`packages/domains/core/X/Y/Z/A-meta.xml`]; + // No change in package config + gitShow = packageConfigJson; + + // Assume passthrough filter for ignore + ignoreFilterResult = gitDiff; + + let packageDiffImpl: PackageDiffImpl = new PackageDiffImpl(new ConsoleLogger(), 'core', null); + let result = await packageDiffImpl.exec(); + expect(result.isToBeBuilt).toEqual(true); + expect(result.reason).toEqual(`Found change(s) in package`); + + packageDiffImpl = new PackageDiffImpl(new ConsoleLogger(), 'core-b', null); + result = await packageDiffImpl.exec(); + expect(result.isToBeBuilt).toEqual(false); + + }); + + it('should return true if package descriptor has changed', async () => { + gitTags = coreTags; + gitDiff = ['sfdx-project.json']; + gitShow = packageDescriptorChange; + + // Assume passthrough filter for ignore + ignoreFilterResult = gitDiff; + + let packageDiffImpl: PackageDiffImpl = new PackageDiffImpl(new ConsoleLogger(), 'core', null); + let result = await packageDiffImpl.exec(); + expect(result.isToBeBuilt).toEqual(true); + expect(result.reason).toEqual(`Package Descriptor Changed`); + }); + + it('should return false even if package descriptor has changed when asked to ignore ', async () => { + gitTags = coreTags; + gitDiff = ['sfdx-project.json']; + gitShow = packageDescriptorChange; + + // Assume passthrough filter for ignore + ignoreFilterResult = gitDiff; + + let packageDiffImpl: PackageDiffImpl = new PackageDiffImpl(new ConsoleLogger(), 'core', null,{skipPackageDescriptorChange:true}); + let result = await packageDiffImpl.exec(); + expect(result.isToBeBuilt).toEqual(false); + }); + + it('should return false if only config file has changed', async () => { + gitDiff = ['config/project-scratch-def.json']; + + // No change in package config + gitShow = packageConfigJson; + + // Assume passthrough filter for ignore + ignoreFilterResult = gitDiff; + + gitTags = coreTags; + let packageDiffImpl: PackageDiffImpl = new PackageDiffImpl(new ConsoleLogger(), 'core', null); + let result = await packageDiffImpl.exec(); + expect(result.isToBeBuilt).toEqual(false); + expect(result.reason).toEqual(`No changes found`); + }); + + it('should return true if package does not have any tags', async () => { + gitTags = []; + + let packageDiffImpl: PackageDiffImpl = new PackageDiffImpl(new ConsoleLogger(), 'core', null); + let result = await packageDiffImpl.exec(); + expect(result.isToBeBuilt).toEqual(true); + expect(result.reason).toEqual(`Previous version not found`); + }); + + it('should return true if packageToCommits is an empty object', async () => { + let packageDiffOptions = new PackageDiffOptions(); + packageDiffOptions.packagesMappedToLastKnownCommitId={}; + let packageDiffImpl: PackageDiffImpl = new PackageDiffImpl(new ConsoleLogger(), 'core', null,packageDiffOptions); + let result = await packageDiffImpl.exec(); + expect(result.isToBeBuilt).toEqual(true); + expect(result.reason).toEqual(`Previous version not found`); + }); + + it('should return false if package metadata and package config has not changed', async () => { + gitTags = coreTags; + gitDiff = [`packages/access-mgmt/X/Y/Z/A-meta.xml`, `packages/bi/X/Y/Z/B-meta.xml`]; + // No change in package config + gitShow = packageConfigJson; + + // Assume passthrough filter for ignore + ignoreFilterResult = gitDiff; + + let packageDiffImpl: PackageDiffImpl = new PackageDiffImpl(new ConsoleLogger(), 'core', null); + let result = await packageDiffImpl.exec(); + expect(result.isToBeBuilt).toEqual(false); + expect(result.reason).toEqual(`No changes found`); + }); +}); + +const coreTags = [ + `core_v1.0.0.2`, + `core_v1.0.0.3`, + `core_v1.0.0.5`, + `core_v1.0.0.6`, + `core_v1.0.0.7`, + `core_v1.0.0.8`, + `core_v1.0.0.9`, + `core_v1.0.0.10`, +]; + +const packageConfigJson: string = ` +{ + "packageDirectories": [ + { + "path": "packages/temp", + "default": true, + "package": "temp", + "versionName": "temp", + "versionNumber": "1.0.0.0", + "ignoreOnStage": ["prepare","validate","build"] + }, + { + "path": "packages/domains/core", + "package": "core", + "default": false, + "versionName": "covax", + "versionNumber": "1.0.0.0", + "assignPermSetsPreDeployment": [ + "PermSetA", + "PermSetB", + "PermSetC" + ] + }, + { + "path": "packages/domains/core-b", + "package": "core-b", + "default": false, + "versionName": "covax", + "versionNumber": "1.0.0.0", + "assignPermSetsPreDeployment": [ + "PermSetA", + "PermSetB", + "PermSetC" + ] + }, + { + "path": "packages/frameworks/mass-dataload", + "package": "mass-dataload", + "default": false, + "type":"data", + "versionName": "mass-dataload", + "versionNumber": "1.0.0.0" + }, + { + "path": "packages/access-mgmt", + "package": "access-mgmt", + "default": false, + "versionName": "access-mgmt", + "versionNumber": "1.0.0.0", + "reconcileProfiles": "true" + }, + { + "path": "packages/bi", + "package": "bi", + "default": false, + "versionName": "bi", + "versionNumber": "1.0.0.0", + "ignoreOnStage":["prepare","validate"] + } + ], + "namespace": "", + "sfdcLoginUrl": "https://login.salesforce.com", + "sourceApiVersion": "50.0", + "packageAliases": { + "bi":"0x002232323232", + "core":"0H04X00000000XXXXX" + } +}`; + +const packageDescriptorChange: string = ` +{ + "packageDirectories": [ + { + "path": "packages/temp", + "default": true, + "package": "temp", + "versionName": "temp", + "versionNumber": "1.0.0.0", + "ignoreOnStage": ["prepare","validate","build"] + }, + { + "path": "packages/domains/core", + "package": "core", + "default": false, + "versionName": "covax", + "versionNumber": "1.0.0.0", + "assignPermSetsPreDeployment": [ + "PermSetA" + ] + }, + { + "path": "packages/frameworks/mass-dataload", + "package": "mass-dataload", + "default": false, + "type":"data", + "versionName": "mass-dataload", + "versionNumber": "1.0.0.0" + }, + { + "path": "packages/access-mgmt", + "package": "access-mgmt", + "default": false, + "versionName": "access-mgmt", + "versionNumber": "1.0.0.0", + "reconcileProfiles": "true" + }, + { + "path": "packages/bi", + "package": "bi", + "default": false, + "versionName": "bi", + "versionNumber": "1.0.0.0", + "ignoreOnStage":["prepare","validate"] + } + ], + "namespace": "", + "sfdcLoginUrl": "https://login.salesforce.com", + "sourceApiVersion": "50.0", + "packageAliases": { + "bi":"0x002232323232", + "core":"0H04X00000000XXXXX" + } +} +`; diff --git a/packages/sfpowerscripts-cli/tests/core/package/PackageManifest.test.ts b/packages/sfpowerscripts-cli/tests/core/package/PackageManifest.test.ts new file mode 100644 index 000000000..e9acf2b69 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/PackageManifest.test.ts @@ -0,0 +1,300 @@ +import fs from 'fs-extra'; +import { jest, expect } from '@jest/globals'; +import PackageManifest from '../../../src/core/package/components/PackageManifest'; + +describe('Given a mdapi directory that contains manifest file', () => { + beforeEach(() => { + const fsextraMock = jest.spyOn(fs, 'readFileSync'); + fsextraMock.mockImplementation((path: any, options: string | { encoding?: string; flag?: string }) => { + return packageManifestXML; + }); + }); + + it('should return the manifest in json format', async () => { + const packageManifest: PackageManifest = await PackageManifest.create('mdapi'); + expect(packageManifest.manifestJson).toEqual(packageManifestJSON); + }); + + it('should return the manifest in xml format', async () => { + const packageManifest: PackageManifest = await PackageManifest.create('mdapi'); + expect(packageManifest.manifestXml).toEqual(packageManifestXML); + }); + + it('should return true if there is apex', async () => { + const packageManifest: PackageManifest = await PackageManifest.create('mdapi'); + expect(packageManifest.isApexInPackage()).toBe(true); + }); + + it('should return undefined if there are no triggers', async () => { + const packageManifest: PackageManifest = await PackageManifest.create('mdapi'); + expect(packageManifest.fetchTriggers()).toBe(undefined); + }); + + it('should return false if there are no profiles', async () => { + const packageManifest: PackageManifest = await PackageManifest.create('mdapi'); + expect(packageManifest.isProfilesInPackage()).toBe(false); + }); + + it('should return false if there are no permission set groups', async () => { + const packageManifest: PackageManifest = await PackageManifest.create('mdapi'); + expect(packageManifest.isPermissionSetGroupsFoundInPackage()).toBe(false); + }); +}); + +describe('Given a list of components', () => { + it('should create a package manifest from scratch', () => { + const packageManifest: PackageManifest = PackageManifest.createFromScratch(components, '50.0'); + + expect(packageManifest).toBeInstanceOf(PackageManifest); + expect(packageManifest.manifestJson).toEqual(packageManifestJSON_b); + expect(packageManifest.manifestXml).toEqual(packageManifestXML_b); + }); + + it('should fetch triggers', () => { + const packageManifest: PackageManifest = PackageManifest.createFromScratch(components, '50.0'); + + expect(packageManifest.fetchTriggers()).toEqual(['ContractTrigger']); + }); + + it('should return true if there are permission set groups ', () => { + const packageManifest: PackageManifest = PackageManifest.createFromScratch(components, '50.0'); + + expect(packageManifest.isPermissionSetGroupsFoundInPackage()).toBe(true); + }); + + it('should return true if there are profiles', () => { + const packageManifest: PackageManifest = PackageManifest.createFromScratch(components, '50.0'); + + expect(packageManifest.isProfilesInPackage()).toBe(true); + }); + + it('should return true if there are types supported by profiles', async () => { + const packageManifest: PackageManifest = PackageManifest.createFromScratch(components, '50.0'); + expect(packageManifest.isPayLoadContainTypesSupportedByProfiles()).toBe(true); + }); + + it('should return true if there are types other than', async () => { + const packageManifest: PackageManifest = PackageManifest.createFromScratch(components, '50.0'); + expect(packageManifest.isPayloadContainTypesOtherThan('Profile')).toBe(true); + }); +}); + +describe('Given a manifest json payload', () => { + it('should create an instance of PackageManifest', async () => { + const packageManifest: PackageManifest = await PackageManifest.createWithJSONManifest(packageManifestJSON); + + expect(packageManifest).toBeInstanceOf(PackageManifest); + expect(packageManifest.manifestJson).toEqual(packageManifestJSON); + expect(packageManifest.manifestXml).toEqual(packageManifestXML); + }); +}); + +const components: { fullName: string; type: string }[] = [ + { + fullName: 'ContractService', + type: 'ApexClass', + }, + { + fullName: 'ContractTrigger', + type: 'ApexTrigger', + }, + { + fullName: 'ContractVariables__mdt-ContractVariables Layout', + type: 'Layout', + }, + { + fullName: 'Contract.Reason__c', + type: 'CustomField', + }, + { + fullName: 'Contract.ContractTerm__c', + type: 'CustomField', + }, + { + fullName: 'Contract', + type: 'CustomObject', + }, + { + fullName: 'ContractPermissionSetGroup', + type: 'PermissionSetGroup', + }, + { + fullName: 'Contractor', + type: 'Profile', + }, +]; + +const packageManifestJSON_b = { + Package: { + $: { + xmlns: 'http://soap.sforce.com/2006/04/metadata', + }, + types: [ + { + name: 'ApexClass', + members: ['ContractService'], + }, + { + name: 'ApexTrigger', + members: ['ContractTrigger'], + }, + { + name: 'Layout', + members: ['ContractVariables__mdt-ContractVariables Layout'], + }, + { + name: 'CustomField', + members: ['Contract.Reason__c', 'Contract.ContractTerm__c'], + }, + { + name: 'CustomObject', + members: ['Contract'], + }, + { + name: 'PermissionSetGroup', + members: ['ContractPermissionSetGroup'], + }, + { + name: 'Profile', + members: ['Contractor'], + }, + ], + version: '50.0', + }, +}; + +const packageManifestXML_b: string = ` + + + ApexClass + ContractService + + + ApexTrigger + ContractTrigger + + + Layout + ContractVariables__mdt-ContractVariables Layout + + + CustomField + Contract.Reason__c + Contract.ContractTerm__c + + + CustomObject + Contract + + + PermissionSetGroup + ContractPermissionSetGroup + + + Profile + Contractor + + 50.0 +`; + +const packageManifestJSON = { + Package: { + $: { xmlns: 'http://soap.sforce.com/2006/04/metadata' }, + types: [ + { + name: 'AuraDefinitionBundle', + members: ['openRecordAction', 'selectObject'], + }, + { + name: 'ApexClass', + members: [ + 'CustomerServices', + 'CustomerServicesTest', + 'MarketServices', + 'MarketServicesTest', + 'TestDataFactory', + ], + }, + { + name: 'CustomMetadata', + members: ['Customer_Fields.Contact_Customer_Fields', 'Customer_Fields.Lead_Customer_Fields'], + }, + { + name: 'Layout', + members: 'Customer_Fields__mdt-Customer Fields Layout', + }, + { name: 'LightningComponentBundle', members: ['errorPanel', 'ldsUtils'] }, + { + name: 'LightningMessageChannel', + members: ['Flow_Status_Change', 'Tile_Selection'], + }, + { name: 'CustomObject', members: 'Customer_Fields__mdt' }, + { + name: 'CustomField', + members: [ + 'Customer_Fields__mdt.Customer_City__c', + 'Customer_Fields__mdt.Customer_Draft_Status_Values__c', + 'Customer_Fields__mdt.Customer_Email__c', + 'Customer_Fields__mdt.Customer_Name__c', + 'Customer_Fields__mdt.Customer_Reservation_Status_Value__c', + 'Customer_Fields__mdt.Customer_State__c', + 'Customer_Fields__mdt.Customer_Status__c', + 'Customer_Fields__mdt.Sobject_Type__c', + ], + }, + ], + version: '50.0', + }, +}; + +const packageManifestXML: string = ` + + + AuraDefinitionBundle + openRecordAction + selectObject + + + ApexClass + CustomerServices + CustomerServicesTest + MarketServices + MarketServicesTest + TestDataFactory + + + CustomMetadata + Customer_Fields.Contact_Customer_Fields + Customer_Fields.Lead_Customer_Fields + + + Layout + Customer_Fields__mdt-Customer Fields Layout + + + LightningComponentBundle + errorPanel + ldsUtils + + + LightningMessageChannel + Flow_Status_Change + Tile_Selection + + + CustomObject + Customer_Fields__mdt + + + CustomField + Customer_Fields__mdt.Customer_City__c + Customer_Fields__mdt.Customer_Draft_Status_Values__c + Customer_Fields__mdt.Customer_Email__c + Customer_Fields__mdt.Customer_Name__c + Customer_Fields__mdt.Customer_Reservation_Status_Value__c + Customer_Fields__mdt.Customer_State__c + Customer_Fields__mdt.Customer_Status__c + Customer_Fields__mdt.Sobject_Type__c + + 50.0 +`; diff --git a/packages/sfpowerscripts-cli/tests/core/package/SFPackageBuilder.test.ts b/packages/sfpowerscripts-cli/tests/core/package/SFPackageBuilder.test.ts new file mode 100644 index 000000000..fa6936479 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/SFPackageBuilder.test.ts @@ -0,0 +1,374 @@ +import { jest, expect } from '@jest/globals'; +import SfpPackage, { PackageType, SfpPackageParams } from '../../../src/core/package/SfpPackage'; +import SfpPackageBuilder, { PackageCreationParams } from '../../../src/core/package/SfpPackageBuilder'; +import * as fs from "fs-extra"; +import { Logger } from '@flxblio/sfp-logger'; + + +let packageType = PackageType.Source; +jest.mock('../../../src/core/project/ProjectConfig', () => { + class ProjectConfig { + static getSFDXPackageDescriptor(projectDirectory, sfdx_package) { + return { + path: 'packages/domains/core', + package: 'core', + default: false, + versionName: 'core', + versionNumber: '1.0.0.0', + }; + } + + static getSFDXProjectConfig(projectDirectory) { + return { + packageDirectories: [ + { + path: 'packages/domains/core', + package: 'core', + default: false, + versionName: 'core', + versionNumber: '1.0.0.0', + }, + ], + namespace: '', + sfdcLoginUrl: 'https://login.salesforce.com', + sourceApiVersion: '50.0', + }; + } + static getPackageType(projectConfig: any, sfdxPackage: string) { + return packageType; + } + } + return ProjectConfig; +}); + +jest.mock('../../../src/core/package/generators/SfpPackageContentGenerator', () => { + class SfpPackageContentGenerator { + static async generateSfpPackageDirectory( + projectDirectory: string, + projectConfig: any, + sfdx_package: string, + packageDirectory: string, + destructiveManifestFilePath?: string, + configFilePath?: string, + pathToReplacementForceIgnore?: string + ): Promise { + return '.sfp/3sIRD_source'; + } + } + + return SfpPackageContentGenerator; +}); + +jest.mock('../../../src/core/package/packageFormatConvertors/SourceToMDAPIConvertor', () => { + class SourceToMDAPIConvertor { + convert = jest.fn().mockReturnValueOnce(Promise.resolve({ packagePath: 'mdapidir' })); + } + return SourceToMDAPIConvertor; +}); + +jest.mock('../../../src/core/package/components/MetadataCount', () => { + class MetadataCount { + static getMetadataCount = jest.fn().mockReturnValue(Promise.resolve(20)); + } + return MetadataCount; +}); + +jest.mock('../../../src/core/apex/parser/ApexTypeFetcher', () => { + class ApexTypeFetcher { + getClassesClassifiedByType = jest.fn(); + getTestClasses = jest + .fn() + .mockReturnValue( + new Array( + 'AccountTriggerHandlerTest', + 'Generate_Dose_Admin_PdfTest', + 'RecordHunterController_Test', + 'SObjectController2Test', + 'Send_Receipt_Test', + 'TestDataFactory', + 'TestFileRestriction', + 'appoinmentSchedulerControllerTest' + ) + ); + + getClassesOnlyExcludingTestsAndInterfaces = jest + .fn() + .mockReturnValue( + new Array( + 'AccountTriggerHandler', + 'Data_TableV2_Controller', + 'Generate_Dose_Admin_Pdf', + 'Generate_QR_Code', + 'RecordHunterController', + 'RecordHunterField', + 'RecordHunterLexer', + 'SObjectController2', + 'Send_Receipt' + ) + ); + } + + return ApexTypeFetcher; +}); + +jest.mock('../../../src/core/package/packageCreators/CreateUnlockedPackageImpl', () => { + class CreateUnlockedPackageImpl { + public constructor( + protected projectDirectory: string, + protected sfpPackage: SfpPackage, + protected packageCreationParams: PackageCreationParams, + protected logger?: Logger, + protected params?: SfpPackageParams + ) {} + + public async exec(): Promise { + return this.sfpPackage; + } + } + return CreateUnlockedPackageImpl; +}); + +// jest.mock('../../src/package/packageCreators/CreatePackage', () => { +// class CreatePackage { +// public constructor( +// protected projectDirectory: string, +// protected sfpPackage: SfpPackage, +// protected packageCreationParams: PackageCreationParams, +// protected logger?: Logger, +// protected params?: SfpPackageParams +// ) {} +// } +// return CreatePackage; +// }); + +jest.mock('../../../src/core/package/packageCreators/CreateSourcePackageImpl', () => { + + + class CreateSourcePackageImpl { + public constructor( + protected projectDirectory: string, + protected sfpPackage: SfpPackage, + protected packageCreationParams: PackageCreationParams, + protected logger?: Logger, + protected params?: SfpPackageParams + ) { + + } + + public async exec(): Promise { + this.sfpPackage.packageType = PackageType.Source; + return this.sfpPackage; + } + getTypeOfPackage() {} + isEmptyPackage(projectDirectory: string, packageDirectory: string){} + preCreatePackage(sfpPackage: SfpPackage) {} + createPackage(sfpPackage: SfpPackage) {} + postCreatePackage(sfpPackage: SfpPackage) {} + printAdditionalPackageSpecificHeaders() {} + } + return CreateSourcePackageImpl; +}); + +describe.skip('Given a sfdx package, build a sfp package', () => { + it('should build a sfp package', async () => { + const fsextraMock = jest.spyOn(fs, 'readFileSync'); + fsextraMock.mockImplementation((path: any, options: string | { encoding?: string; flag?: string }) => { + return packageManifestXML; + }); + + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory( + null, + null, + 'ESBaseCodeLWC' + ); + expect(sfpPackage.isProfilesFound).toStrictEqual(false); + expect(sfpPackage.isApexFound).toStrictEqual(true); + expect(sfpPackage.isPermissionSetGroupFound).toStrictEqual(true); + expect(sfpPackage.triggers).toBeUndefined(); + expect(sfpPackage.packageType).toStrictEqual(PackageType.Source); + expect(sfpPackage.payload).toStrictEqual(packageManifestJSON); + expect(sfpPackage.mdapiDir).toStrictEqual('mdapidir'); + expect(sfpPackage.packageDescriptor).toStrictEqual({ + path: 'packages/domains/core', + package: 'core', + default: false, + versionName: 'core', + versionNumber: '1.0.0.0', + }); + expect(sfpPackage.apexTestClassses).toStrictEqual( + new Array( + 'AccountTriggerHandlerTest', + 'Generate_Dose_Admin_PdfTest', + 'RecordHunterController_Test', + 'SObjectController2Test', + 'Send_Receipt_Test', + 'TestDataFactory', + 'TestFileRestriction', + 'appoinmentSchedulerControllerTest' + ) + ); + expect(sfpPackage.apexClassWithOutTestClasses).toStrictEqual( + new Array( + 'AccountTriggerHandler', + 'Data_TableV2_Controller', + 'Generate_Dose_Admin_Pdf', + 'Generate_QR_Code', + 'RecordHunterController', + 'RecordHunterField', + 'RecordHunterLexer', + 'SObjectController2', + 'Send_Receipt' + ) + ); + }); + + it('should build a sfp package when there is only one type', async () => { + const fsextraMock = jest.spyOn(fs, 'readFileSync'); + fsextraMock.mockImplementation((path: any, options: string | { encoding?: string; flag?: string }) => { + return packageManifestXML2; + }); + + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory( + null, + null, + 'ESBaseCodeLWC' + ); + expect(sfpPackage.isProfilesFound).toStrictEqual(true); + expect(sfpPackage.isApexFound).toStrictEqual(false); + expect(sfpPackage.triggers).toBeUndefined(); + expect(sfpPackage.packageType).toStrictEqual(PackageType.Source); + expect(sfpPackage.mdapiDir).toStrictEqual('mdapidir'); + expect(sfpPackage.packageDescriptor).toStrictEqual({ + path: 'packages/domains/core', + package: 'core', + default: false, + versionName: 'core', + versionNumber: '1.0.0.0', + }); + expect(sfpPackage.isPayLoadContainTypesSupportedByProfiles).toStrictEqual(false); + }); +}); + +let packageManifestJSON = { + Package: { + $: { xmlns: 'http://soap.sforce.com/2006/04/metadata' }, + types: [ + { + name: 'AuraDefinitionBundle', + members: ['openRecordAction', 'selectObject'], + }, + { + name: 'ApexClass', + members: [ + 'CustomerServices', + 'CustomerServicesTest', + 'MarketServices', + 'MarketServicesTest', + 'TestDataFactory', + ], + }, + { + name: 'CustomMetadata', + members: ['Customer_Fields.Contact_Customer_Fields', 'Customer_Fields.Lead_Customer_Fields'], + }, + { + name: 'Layout', + members: 'Customer_Fields__mdt-Customer Fields Layout', + }, + { name: 'LightningComponentBundle', members: ['errorPanel', 'ldsUtils'] }, + { + name: 'LightningMessageChannel', + members: ['Flow_Status_Change', 'Tile_Selection'], + }, + { name: 'CustomObject', members: 'Customer_Fields__mdt' }, + { name: 'PermissionSetGroup', members: 'TestPermissionSetGroup' }, + { + name: 'CustomField', + members: [ + 'Customer_Fields__mdt.Customer_City__c', + 'Customer_Fields__mdt.Customer_Draft_Status_Values__c', + 'Customer_Fields__mdt.Customer_Email__c', + 'Customer_Fields__mdt.Customer_Name__c', + 'Customer_Fields__mdt.Customer_Reservation_Status_Value__c', + 'Customer_Fields__mdt.Customer_State__c', + 'Customer_Fields__mdt.Customer_Status__c', + 'Customer_Fields__mdt.Sobject_Type__c', + ], + }, + ], + version: '50.0', + }, +}; + +let packageManifestXML: string = ` + + + + AuraDefinitionBundle + openRecordAction + selectObject + + + ApexClass + CustomerServices + CustomerServicesTest + MarketServices + MarketServicesTest + TestDataFactory + + + CustomMetadata + Customer_Fields.Contact_Customer_Fields + Customer_Fields.Lead_Customer_Fields + + + Layout + Customer_Fields__mdt-Customer Fields Layout + + + LightningComponentBundle + errorPanel + ldsUtils + + + LightningMessageChannel + Flow_Status_Change + Tile_Selection + + + CustomObject + Customer_Fields__mdt + + + PermissionSetGroup + TestPermissionSetGroup + + + CustomField + Customer_Fields__mdt.Customer_City__c + Customer_Fields__mdt.Customer_Draft_Status_Values__c + Customer_Fields__mdt.Customer_Email__c + Customer_Fields__mdt.Customer_Name__c + Customer_Fields__mdt.Customer_Reservation_Status_Value__c + Customer_Fields__mdt.Customer_State__c + Customer_Fields__mdt.Customer_Status__c + Customer_Fields__mdt.Sobject_Type__c + + 50.0 + +`; + +let packageManifestXML2: string = ` + + + + Profile + CustomerServices + CustomerServicesTest + MarketServices + MarketServicesTest + TestDataFactory + + 50.0 + +`; diff --git a/packages/sfpowerscripts-cli/tests/core/package/analysers/FHTAnalyzer.test.ts b/packages/sfpowerscripts-cli/tests/core/package/analysers/FHTAnalyzer.test.ts new file mode 100644 index 000000000..437c055d5 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/analysers/FHTAnalyzer.test.ts @@ -0,0 +1,360 @@ +import { jest, expect } from '@jest/globals'; +import FHTAnalyser from '../../../../src/core/package/analyser/FHTAnalyzer'; +import SfpPackage, { PackageType } from '../../../../src/core/package/SfpPackage'; +const fs = require('fs-extra'); +import { ComponentSet, SourceComponent, registry, VirtualDirectory } from '@salesforce/source-deploy-retrieve'; +import { VoidLogger } from '@flxblio/sfp-logger'; + +let isYamlFileFound: boolean = true; + +describe('FHT Analyzer', () => { + beforeEach(() => { + const fsReadMock = jest.spyOn(fs, 'readFileSync'); + fsReadMock.mockImplementationOnce(() => { + return ` + Account: + - Name + - Phone + Contact: + - Name + - Phone + `; + }); + const fsExistSyncMock = jest.spyOn(fs, 'existsSync'); + fsExistSyncMock.mockImplementationOnce(() => { + return isYamlFileFound; + }); + }); + + it('Should not be enabled for data packages', async () => { + let fhtAnalyzer = new FHTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: process.cwd(), + workingDirectory: 'force-app', + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Data, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + expect(await fhtAnalyzer.isEnabled(sfpPackage,new VoidLogger())).toBe(false); + }); + + it('Should be enabled for source packages by default', async () => { + let fhtAnalyzer = new FHTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: '', + workingDirectory: process.cwd(), + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Source, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + expect(await fhtAnalyzer.isEnabled(sfpPackage,new VoidLogger())).toBe(true); + }); + + it('Should be enabled for unlocked packages by default', async () => { + let fhtAnalyzer = new FHTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: process.cwd(), + workingDirectory: 'force-app', + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Unlocked, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + expect(await fhtAnalyzer.isEnabled(sfpPackage,new VoidLogger())).toBe(true); + }); + + it(' When a yaml is provided and no additional fields, a sfpPackage with additional properties should be created', async () => { + + isYamlFileFound = true; + + const set = new ComponentSet(); + set.add({ fullName: 'MyClass', type: 'ApexClass' }); + set.add({ fullName: 'MyLayout', type: 'Layout' }); + + const componentSetMock = jest.spyOn(ComponentSet, 'fromSource'); + componentSetMock.mockImplementationOnce(() => { + return set; + }); + + let fhtAnalyzer = new FHTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: '', + packageDirectory: 'force-app', + workingDirectory: process.cwd(), + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Unlocked, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + sfpPackage = await fhtAnalyzer.analyze(sfpPackage,set,new VoidLogger()); + expect(sfpPackage['isFHTFieldFound']).toBe(true); + expect(sfpPackage['fhtFields']).toBeDefined(); + let fhtFields = sfpPackage['fhtFields']; + expect(fhtFields.Account).toStrictEqual(['Name', 'Phone']); + expect(fhtFields.Contact).toStrictEqual(['Name', 'Phone']); + }); + + it(' When a yaml is provided, package has no history enabled fields, a sfpPackage with combined additional properties should be created', async () => { + + isYamlFileFound = true; + + const set = new ComponentSet(); + set.add({ fullName: 'MyClass', type: 'ApexClass' }); + set.add({ fullName: 'MyLayout', type: 'Layout' }); + + const virtualFs: VirtualDirectory[] = [ + { + dirPath: '/main/default/object/Test__c/fields', + children: [ + { + name: 'AccountManager__c.field-meta.xml', + data: Buffer.from(` + + AccountManager__c + false + Account.AccountOwner__c + BlankAsZero + + false + true + false + Text + false + `), + }, + ], + }, + ]; + const customField = SourceComponent.createVirtualComponent( + { + name: 'AccountManager__c', + type: registry.types.customobject.children.types.customfield, + xml: '/main/default/object/Test__c/fields/AccountManager__c.field-meta.xml', + parent: SourceComponent.createVirtualComponent({ + name: 'Test__c', + type: registry.types.customobject, + xml: '/main/default/object/Test__c.object-meta.xml', + }), + }, + virtualFs + ); + + set.add(customField); + + const componentSetMock = jest.spyOn(ComponentSet, 'fromSource'); + componentSetMock.mockImplementationOnce(() => { + return set; + }); + + let fhtAnalyzer = new FHTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: '', + packageDirectory: 'force-app', + workingDirectory: process.cwd(), + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Unlocked, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + sfpPackage = await fhtAnalyzer.analyze(sfpPackage,set,new VoidLogger()); + expect(sfpPackage['isFHTFieldFound']).toBe(true); + expect(sfpPackage['fhtFields']).toBeDefined(); + let fhtFields = sfpPackage['fhtFields']; + expect(fhtFields.Account).toStrictEqual(['Name', 'Phone']); + expect(fhtFields.Contact).toStrictEqual(['Name', 'Phone']); + expect(fhtFields.Test__c).toStrictEqual(['AccountManager__c']); + }); + + it(' When a yaml is provided, package has no history enabled fields, a sfpPackage with combined additional properties should be created', async () => { + + + isYamlFileFound = true; + + const set = new ComponentSet(); + set.add({ fullName: 'MyClass', type: 'ApexClass' }); + set.add({ fullName: 'MyLayout', type: 'Layout' }); + + const virtualFs: VirtualDirectory[] = [ + { + dirPath: '/main/default/object/Test__c/fields', + children: [ + { + name: 'AccountManager__c.field-meta.xml', + data: Buffer.from(` + + AccountManager__c + false + Account.AccountOwner__c + BlankAsZero + + false + false + false + Text + false + `), + }, + ], + }, + ]; + const customField = SourceComponent.createVirtualComponent( + { + name: 'AccountManager__c', + type: registry.types.customobject.children.types.customfield, + xml: '/main/default/object/Test__c/fields/AccountManager__c.field-meta.xml', + parent: SourceComponent.createVirtualComponent({ + name: 'Test__c', + type: registry.types.customobject, + xml: '/main/default/object/Test__c.object-meta.xml', + }), + }, + virtualFs + ); + + set.add(customField); + + const componentSetMock = jest.spyOn(ComponentSet, 'fromSource'); + componentSetMock.mockImplementationOnce(() => { + return set; + }); + + let fhtAnalyzer = new FHTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: '', + packageDirectory: 'force-app', + workingDirectory: process.cwd(), + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Unlocked, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + sfpPackage = await fhtAnalyzer.analyze(sfpPackage,set,new VoidLogger()); + expect(sfpPackage['isFHTFieldFound']).toBe(true); + expect(sfpPackage['fhtFields']).toBeDefined(); + let fhtFields = sfpPackage['fhtFields']; + expect(fhtFields.Account).toStrictEqual(['Name', 'Phone']); + expect(fhtFields.Contact).toStrictEqual(['Name', 'Phone']); + expect(fhtFields).not.toHaveProperty('Test__c'); + }); + + it(' When no yaml is provided, package has history enabled fields, a sfpPackage with combined additional properties should be created', async () => { + + + isYamlFileFound = false; + + const set = new ComponentSet(); + set.add({ fullName: 'MyClass', type: 'ApexClass' }); + set.add({ fullName: 'MyLayout', type: 'Layout' }); + + const virtualFs: VirtualDirectory[] = [ + { + dirPath: '/main/default/object/Test__c/fields', + children: [ + { + name: 'AccountManager__c.field-meta.xml', + data: Buffer.from(` + + AccountManager__c + false + Account.AccountOwner__c + BlankAsZero + + false + true + false + Text + false + `), + }, + ], + }, + ]; + const customField = SourceComponent.createVirtualComponent( + { + name: 'AccountManager__c', + type: registry.types.customobject.children.types.customfield, + xml: '/main/default/object/Test__c/fields/AccountManager__c.field-meta.xml', + parent: SourceComponent.createVirtualComponent({ + name: 'Test__c', + type: registry.types.customobject, + xml: '/main/default/object/Test__c.object-meta.xml', + }), + }, + virtualFs + ); + + set.add(customField); + + const componentSetMock = jest.spyOn(ComponentSet, 'fromSource'); + componentSetMock.mockImplementationOnce(() => { + return set; + }); + + let fhtAnalyzer = new FHTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: '', + packageDirectory: 'force-app', + workingDirectory: process.cwd(), + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Unlocked, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + sfpPackage = await fhtAnalyzer.analyze(sfpPackage,set,new VoidLogger()); + expect(sfpPackage['isFHTFieldFound']).toBe(true); + expect(sfpPackage['fhtFields']).toBeDefined(); + let fhtFields = sfpPackage['fhtFields']; + expect(fhtFields.Test__c).toStrictEqual(['AccountManager__c']); + }); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/package/analysers/FTAnalyzer.test.ts b/packages/sfpowerscripts-cli/tests/core/package/analysers/FTAnalyzer.test.ts new file mode 100644 index 000000000..bc78d22b0 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/analysers/FTAnalyzer.test.ts @@ -0,0 +1,363 @@ +import { jest, expect } from '@jest/globals'; +import FTAnalyser from '../../../../src/core/package/analyser/FTAnalyzer'; +import SfpPackage, { PackageType } from '../../../../src/core/package/SfpPackage'; +const fs = require('fs-extra'); +import { ComponentSet, SourceComponent, registry, VirtualDirectory } from '@salesforce/source-deploy-retrieve'; +import { VoidLogger } from '@flxblio/sfp-logger'; + +let isYamlFileFound: boolean = true; + +describe('FT Analyzer', () => { + beforeEach(() => { + const fsReadMock = jest.spyOn(fs, 'readFileSync'); + fsReadMock.mockImplementationOnce(() => { + return ` + Account: + - Name + - Phone + Contact: + - Name + - Phone + `; + }); + const fsExistSyncMock = jest.spyOn(fs, 'existsSync'); + fsExistSyncMock.mockImplementationOnce(() => { + return isYamlFileFound; + }); + }); + + it('Should not be enabled for data packages', async () => { + let ftAnalyzer = new FTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: process.cwd(), + workingDirectory: 'force-app', + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Data, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + expect(await ftAnalyzer.isEnabled(sfpPackage,new VoidLogger())).toBe(false); + }); + + it('Should be enabled for source packages by default', async () => { + let ftAnalyzer = new FTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: '', + workingDirectory: process.cwd(), + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Source, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + expect(await ftAnalyzer.isEnabled(sfpPackage,new VoidLogger())).toBe(true); + }); + + it('Should be enabled for unlocked packages by default', async () => { + let ftAnalyzer = new FTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: process.cwd(), + workingDirectory: 'force-app', + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Unlocked, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + expect(await ftAnalyzer.isEnabled(sfpPackage,new VoidLogger())).toBe(true); + }); + + it(' When a yaml is provided and no additional fields, a sfpPackage with additional properties should be created', async () => { + + isYamlFileFound = true; + + const set = new ComponentSet(); + set.add({ fullName: 'MyClass', type: 'ApexClass' }); + set.add({ fullName: 'MyLayout', type: 'Layout' }); + + const componentSetMock = jest.spyOn(ComponentSet, 'fromSource'); + componentSetMock.mockImplementationOnce(() => { + return set; + }); + + let ftAnalyzer = new FTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: '', + packageDirectory: 'force-app', + workingDirectory: process.cwd(), + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Unlocked, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + sfpPackage = await ftAnalyzer.analyze(sfpPackage,set,new VoidLogger()); + expect(sfpPackage['isFTFieldFound']).toBe(true); + expect(sfpPackage['ftFields']).toBeDefined(); + let ftFields = sfpPackage['ftFields']; + expect(ftFields.Account).toStrictEqual(['Name', 'Phone']); + expect(ftFields.Contact).toStrictEqual(['Name', 'Phone']); + }); + + it(' When a yaml is provided, package has no feed enabled fields, a sfpPackage with combined additional properties should be created', async () => { + + isYamlFileFound = true; + + const set = new ComponentSet(); + set.add({ fullName: 'MyClass', type: 'ApexClass' }); + set.add({ fullName: 'MyLayout', type: 'Layout' }); + + const virtualFs: VirtualDirectory[] = [ + { + dirPath: '/main/default/object/Test__c/fields', + children: [ + { + name: 'AccountManager__c.field-meta.xml', + data: Buffer.from(` + + AccountManager__c + false + Account.AccountOwner__c + BlankAsZero + + false + false + true + false + Text + false + `), + }, + ], + }, + ]; + const customField = SourceComponent.createVirtualComponent( + { + name: 'AccountManager__c', + type: registry.types.customobject.children.types.customfield, + xml: '/main/default/object/Test__c/fields/AccountManager__c.field-meta.xml', + parent: SourceComponent.createVirtualComponent({ + name: 'Test__c', + type: registry.types.customobject, + xml: '/main/default/object/Test__c.object-meta.xml', + }), + }, + virtualFs + ); + + set.add(customField); + + const componentSetMock = jest.spyOn(ComponentSet, 'fromSource'); + componentSetMock.mockImplementationOnce(() => { + return set; + }); + + let ftAnalyzer = new FTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: '', + packageDirectory: 'force-app', + workingDirectory: process.cwd(), + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Unlocked, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + sfpPackage = await ftAnalyzer.analyze(sfpPackage,set,new VoidLogger()); + expect(sfpPackage['isFTFieldFound']).toBe(true); + expect(sfpPackage['ftFields']).toBeDefined(); + let ftFields = sfpPackage['ftFields']; + expect(ftFields.Account).toStrictEqual(['Name', 'Phone']); + expect(ftFields.Contact).toStrictEqual(['Name', 'Phone']); + expect(ftFields.Test__c).toStrictEqual(['AccountManager__c']); + }); + + it(' When a yaml is provided, package has no feed enabled fields, a sfpPackage with combined additional properties should be created', async () => { + + + isYamlFileFound = true; + + const set = new ComponentSet(); + set.add({ fullName: 'MyClass', type: 'ApexClass' }); + set.add({ fullName: 'MyLayout', type: 'Layout' }); + + const virtualFs: VirtualDirectory[] = [ + { + dirPath: '/main/default/object/Test__c/fields', + children: [ + { + name: 'AccountManager__c.field-meta.xml', + data: Buffer.from(` + + AccountManager__c + false + Account.AccountOwner__c + BlankAsZero + + false + false + false + false + Text + false + `), + }, + ], + }, + ]; + const customField = SourceComponent.createVirtualComponent( + { + name: 'AccountManager__c', + type: registry.types.customobject.children.types.customfield, + xml: '/main/default/object/Test__c/fields/AccountManager__c.field-meta.xml', + parent: SourceComponent.createVirtualComponent({ + name: 'Test__c', + type: registry.types.customobject, + xml: '/main/default/object/Test__c.object-meta.xml', + }), + }, + virtualFs + ); + + set.add(customField); + + const componentSetMock = jest.spyOn(ComponentSet, 'fromSource'); + componentSetMock.mockImplementationOnce(() => { + return set; + }); + + let ftAnalyzer = new FTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: '', + packageDirectory: 'force-app', + workingDirectory: process.cwd(), + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Unlocked, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + sfpPackage = await ftAnalyzer.analyze(sfpPackage,set,new VoidLogger()); + expect(sfpPackage['isFTFieldFound']).toBe(true); + expect(sfpPackage['ftFields']).toBeDefined(); + let ftFields = sfpPackage['ftFields']; + expect(ftFields.Account).toStrictEqual(['Name', 'Phone']); + expect(ftFields.Contact).toStrictEqual(['Name', 'Phone']); + expect(ftFields).not.toHaveProperty('Test__c'); + }); + + it(' When no yaml is provided, package has feed enabled fields, a sfpPackage with combined additional properties should be created', async () => { + + + isYamlFileFound = false; + + const set = new ComponentSet(); + set.add({ fullName: 'MyClass', type: 'ApexClass' }); + set.add({ fullName: 'MyLayout', type: 'Layout' }); + + const virtualFs: VirtualDirectory[] = [ + { + dirPath: '/main/default/object/Test__c/fields', + children: [ + { + name: 'AccountManager__c.field-meta.xml', + data: Buffer.from(` + + AccountManager__c + false + Account.AccountOwner__c + BlankAsZero + + false + false + true + false + Text + false + `), + }, + ], + }, + ]; + const customField = SourceComponent.createVirtualComponent( + { + name: 'AccountManager__c', + type: registry.types.customobject.children.types.customfield, + xml: '/main/default/object/Test__c/fields/AccountManager__c.field-meta.xml', + parent: SourceComponent.createVirtualComponent({ + name: 'Test__c', + type: registry.types.customobject, + xml: '/main/default/object/Test__c.object-meta.xml', + }), + }, + virtualFs + ); + + set.add(customField); + + const componentSetMock = jest.spyOn(ComponentSet, 'fromSource'); + componentSetMock.mockImplementationOnce(() => { + return set; + }); + + let ftAnalyzer = new FTAnalyser(); + let sfpPackage: SfpPackage = { + projectDirectory: '', + packageDirectory: 'force-app', + workingDirectory: process.cwd(), + mdapiDir: '', + destructiveChangesPath: '', + resolvedPackageDirectory: '', + version: '', + packageName: '', + versionNumber: '', + packageType: PackageType.Unlocked, + package_name: '', + toJSON: function (): any { + return ''; + }, + }; + sfpPackage = await ftAnalyzer.analyze(sfpPackage,set,new VoidLogger()); + expect(sfpPackage['isFTFieldFound']).toBe(true); + expect(sfpPackage['ftFields']).toBeDefined(); + let ftFields = sfpPackage['ftFields']; + expect(ftFields.Test__c).toStrictEqual(['AccountManager__c']); + }); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/package/coverage/PackageTestCoverage.test.ts b/packages/sfpowerscripts-cli/tests/core/package/coverage/PackageTestCoverage.test.ts new file mode 100644 index 000000000..fa3ab1d6e --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/coverage/PackageTestCoverage.test.ts @@ -0,0 +1,330 @@ +import PackageTestCoverage from '../../../../src/core/package/coverage/PackageTestCoverage'; +import { jest, expect } from '@jest/globals'; +import { ConsoleLogger, Logger } from '@flxblio/sfp-logger'; +import ApexClassFetcher from '../../../../src/core/apex/ApexClassFetcher'; +import ApexTriggerFetcher from '../../../../src/core/apex/ApexTriggerFetcher'; +import ApexCodeCoverageAggregateFetcher from '../../../../src/core/apex/coverage/ApexCodeCoverageAggregateFetcher'; + +import { AuthInfo, ConfigAggregator, Connection, Org, OrgConfigProperties } from '@salesforce/core'; +import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup'; +import SfpPackage, { PackageType } from '../../../../src/core/package/SfpPackage'; +import SfpPackageBuilder from '../../../../src/core/package/SfpPackageBuilder'; +const $$ = new TestContext(); + +let packageType = PackageType.Unlocked; + + + +jest.mock('../../../../src/core/package/SfpPackageBuilder', () => { + class SfpPackageBuilder { + + public assignPermSetsPreDeployment?: string[]; + public assignPermSetsPostDeployment?: string[]; + + public static async buildPackageFromProjectDirectory( + logger: Logger, + projectDirectory: string, + sfdx_package: string + ) { + + + let sfpPackage: SfpPackage = new SfpPackage(); + sfpPackage.apexClassWithOutTestClasses = new Array('CustomerServices', 'MarketServices'); + sfpPackage.triggers = new Array('AccountTrigger'); + sfpPackage.packageType = packageType; + return sfpPackage; + } + } + + return SfpPackageBuilder; +}); + + +const setupConnection = async () => { + const testData = new MockTestOrgData(); + testData.makeDevHub(); + await $$.stubConfig({ [OrgConfigProperties.TARGET_ORG]: testData.username }); + const { value } = (await ConfigAggregator.create()).getInfo(OrgConfigProperties.TARGET_ORG); + await $$.stubAuths(testData); + await $$.stubAliases({ myAlias: testData.username }); + + + const conn = await Connection.create({ + authInfo: await AuthInfo.create({username: testData.username}) + }); + + return conn; + } + + + +describe('Given a sfp package and code coverage report, a package coverage calculator', () => { + it('should be able to provide the coverage of a provided unlocked package', async () => { + const conn = await setupConnection(); + + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory(null, 'es-base-code', null, null); + let packageTestCoverage: PackageTestCoverage = new PackageTestCoverage( + sfpPackage, + succesfulTestCoverage, + new ConsoleLogger(), + conn + ); + expect(await packageTestCoverage.getCurrentPackageTestCoverage()).toBe(89); + }); + + it('should able to validate whether the coverage of unlocked package is above a certain threshold', async () => { + const conn = await setupConnection(); + + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory(null, 'es-base-code', null, null); + let packageTestCoverage: PackageTestCoverage = new PackageTestCoverage( + sfpPackage, + succesfulTestCoverage, + new ConsoleLogger(), + conn + ); + let requiredCoverage = 80; + let result = await packageTestCoverage.validateTestCoverage(requiredCoverage); + expect(result.result).toBe(true); + expect(result.packageTestCoverage).toBe(89); + expect(result.message).toStrictEqual(`Package overall coverage is greater than ${requiredCoverage}%`); + expect(result.classesCovered).toStrictEqual([ + { name: 'CustomerServices', coveredPercent: 87.09677419354838 }, + { name: 'MarketServices', coveredPercent: 100 }, + { name: 'AccountTrigger', coveredPercent: 100 }, + ]); + expect(result.classesWithInvalidCoverage).toBeUndefined(); + }); + + it('should able to validate whether the coverage of unlocked package is above mandatory threshold', async () => { + const conn = await setupConnection(); + + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory(null, 'es-base-code', null, null); + let packageTestCoverage: PackageTestCoverage = new PackageTestCoverage( + sfpPackage, + succesfulTestCoverage, + new ConsoleLogger(), + conn + ); + let requiredCoverage = 75; + let result = await packageTestCoverage.validateTestCoverage(); + expect(result.result).toBe(true); + expect(result.packageTestCoverage).toBe(89); + expect(result.message).toStrictEqual(`Package overall coverage is greater than ${requiredCoverage}%`); + expect(result.classesCovered).toStrictEqual([ + { name: 'CustomerServices', coveredPercent: 87.09677419354838 }, + { name: 'MarketServices', coveredPercent: 100 }, + { name: 'AccountTrigger', coveredPercent: 100 }, + ]); + expect(result.classesWithInvalidCoverage).toBeUndefined(); + }); + + it('should be able to provide the coverage of a provided source package', async () => { + const conn = await setupConnection(); + + packageType = PackageType.Source; + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory(null, 'es-base-code', null, null); + let packageTestCoverage: PackageTestCoverage = new PackageTestCoverage( + sfpPackage, + succesfulTestCoverage, + new ConsoleLogger(), + conn + ); + expect(await packageTestCoverage.getCurrentPackageTestCoverage()).toBe(89); + }); + + it('should able to validate whether the coverage of source package is above a certain threshold', async () => { + const conn = await setupConnection(); + + packageType = PackageType.Source; + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory(null, 'es-base-code', null, null); + let packageTestCoverage: PackageTestCoverage = new PackageTestCoverage( + sfpPackage, + succesfulTestCoverage, + new ConsoleLogger(), + conn + ); + let requiredCoverage = 80; + let result = await packageTestCoverage.validateTestCoverage(requiredCoverage); + expect(result.result).toBe(true); + expect(result.packageTestCoverage).toBe(89); + expect(result.message).toStrictEqual(`Individidual coverage of classes is greater than ${requiredCoverage}%`); + expect(result.classesCovered).toStrictEqual([ + { name: 'CustomerServices', coveredPercent: 87.09677419354838 }, + { name: 'MarketServices', coveredPercent: 100 }, + { name: 'AccountTrigger', coveredPercent: 100 }, + ]); + expect(result.classesWithInvalidCoverage).toBeUndefined(); + }); + + it('should able to validate whether the coverage of source package is above mandatory threshold', async () => { + const conn = await setupConnection(); + + packageType = PackageType.Source; + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory(null, 'es-base-code', null, null); + let packageTestCoverage: PackageTestCoverage = new PackageTestCoverage( + sfpPackage, + succesfulTestCoverage, + new ConsoleLogger(), + conn + ); + let requiredCoverage = 75; + let result = await packageTestCoverage.validateTestCoverage(); + expect(result.result).toBe(true); + expect(result.packageTestCoverage).toBe(89); + expect(result.message).toStrictEqual(`Individidual coverage of classes is greater than ${requiredCoverage}%`); + expect(result.classesCovered).toStrictEqual([ + { name: 'CustomerServices', coveredPercent: 87.09677419354838 }, + { name: 'MarketServices', coveredPercent: 100 }, + { name: 'AccountTrigger', coveredPercent: 100 }, + ]); + expect(result.classesWithInvalidCoverage).toBeUndefined(); + }); + + it('should account for untouched classes and triggers when calculating package test coverage', async () => { + const conn = await setupConnection(); + + jest.spyOn(ApexClassFetcher.prototype, 'fetchApexClassByName').mockResolvedValue([ + { Id: '01p0w000001n1SfAAI', Name: 'MarketServices' }, + ]); + jest.spyOn(ApexTriggerFetcher.prototype, 'fetchApexTriggerByName').mockResolvedValue([ + { Id: '01p2O000003s9qcQAA', Name: 'AccountTrigger' }, + ]); + jest.spyOn(ApexCodeCoverageAggregateFetcher.prototype, 'fetchACCAById').mockResolvedValue([ + { ApexClassOrTriggerId: '01p0w000001n1SfAAI', NumLinesCovered: 0, NumLinesUncovered: 3, Coverage: {} }, + { ApexClassOrTriggerId: '01p2O000003s9qcQAA', NumLinesCovered: 0, NumLinesUncovered: 4, Coverage: {} }, + ]); + + packageType = PackageType.Source; + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory(null, 'es-base-code', null, null); + let packageTestCoverage: PackageTestCoverage = new PackageTestCoverage( + sfpPackage, + testCoverageWithUntouchedClasses, + new ConsoleLogger(), + conn + ); + let result = await packageTestCoverage.validateTestCoverage(); + + expect(result.result).toBe(false); + expect(result.packageTestCoverage).toBe(71); + expect(result.classesCovered).toEqual([ + { name: 'CustomerServices', coveredPercent: 87.09677419354838 }, + { name: 'MarketServices', coveredPercent: 0 }, + { name: 'AccountTrigger', coveredPercent: 0 }, + ]); + expect(result.classesWithInvalidCoverage).toEqual([ + { name: 'MarketServices', coveredPercent: 0 }, + { name: 'AccountTrigger', coveredPercent: 0 }, + ]); + }); +}); + +let succesfulTestCoverage = [ + { + id: '01p0w000001n1SdAAI', + name: 'CustomerServices', + totalLines: 31, + lines: { + '3': 1, + '4': 1, + '5': 1, + '13': 1, + '15': 1, + '16': 1, + '17': 1, + '18': 1, + '19': 1, + '20': 1, + '21': 1, + '22': 1, + '25': 1, + '31': 1, + '34': 1, + '37': 1, + '40': 1, + '43': 0, + '46': 0, + '49': 1, + '57': 1, + '58': 1, + '59': 1, + '60': 1, + '61': 1, + '62': 1, + '63': 1, + '64': 1, + '65': 0, + '66': 1, + '67': 0, + }, + totalCovered: 27, + coveredPercent: 87.09677419354838, + }, + { + id: '01p0w000001n1SfAAI', + name: 'MarketServices', + totalLines: 3, + lines: { + '3': 1, + '4': 1, + '16': 1, + }, + totalCovered: 3, + coveredPercent: 100, + }, + { + id: '01p2O000003s9qcQAA', + name: 'AccountTrigger', + totalLines: 4, + lines: { + '3': 1, + '5': 1, + '10': 1, + '11': 1, + }, + totalCovered: 4, + coveredPercent: 100, + }, +]; + +const testCoverageWithUntouchedClasses = [ + { + id: '01p0w000001n1SdAAI', + name: 'CustomerServices', + totalLines: 31, + lines: { + '3': 1, + '4': 1, + '5': 1, + '13': 1, + '15': 1, + '16': 1, + '17': 1, + '18': 1, + '19': 1, + '20': 1, + '21': 1, + '22': 1, + '25': 1, + '31': 1, + '34': 1, + '37': 1, + '40': 1, + '43': 0, + '46': 0, + '49': 1, + '57': 1, + '58': 1, + '59': 1, + '60': 1, + '61': 1, + '62': 1, + '63': 1, + '64': 1, + '65': 0, + '66': 1, + '67': 0, + }, + totalCovered: 27, + coveredPercent: 87.09677419354838, + }, +]; diff --git a/packages/sfpowerscripts-cli/tests/core/package/dependencies/PackageDependencyResolver.test.ts b/packages/sfpowerscripts-cli/tests/core/package/dependencies/PackageDependencyResolver.test.ts new file mode 100644 index 000000000..0a27c92a1 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/dependencies/PackageDependencyResolver.test.ts @@ -0,0 +1,346 @@ +import { jest, expect } from '@jest/globals'; +import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup'; +import { Connection, AuthInfo, OrgConfigProperties, ConfigAggregator } from '@salesforce/core'; +import PackageDependencyResolver from '../../../../src/core/package/dependencies/PackageDependencyResolver'; +const $$ = new TestContext(); + +const setupFakeConnection = async () => { + const testData = new MockTestOrgData(); + testData.makeDevHub(); + await $$.stubConfig({ [OrgConfigProperties.TARGET_ORG]: testData.username }); + const { value } = (await ConfigAggregator.create()).getInfo(OrgConfigProperties.TARGET_ORG); + await $$.stubAuths(testData); + await $$.stubAliases({ myAlias: testData.username }); + $$.fakeConnectionRequest = (request) => { + return Promise.resolve(response); + }; + + const conn = await Connection.create({ + authInfo: await AuthInfo.create({username: testData.username}) + }); + + return conn; +} + +jest.mock('../../../../src/core/git/Git', () => { + class Git { + static async initiateRepo() + { + return new Git(); + } + } + + return Git; +}); + +jest.mock('../../../../src/core/git/GitTags', () => { + class GitTags { + async listTagsOnBranch(): Promise { + return gitTags; + } + } + + return GitTags; +}); + +let conn: Connection; +let gitTags; +let response; + +describe("Given a PackageDependencyResolver", () => { + + beforeEach(async () => { + conn = await setupFakeConnection(); + gitTags = coreGitTags; + response = coreResponse; + }) + + it("should resolve package dependencies to current branch", async () => { + const packageDependencyResolver = new PackageDependencyResolver(conn, projectConfig, ["candidate-management", "contact-management"]); + const resolvedProjectConfig = await packageDependencyResolver.resolvePackageDependencyVersions(); + + let packageDescriptor = resolvedProjectConfig.packageDirectories.find((dir) => dir.package === "candidate-management"); + let coreDependency = packageDescriptor.dependencies.find(dependency => dependency.package === "core"); + expect(coreDependency.versionNumber).toBe("1.0.0.2"); + + packageDescriptor = resolvedProjectConfig.packageDirectories.find((dir) => dir.package === "contact-management"); + coreDependency = packageDescriptor.dependencies.find(dependency => dependency.package === "core"); + expect(coreDependency.versionNumber).toBe("1.0.0.2"); + }); + + it("should skip dependency resolution for packages that are not queued for build", async () => { + const packageDependencyResolver = new PackageDependencyResolver(conn, projectConfig, ["core"]); + const resolvedProjectConfig = await packageDependencyResolver.resolvePackageDependencyVersions(); + + let packageDescriptor = resolvedProjectConfig.packageDirectories.find((dir) => dir.package === "candidate-management"); + let coreDependency = packageDescriptor.dependencies.find(dependency => dependency.package === "core"); + expect(coreDependency.versionNumber).toBe("1.0.0.LATEST"); + + packageDescriptor = resolvedProjectConfig.packageDirectories.find((dir) => dir.package === "contact-management"); + coreDependency = packageDescriptor.dependencies.find(dependency => dependency.package === "core"); + expect(coreDependency.versionNumber).toBe("1.0.0.LATEST"); + }); + + it("should skip dependencies on packages from the same build", async () => { + const packageDependencyResolver = new PackageDependencyResolver(conn, projectConfig, ["core", "candidate-management", "contact-management"]); + const resolvedProjectConfig = await packageDependencyResolver.resolvePackageDependencyVersions(); + + let packageDescriptor = resolvedProjectConfig.packageDirectories.find((dir) => dir.package === "candidate-management"); + let coreDependency = packageDescriptor.dependencies.find(dependency => dependency.package === "core"); + expect(coreDependency.versionNumber).toBe("1.0.0.LATEST"); + + packageDescriptor = resolvedProjectConfig.packageDirectories.find((dir) => dir.package === "contact-management"); + coreDependency = packageDescriptor.dependencies.find(dependency => dependency.package === "core"); + expect(coreDependency.versionNumber).toBe("1.0.0.LATEST"); + }); + + it("should skip dependencies on a subscriber package version id", async () => { + const packageDependencyResolver = new PackageDependencyResolver(conn, projectConfig, ["candidate-management"]); + const resolvedProjectConfig = await packageDependencyResolver.resolvePackageDependencyVersions(); + + const packageDescriptor = resolvedProjectConfig.packageDirectories.find((dir) => dir.package === "candidate-management"); + const techFrameworkDependency = packageDescriptor.dependencies.find(dependency => dependency.package.startsWith("tech-framework")); + expect(techFrameworkDependency.versionNumber).toBeUndefined(); + }); + + it("should throw if dependency package version cannot be found for current branch ", async () => { + gitTags = []; + const packageDependencyResolver = new PackageDependencyResolver(conn, projectConfig, ["candidate-management"]); + expect(() => {return packageDependencyResolver.resolvePackageDependencyVersions()}).rejects.toThrow(); + }); + + it("should throw if there are no validated dependency package versions", async () => { + response = {records: []}; + const packageDependencyResolver = new PackageDependencyResolver(conn, projectConfig, ["candidate-management"]); + expect(() => {return packageDependencyResolver.resolvePackageDependencyVersions()}).rejects.toThrow(); + }); + + it("should throw if there are no validated dependency package id", async () => { + response = {records: []}; + const packageDependencyResolver = new PackageDependencyResolver(conn, falseProjectConfig, ["contact-management"]); + expect(() => {return packageDependencyResolver.resolvePackageDependencyVersions()}).rejects.toThrow(); + }); + it('should return the latest branched package version id if matching records found', async () => { + // Mock the query method in QueryHelper to return some dummy data + response = { + records: [ + { + attributes: { + type: 'Package2Version', + url: '/services/data/v57.0/tooling/sobjects/Package2Version/05i5i000000TNPWAA4' + }, + SubscriberPackageVersionId: '04t5i000000V2DiAAK', + Package2Id: '0Ho5i000000sYaWCAU', + Package2: { attributes: [Object], Name: 'core' }, + IsPasswordProtected: false, + IsReleased: false, + MajorVersion: 0, + MinorVersion: 1, + PatchVersion: 0, + BuildNumber: 17, + CodeCoverage: { apexCodeCoveragePercentage: 100 }, + HasPassedCodeCoverageCheck: true, + Branch: 'inspection' + }, + { + attributes: { + type: 'Package2Version', + url: '/services/data/v57.0/tooling/sobjects/Package2Version/05i5i000000TNOiAAO' + }, + SubscriberPackageVersionId: '04t5i000000UyCJAA0', + Package2Id: '0Ho5i000000sYaWCAU', + Package2: { attributes: [Object], Name: 'core' }, + IsPasswordProtected: false, + IsReleased: false, + MajorVersion: 0, + MinorVersion: 1, + PatchVersion: 0, + BuildNumber: 16, + CodeCoverage: { apexCodeCoveragePercentage: 100 }, + HasPassedCodeCoverageCheck: true, + Branch: 'inspection' + } + ], + }; + const packageDependencyResolver = new PackageDependencyResolver(conn, projectConfig, ["inspections"]); + const resolvedProjectConfig = await packageDependencyResolver.resolvePackageDependencyVersions(); + + expect(resolvedProjectConfig.packageAliases['core@0.1.0.17-inspection']).toEqual('04t5i000000V2DiAAK'); + }); + + // TODO: test cache +}); + +let coreGitTags = ['core_v1.0.0.2']; + +let coreResponse = { + records: [ + { + SubscriberPackageVersionId: '04t1P00000xxxxxxx3', + Package2Id: '0Ho4a00000000xxxxx', + Package2: { Name: 'core' }, + IsPasswordProtected: false, + IsReleased: false, + MajorVersion: 1, + MinorVersion: 0, + PatchVersion: 0, + BuildNumber: 3, + CodeCoverage: { apexCodeCoveragePercentage: 80 }, + HasPassedCodeCoverageCheck: true + }, + { + SubscriberPackageVersionId: '04t1P00000xxxxxxx2', + Package2Id: '0Ho4a00000000xxxxx', + Package2: { Name: 'core' }, + IsPasswordProtected: false, + IsReleased: false, + MajorVersion: 1, + MinorVersion: 0, + PatchVersion: 0, + BuildNumber: 2, + CodeCoverage: { apexCodeCoveragePercentage: 80 }, + HasPassedCodeCoverageCheck: true + }, + { + SubscriberPackageVersionId: '04t1P00000xxxxxxx1', + Package2Id: '0Ho4a00000000xxxxx', + Package2: { Name: 'core' }, + IsPasswordProtected: false, + IsReleased: false, + MajorVersion: 1, + MinorVersion: 0, + PatchVersion: 0, + BuildNumber: 1, + CodeCoverage: { apexCodeCoveragePercentage: 80 }, + HasPassedCodeCoverageCheck: true + }, + ] +} + +const projectConfig = { + packageDirectories: [ + { + path: 'packages/temp', + default: true, + package: 'temp', + versionName: 'temp', + versionNumber: '1.0.0.0', + }, + { + path: 'packages/core', + package: 'core', + default: false, + versionName: 'core-1.0.0', + versionNumber: '1.0.0.NEXT', + }, + { + path: 'packages/candidate-management', + package: 'candidate-management', + default: false, + versionName: 'candidate-management-1.0.0', + versionNumber: '1.0.0.NEXT', + dependencies: [ + { + package: 'tech-framework@2.0.0.38' + }, + { + package: 'core', + versionNumber: '1.0.0.LATEST', + } + ] + }, + { + path: 'packages/contact-management', + package: 'contact-management', + default: false, + versionName: 'contact-management-1.0.0', + versionNumber: '1.0.0.NEXT', + dependencies: [ + { + package: 'core', + versionNumber: '1.0.0.LATEST' + } + ] + }, + { + path: 'packages/inspections', + package: 'inspections', + default: false, + versionName: 'inspections-1.0.0', + versionNumber: '1.0.0.NEXT', + dependencies: [ + { + package: 'core', + versionNumber: '1.0.0.LATEST', + branch: 'inspection' + } + ] + } + ], + namespace: '', + sfdcLoginUrl: 'https://login.salesforce.com', + sourceApiVersion: '50.0', + packageAliases: { + "tech-framework@2.0.0.38": '04t1P00000xxxxxx00', + "core": '0Ho4a00000000xxxxx', + "candidate-management": '0Ho4a00000000xxxx1', + "contact-management": '0Ho4a00000000xxxx2' + } +}; + +const falseProjectConfig = { + packageDirectories: [ + { + path: 'packages/temp', + default: true, + package: 'temp', + versionName: 'temp', + versionNumber: '1.0.0.0', + }, + { + path: 'packages/core', + package: 'core', + default: false, + versionName: 'core-1.0.0', + versionNumber: '1.0.0.NEXT', + }, + { + path: 'packages/candidate-management', + package: 'candidate-management', + default: false, + versionName: 'candidate-management-1.0.0', + versionNumber: '1.0.0.NEXT', + dependencies: [ + { + package: 'tech-framework@2.0.0.38' + }, + { + package: 'core', + versionNumber: '1.0.0.LATEST' + } + ] + }, + { + path: 'packages/contact-management', + package: 'contact-management', + default: false, + versionName: 'contact-management-1.0.0', + versionNumber: '1.0.0.NEXT', + dependencies: [ + { + package: 'core', + versionNumber: '1.0.0.LATEST' + } + ] + } + ], + namespace: '', + sfdcLoginUrl: 'https://login.salesforce.com', + sourceApiVersion: '50.0', + packageAliases: { + "tech-framework@2.0.0.38": '04t1P00000xxxxxx00', + "candidate-management": '0Ho4a00000000xxxx1', + "contact-management": '0Ho4a00000000xxxx2' + } +}; + diff --git a/packages/sfpowerscripts-cli/tests/core/package/dependencies/TransitiveDependencyResolver.test.ts b/packages/sfpowerscripts-cli/tests/core/package/dependencies/TransitiveDependencyResolver.test.ts new file mode 100644 index 000000000..ff8343eb2 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/dependencies/TransitiveDependencyResolver.test.ts @@ -0,0 +1,235 @@ +import { jest, expect } from '@jest/globals'; +import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup'; +import { Connection, AuthInfo, OrgConfigProperties, ConfigAggregator } from '@salesforce/core'; +import TransitiveDependencyResolver from '../../../../src/core/package/dependencies/TransitiveDependencyResolver'; +const $$ = new TestContext(); + +const setupFakeConnection = async () => { + const testData = new MockTestOrgData(); + testData.makeDevHub(); + await $$.stubConfig({ [OrgConfigProperties.TARGET_ORG]: testData.username }); + const { value } = (await ConfigAggregator.create()).getInfo(OrgConfigProperties.TARGET_ORG); + await $$.stubAuths(testData); + await $$.stubAliases({ myAlias: testData.username }); + $$.fakeConnectionRequest = (request) => { + return Promise.resolve(response); + }; + + const conn = await Connection.create({ + authInfo: await AuthInfo.create({username: testData.username}) + }); + + return conn; +} + +jest.mock('../../../../src/core/git/Git', () => { + class Git { + static async initiateRepo() + { + return new Git(); + } + } + + return Git; +}); + +jest.mock('../../../../src/core/git/GitTags', () => { + class GitTags { + async listTagsOnBranch(): Promise { + return gitTags; + } + } + + return GitTags; +}); + +let conn: Connection; +let gitTags; +let response; + +describe("Given a TransitiveDependencyResolver", () => { + + beforeEach(async () => { + conn = await setupFakeConnection(); + + }) + + it("should resolve missing package dependencies with transitive dependency", async () => { + const transitiveDependencyResolver = new TransitiveDependencyResolver(projectConfig); + let resolvedDependencies = await transitiveDependencyResolver.resolveTransitiveDependencies(); + + let dependencies = resolvedDependencies.get('candidate-management'); + expect(dependencies?.find(dependency => dependency.package === "temp")).toBeTruthy(); + expect(dependencies?.find(dependency => dependency.package === "temp")?.versionNumber).toBe("1.0.0.LATEST"); + }); + + it("should resolve package dependencies in the same order as its dependent packages", async () => { + const transitiveDependencyResolver = new TransitiveDependencyResolver(projectConfig); + const resolvedDependencies = await transitiveDependencyResolver.resolveTransitiveDependencies(); + + let baseIndex = resolvedDependencies.get('candidate-management')?.findIndex(dependency => dependency.package === "base"); + expect(baseIndex).toBe(2); + let tempIndex = resolvedDependencies.get('candidate-management')?.findIndex(dependency => dependency.package === "temp"); + expect(tempIndex).toBe(3); + let coreIndex = resolvedDependencies.get('candidate-management')?.findIndex(dependency => dependency.package === "core"); + expect(coreIndex).toBe(4); + + }); + + + it("should resolve package dependencies with a higher version of a given package if a higher version is specified", async () => { + const transitiveDependencyResolver = new TransitiveDependencyResolver(projectConfig); + const resolvedDependencies = await transitiveDependencyResolver.resolveTransitiveDependencies(); + + let dependencies = resolvedDependencies.get('quote-management'); + expect(dependencies?.find(dependency => dependency.package === "core")?.versionNumber).toBe("1.2.0.LATEST"); + + }); + + it("should have only one version of a package", async () => { + const transitiveDependencyResolver = new TransitiveDependencyResolver(projectConfig); + const resolvedDependencies = await transitiveDependencyResolver.resolveTransitiveDependencies(); + expect(verifyUniquePkgs(resolvedDependencies.get('quote-management'))).toBeTruthy(); + + }); + + it("should expand the dependencies of external packages", async () => { + const transitiveDependencyResolver = new TransitiveDependencyResolver(projectConfig); + const resolvedDependencies = await transitiveDependencyResolver.resolveTransitiveDependencies(); + let externalDependencyIndex = resolvedDependencies.get('contact-management')?.findIndex(dependency => dependency.package === "sfdc-framework"); + expect(externalDependencyIndex).toBe(0); + + }); + + function verifyUniquePkgs(arr) { + let pkgs = {}; + for (let i = 0; i < arr.length; i++) { + if (arr[i].hasOwnProperty('package')) { + if (pkgs.hasOwnProperty(arr[i].package)) { + return false; + } + pkgs[arr[i].package] = true; + } + } + return true; + } + + + // TODO: test cache +}); + +const projectConfig = { + packageDirectories: [ + { + path: 'packages/base', + default: true, + package: 'base', + versionName: 'temp', + versionNumber: '1.0.2.NEXT', + }, + { + path: 'packages/temp', + default: true, + package: 'temp', + versionName: 'temp', + versionNumber: '1.0.0.NEXT', + dependencies: [ + { + package: 'base', + versionNumber: '1.0.2.LATEST' + } + ] + }, + { + path: 'packages/core', + package: 'core', + default: false, + versionName: 'core-1.0.0', + versionNumber: '1.0.0.NEXT', + dependencies: [ + { + package: 'temp', + versionNumber: '1.0.0.LATEST' + } + ] + }, + { + path: 'packages/candidate-management', + package: 'candidate-management', + default: false, + versionName: 'candidate-management-1.0.0', + versionNumber: '1.0.0.NEXT', + dependencies: [ + { + package: 'tech-framework@2.0.0.38' + }, + { + package: 'core', + versionNumber: '1.0.0.LATEST' + } + ] + }, + { + path: 'packages/contact-management', + package: 'contact-management', + default: false, + versionName: 'contact-management-1.0.0', + versionNumber: '1.0.0.NEXT', + dependencies: [ + { + package: 'tech-framework@2.0.0.38' + }, + { + package: 'core', + versionNumber: '1.0.0.LATEST' + }, + { + package: 'candidate-management', + versionNumber: '1.0.0.LATEST' + }, + ] + }, + { + path: 'packages/quote-management', + package: 'quote-management', + default: false, + versionName: 'quote-management-1.0.0', + versionNumber: '1.0.0.NEXT', + dependencies: [ + { + package: 'tech-framework@2.0.0.38' + }, + { + package: 'core', + versionNumber: '1.2.0.LATEST' + }, + { + package: 'candidate-management', + versionNumber: '1.0.0.LATEST' + }, + ] + } + ], + namespace: '', + sfdcLoginUrl: 'https://login.salesforce.com', + sourceApiVersion: '50.0', + packageAliases: { + "tech-framework@2.0.0.38": '04t1P00000xxxxxx00', + "candidate-management": '0Ho4a00000000xxxx1', + "contact-management": '0Ho4a00000000xxxx2', + "sfdc-framework":"04t1000x00x00x" + }, + "plugins": { + "sfp": { + "disableTransitiveDependencyResolver": false, + "externalDependencyMap": { + "tech-framework@2.0.0.38": [ + { + "package": "sfdc-framework" + } + ] + } + } + } +}; + diff --git a/packages/sfpowerscripts-cli/tests/core/package/deploymentFilters/EntitlementVersionFilter.test.ts b/packages/sfpowerscripts-cli/tests/core/package/deploymentFilters/EntitlementVersionFilter.test.ts new file mode 100644 index 000000000..cce32d2ff --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/deploymentFilters/EntitlementVersionFilter.test.ts @@ -0,0 +1,500 @@ +import { jest, expect } from '@jest/globals'; +import { MockTestOrgData, TestContext, } from '@salesforce/core/lib/testSetup'; +import { ConsoleLogger } from '@flxblio/sfp-logger'; +import { AnyJson } from '@salesforce/ts-types'; +import SFPOrg from '../../../../src/core/org/SFPOrg'; +import { ComponentSet, VirtualDirectory, VirtualTreeContainer } from '@salesforce/source-deploy-retrieve'; +import EntitlementVersionFilter from '../../../../src/core/package/deploymentFilters/EntitlementVersionFilter'; +import { OrgConfigProperties } from '@salesforce/core'; + +const fs = require('fs-extra'); + + +const $$ = new TestContext(); +const createOrg = async () => { + const testData = new MockTestOrgData(); + + await $$.stubAuths(testData); + await $$.stubAliases({ myAlias: testData.username }); + await $$.stubConfig({ [OrgConfigProperties.TARGET_ORG]: testData.username }); + + return await SFPOrg.create({ aliasOrUsername: testData.username }); +}; + +let entitlementSetting:any={}; +jest.mock('../../../../src/core/metadata/MetadataFetcher', () => { + class MetadataFetcher { + getSetttingMetadata= jest.fn().mockReturnValue(entitlementSetting) + } + + return MetadataFetcher; +}); + + +describe('Filter entitlements during deployment', () => { + + beforeEach(() => { + const fsMock = jest.spyOn(fs, 'writeFileSync'); + fsMock.mockImplementationOnce(() => { + return ; + }); + }); + + it('Should return a component set by filtering entitlement versions which are existing in the org', async () => { + + let org = await createOrg(); + let records: AnyJson = { + records: [ + { + Name: 'TestEntitlement1', + NameNorm: 'testentitlement1_v1', + VersionNumber: 1, + VersionMaster:'5522N000000c01Q', + } + ], + }; + $$.fakeConnectionRequest = (request: AnyJson): Promise => { + return Promise.resolve(records); + }; + + + const virtualFs: VirtualDirectory[] = [ + { + dirPath: '/metadata/entitlementProcesses', + children: [ + { + name: 'testentitlement1_v1.entitlementProcess-meta.xml', + data: Buffer.from(TESTENTITLEMENT_1) + }, + { + name: 'testentitlement2_v1.entitlementProcess', + data: Buffer.from(TESTENTITLEMENT_2) + } + ] + } + ] + + + + // resolve components of a virtual tree + const virtualTree = new VirtualTreeContainer(virtualFs); + const componentSet = ComponentSet.fromSource({ + fsPaths: ['/metadata/entitlementProcesses'], + tree: virtualTree, + }); + let entitlementVersionFilter:EntitlementVersionFilter=new EntitlementVersionFilter(); + entitlementSetting={ + "enableEntitlementVersioning":true + }; + let modifiedComponentSet = await entitlementVersionFilter.apply(org,componentSet,new ConsoleLogger()); + + let sourceComponents = modifiedComponentSet.getSourceComponents().toArray(); + expect(sourceComponents.find((element)=>element.name==`testentitlement1_v1`)).toBeUndefined(); + expect(sourceComponents.find((element)=>element.name==`testentitlement2_v1`)).toBeDefined(); + + + + }); + + it('Should only return component sets when the version number is higher than whats existing in the org', async () => { + entitlementSetting={enableEntitlementVersioning:true}; + let org = await createOrg(); + let records: AnyJson = { + records: [ + { + Name: 'TestEntitlement1', + NameNorm: 'testentitlement1_v1', + VersionNumber: 1, + VersionMaster:'5522N000000c01Q', + }, + { + Name: 'TestEntitlement2', + NameNorm: 'testentitlement2_v1', + VersionNumber: 1, + VersionMaster:'5522O000000LlFu', + } + ], + }; + $$.fakeConnectionRequest = (request: AnyJson): Promise => { + return Promise.resolve(records); + }; + + + const virtualFs: VirtualDirectory[] = [ + { + dirPath: '/metadata/entitlementProcesses', + children: [ + { + name: 'testentitlement1_v1.entitlementProcess-meta.xml', + data: Buffer.from(TESTENTITLEMENT_1) + }, + { + name: 'testentitlement2_v1.entitlementProcess-meta.xml', + data: Buffer.from(TESTENTITLEMENT_2) + }, + { + name: 'testentitlement2_v2.entitlementProcess', + data: Buffer.from(TESTENTITLEMENT_2_V2) + } + ] + } + ] + + + + // resolve components of a virtual tree + const virtualTree = new VirtualTreeContainer(virtualFs); + const componentSet = ComponentSet.fromSource({ + fsPaths: ['/metadata/entitlementProcesses'], + tree: virtualTree, + }); + let entitlementVersionFilter:EntitlementVersionFilter=new EntitlementVersionFilter(); + let modifiedComponentSet = await entitlementVersionFilter.apply(org,componentSet,new ConsoleLogger()); + + let sourceComponents = modifiedComponentSet.getSourceComponents().toArray(); + expect(sourceComponents.find((element)=>element.name==`testentitlement1_v1`)).toBeUndefined(); + expect(sourceComponents.find((element)=>element.name==`testentitlement2_v1`)).toBeUndefined(); + expect(sourceComponents.find((element)=>element.name==`testentitlement2_v2`)).toBeDefined(); + + + + }); + + + it('should return all components when there are no existing versions in the org', async () => { + entitlementSetting={enableEntitlementVersioning:true}; + let org = await createOrg(); + let records: AnyJson = { + records: [ + ], + }; + $$.fakeConnectionRequest = (request: AnyJson): Promise => { + return Promise.resolve(records); + }; + + + const virtualFs: VirtualDirectory[] = [ + { + dirPath: '/metadata/entitlementProcesses', + children: [ + { + name: 'testentitlement1_v1.entitlementProcess-meta.xml', + data: Buffer.from(TESTENTITLEMENT_1) + }, + { + name: 'testentitlement2_v1.entitlementProcess', + data: Buffer.from(TESTENTITLEMENT_1) + } + ] + } + ] + + + + // resolve components of a virtual tree + const virtualTree = new VirtualTreeContainer(virtualFs); + const componentSet = ComponentSet.fromSource({ + fsPaths: ['/metadata/entitlementProcesses'], + tree: virtualTree, + }); + let entitlementVersionFilter:EntitlementVersionFilter=new EntitlementVersionFilter(); + let modifiedComponentSet = await entitlementVersionFilter.apply(org,componentSet,new ConsoleLogger()); + + let sourceComponents = modifiedComponentSet.getSourceComponents().toArray(); + expect(sourceComponents.find((element)=>element.name==`testentitlement1_v1`)).toBeDefined(); + expect(sourceComponents.find((element)=>element.name==`testentitlement2_v1`)).toBeDefined(); + + + + }); + + it('should return all components when entitlement versioning is not enabled in the org', async () => { + entitlementSetting={enableEntitlementVersioning:undefined}; + let org = await createOrg(); + let records: AnyJson = { + records: [ + ], + }; + $$.fakeConnectionRequest = (request: AnyJson): Promise => { + return Promise.resolve(records); + }; + + + const virtualFs: VirtualDirectory[] = [ + { + dirPath: '/metadata/entitlementProcesses', + children: [ + { + name: 'TestEntitlement.entitlementProcess-meta.xml', + data: Buffer.from(TESTENTITLEMENT_NO_VERSION_NUMBER) + } + ] + } + ] + + + + // resolve components of a virtual tree + const virtualTree = new VirtualTreeContainer(virtualFs); + const componentSet = ComponentSet.fromSource({ + fsPaths: ['/metadata/entitlementProcesses'], + tree: virtualTree, + }); + let entitlementVersionFilter:EntitlementVersionFilter=new EntitlementVersionFilter(); + let modifiedComponentSet = await entitlementVersionFilter.apply(org,componentSet,new ConsoleLogger()); + + let sourceComponents = modifiedComponentSet.getSourceComponents().toArray(); + expect(sourceComponents.find((element)=>element.name==`TestEntitlement`)).toBeDefined(); + + + + + }); + + it('should return the same components when unable to fetch entitlement settings', async () => { + entitlementSetting=undefined; + let org = await createOrg(); + let records: AnyJson = { + records: [ + ], + }; + $$.fakeConnectionRequest = (request: AnyJson): Promise => { + return Promise.resolve(records); + }; + + + const virtualFs: VirtualDirectory[] = [ + { + dirPath: '/metadata/entitlementProcesses', + children: [ + { + name: 'TestEntitlement.entitlementProcess-meta.xml', + data: Buffer.from(TESTENTITLEMENT_NO_VERSION_NUMBER) + } + ] + } + ] + + + + // resolve components of a virtual tree + const virtualTree = new VirtualTreeContainer(virtualFs); + const componentSet = ComponentSet.fromSource({ + fsPaths: ['/metadata/entitlementProcesses'], + tree: virtualTree, + }); + let entitlementVersionFilter:EntitlementVersionFilter=new EntitlementVersionFilter(); + let modifiedComponentSet = await entitlementVersionFilter.apply(org,componentSet,new ConsoleLogger()); + + let sourceComponents = modifiedComponentSet.getSourceComponents().toArray(); + expect(sourceComponents.find((element)=>element.name==`TestEntitlement`)).toBeDefined(); + + + + + }); + +}); + + +const TESTENTITLEMENT_1=` + + Case + true + SLA Management of Case Resolution Time for AdCreation HK queue + Case.CreatedDate + + Case.IsClosed + equals + true + + true + + First Response to Customer + 999999 + false + + + + Case.Status + equals + New, Open, On Hold + + + Case.Type + equals + Hirer, Candidate, Internal, Partner + + + Case.Priority + equals + Urgent, Normal + + Case Resolution Time + 960 + + + Update_SLA_Breached_to_True + FieldUpdate + + 0 + Minutes + + false + + TestEntitlement1 + 5522O000000LlFu + 1 + +` + +const TESTENTITLEMENT_2=` + + Case + true + SLA Management of Case Resolution Time for AdCreation HK queue + Case.CreatedDate + + Case.IsClosed + equals + true + + true + + First Response to Customer + 999999 + false + + + + Case.Status + equals + New, Open, On Hold + + + Case.Type + equals + Hirer, Candidate, Internal, Partner + + + Case.Priority + equals + Urgent, Normal + + Case Resolution Time + 960 + + + Update_SLA_Breached_to_True + FieldUpdate + + 0 + Minutes + + false + + TestEntitlement2 + 5522O000000LlFu + 1 + +` + +const TESTENTITLEMENT_2_V2=` + + Case + true + SLA Management of Case Resolution Time for AdCreation HK queue + Case.CreatedDate + + Case.IsClosed + equals + true + + true + + First Response to Customer + 999999 + false + + + + Case.Status + equals + New, Open, On Hold + + + Case.Type + equals + Hirer, Candidate, Internal, Partner + + + Case.Priority + equals + Urgent, Normal + + Case Resolution Time + 960 + + + Update_SLA_Breached_to_True + FieldUpdate + + 0 + Minutes + + false + + TestEntitlement2 + 5522O000000LlFu + 2 + +` + +const TESTENTITLEMENT_NO_VERSION_NUMBER=` + + Case + true + SLA Management of Case Resolution Time for AdCreation HK queue + Case.CreatedDate + + Case.IsClosed + equals + true + + true + + First Response to Customer + 999999 + false + + + + Case.Status + equals + New, Open, On Hold + + + Case.Type + equals + Hirer, Candidate, Internal, Partner + + + Case.Priority + equals + Urgent, Normal + + Case Resolution Time + 960 + + + Update_SLA_Breached_to_True + FieldUpdate + + 0 + Minutes + + false + + TestEntitlement + +` diff --git a/packages/sfpowerscripts-cli/tests/core/package/packageMerger/PackageMergeManager.test.ts b/packages/sfpowerscripts-cli/tests/core/package/packageMerger/PackageMergeManager.test.ts new file mode 100644 index 000000000..5bfa5628e --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/packageMerger/PackageMergeManager.test.ts @@ -0,0 +1,39 @@ +import ArtifactFetcher from '../../../../src/core/artifacts/ArtifactFetcher'; +import SfpPackage from '../../../../src/core/package/SfpPackage'; +import SfpPackageBuilder from '../../../../src/core/package/SfpPackageBuilder'; +import PackageMergeManager from '../../../../src/core/package/packageMerger/PackageMergeManager' +import { ConsoleLogger } from '@flxblio/sfp-logger'; +import { ComponentSet } from '@salesforce/source-deploy-retrieve'; +import { jest, expect } from '@jest/globals'; + +const path = require('path'); + +describe('Given multiple sfpPackages, packageManager should be', () => { + it('able to merge into a single package', async () => { + const set = new ComponentSet(); + set.add({ fullName: 'MyClass', type: 'ApexClass' }); + set.add({ fullName: 'MyLayout', type: 'Layout' }); + + const componentSetMock = jest.spyOn(ComponentSet, 'fromSource'); + componentSetMock.mockImplementation(() => { + return set; + }); + // TODO: Complete along with PackageMergeManager Feature + // let artifacts = ArtifactFetcher.fetchArtifacts(path.join(__dirname, 'artifacts1'), undefined, undefined); + // let sfpPackages: SfpPackage[] = []; + + // for (const artifact of artifacts) { + // let sfpPackage = await SfpPackageBuilder.buildPackageFromArtifact(artifact, new ConsoleLogger()); + // sfpPackages.push(sfpPackage); + // } + + // let packageMerger = new PackageMergeManager(sfpPackages); + // let mergeResult = await packageMerger.mergePackages(); + + // expect(mergeResult.mergedPackages.length).toBeGreaterThanOrEqual(2); + // expect(mergeResult.mergedPackage.apexTestClassses?.length).toBeGreaterThanOrEqual(7); + // expect(mergeResult.mergedPackage.isApexFound).toBeTruthy(); + + + }); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/package/packageMerger/artifacts1/core2_sfpowerscripts_artifact_1.0.4-1.zip b/packages/sfpowerscripts-cli/tests/core/package/packageMerger/artifacts1/core2_sfpowerscripts_artifact_1.0.4-1.zip new file mode 100644 index 0000000000000000000000000000000000000000..b5436df6dd5882319d3577edc14132a05275fe3f GIT binary patch literal 8405 zcmeHLc{mi>8y*bV2FXqcNtQt*OVP;A*s@)OF-FEV%vhsE#LbPscOQjDOzu`_A`$=e*}N)TIEj007j~07HPVX@3$B zyo($Fcmf0fWB~vG9EC>gv&A@ipnMT%3>=O0@WR-_&|XMK7~D&8`G>6=!VBgA^MZk$ zF(~(wCQww@VW#p)F@bE&ckCUw(zL=U<037{XmVC_9&h!HV&DBr`VgbV03Q&QYcx84 zs;A6Y(?tDC!PU7CGuK|c1HqB&T{#gv&A6sg-|Zat^=h7%FuLAjvEkp z8XbrFGnH>~ew;o~>*SYism#xLxw8XynH${~)FU=Nj+6B%y;h9{ zu52pht}Uve$*oPY&=(VK@j{;a^p(>1c+Y=IFZGiSjx{%2(Q*L=cZ|CUtu{dh0Equ% z%fX#s?#B_XsN-K+?LwrAOSKf!{n&3S`i>| zXa8=>OIIU&$DhCOwTkj|3ad2K&Im8x?lmd#(n826D5r8UTr{)~%_aFtTLg3Uj>?r( z@Nm2s;wt;%j>JP;lO`f1OjvVlHI4IcrUgAYWI~_W@9jEWQ=!IS9@#&^qo*^cTt0Gv z_wufG&Rg1O^U2q4sf*$oijA`mWyDh|*+zKTEY3`Xo_`5cQW$WBfdZo_>FBD>7?u0? zwdZ3xllR=ZVF30KvW$k7%;g_E1d(?`WiPNVZxZ0kToU%>+uBX|weehHP~K=bLUI{m zX|$UW2dg{?dz?lXZ|TiKfE`f;B;;{-!a0y&gm>qbW*V7i1-8;CGkX{V-(YL@(&K5! zOAbm6_Uv@4$irWrnin1y;%2xg%o;0$zdeX8`}Dr()Fq|N8(6lF*Y=$~*b|-*I8}pw zPp0K~xk=EX>2>!sV@P21wV|{G_TS$Z9*(v+`W|^DyCVoas+n2%>?7!$haeQ42(zz_ z=^9H1zdb6L3QK$f0Y5m$V5uZuJW6R8g*33Li+N~-ch%-*2xY+~N2fF-)V1ddIq6s&ot-up;Za&zejw=aBBL54K>Y)ibW zd-ciStMIo6$OBZ(9Hm?9s$>isGg?Y4y;Sd=MhzN-$fUTW1Y#{o(B6-ESGO>2mJGxeC+@@%H0up zd{rZum38gcCNzR91puJ7*+%$^VgsTAN1$UBgKsYW9VzdgXiZF=2%Ibp( zbUT$G+Ek(B(WXVlZT(u{{Bv;4SGvinfwS=(;a%?~gVU}ho*pl+>4q@#_Ges&1P@4W z@9frMvy*Db^K7{09Hjxe5?^$?oJysogl)Ja{hqFqWiY1{P@S^&1?-k;Uqo}*M1cHn zyR<2BZimQ@{%)dY{lrTyFIWg#BNoRhhCY__VfITfzg|dm~*PejNxu^FUa8o*&*?Ej`c(4;cE_ z@%~ef_qEkLvbAb@dL!X3zsB>Y-`8sk3fWpgePFIg!mH}neE!t=Tr0mMH(O3Om^;!D zf$>^>4eLno$u8zkp9M+RY(}W`5 z_^4FFsn5zEs{9f@KWv_9ShyrB6VK1|s9uD>FbN3L$cN}0DQFq2VI|K|8s;oaHom5? zNUdlXHq0LgwWc>L^9yNX;L^9G&Fr_f?mY2nL2^t{0Yob)Zkyf*f7Ma^K{z6MA~jVU zHQ62^ITJR2v+K2xyP4AinCNte1Et+tS$-QdUhVm#yqt01G=yTN`1(xnO?*O4FZ5Bw zc>@!&aT{*?LX8VZNWZ8YOvI(~Tl60(PopnAx~m)fXaJ*&TN)YI4(h-Zp0QXwr| zN@B&EvwC*|249C?Eu?m8fpz$}w9nh~>rrzA%d;u_GuU^=ybc-fa%s0LDlQg#C_1WG zu!ysQ%iWiJNzHQUb6%n#opp{O9#nt>f$O`A=oMHxr1AO!b$7$!uTV>dPIRfOJLcP) zE5}sq>2+coF=!XPornQG9Bbz@xYEQ_%}Yb_(DGStF-n@`PxX`m4e5R;&E!Wa zZKgRFdKR@C=cwvS-(AR67`8jx0r85BdDYVD>(2UEYEJ$cmxTVw)GOvuC#+P>pta=W z0=IU{slQ0J=VcBs;E3bFT+hhN;@7$aI($-fTi5P+`m`!_rTO0M&W_xo(!krGp6F`_ zK4XriJ`jG&_;O)X#UQ=TC!KBPNWaGR*de<0w}0=Z_Y4jHw)Ja|kWGtu3;~At*q4B# z-8R5G@FQV;!*hcn-8TSL**Yl7p7=~8oqKC{K=a>&L*Q_fx4YK{8R-9aB#uags{<*a zrAuD?Wp*TxFiHJ{kK2FF^))|zZ#R1c+SV2h{t|-((QF0#xw#^u8r`e;S=441X=^HK zwfV9{^-N3)9$zpYQxup})VSaGE+hJ`9gqR=5i*80`7YEMM_xU#- zsuMpl(F_(boh0kPGYLD{hV@g=craZSHxsb(sQO<^orVl{iVxb*r7LTF(Bfg+@kYg5 z9>W4-W`Uw;u9>7-;S)E9$tLgt;}8_HH*N!jIgj`xofKf;s!AjZ~`I{xVayz~^ zB#5Tu^1=mmZx-@+OKqcEh}Xx` zBIXjgZIijIa)wrB^nEFa-q7;G1@&$T|9hv1l*`hfe$RDynTYU>3(6));!SnZid?`e zoQ#dwh;QrVg$weh+Y;OAm-a@4kYzT)H!esAv?aooFF_LB+PNgYO_mn1GdLMHNk@8I zkieHpwfe2H437(P{BiKVeYubr*QP^MyGx6h&Xw((Ooya>SN8a7^}7s?3+mrA^lB}< zQTRl)xU`7jv+mez_+PZ+a_DP!W##x={|GE`w!Uh~_3?>nk`(sME#ZIFmg|!c)g_69 zjBrJ6YVKDE;x8m5t+_rKQEigQZ2mnNNqeqON>raD((3@uuw^Iy_kY+So!tNc literal 0 HcmV?d00001 diff --git a/packages/sfpowerscripts-cli/tests/core/package/packageMerger/artifacts1/feature-mgmt3_sfpowerscripts_artifact_1.0.6-1.zip b/packages/sfpowerscripts-cli/tests/core/package/packageMerger/artifacts1/feature-mgmt3_sfpowerscripts_artifact_1.0.6-1.zip new file mode 100644 index 0000000000000000000000000000000000000000..6adbcf2731d852b7133e9e771c7a5152ba9df920 GIT binary patch literal 35211 zcmeHQcRbeZ_qVgj%BJkS$=-WpQ$qG0*)ngNtgMh(vO>tpC^J+>NOtzh%E&CgTe=g{ z^YnaE{qg&}UcGK9pYy)X^*-l1*SXGh$V)>)VSs_b!h*?zk*nk_p~5IYfq|7$f`RdX zfq@ws=-AoY7|>Z5S=g~?+ZtM1IU3m5>e-lB+u3UC*w~pE>gd@qfWFYSFtF3n*Rj)~ zH?_60oWG)B)z6OY*7gwAhxb_oLxJLYuI;RjfIo~y2@i&^fKpV87#e4U=_uyrB8mMY zQfVwqZU8jtrB-qG+qM!x{!#9Ui7)uaD0J~7+nqctui~By41sV$QEh?~^i=lt_YAQVBfvw$|-{8 z1BP#?>PI=E9z7@WQZ3i)Jf(-#Wg6P=uxXFeVxiFDHMFO zjo&(;&8LSFK%|M44v{Uc)_8#7xrcD7Z#8L>Mm{T>p0=j(PamAo>!Fi;?4c3rT;HHg z6`A!yjT8!5ZO89ca*wDC1r%H%@z%t`%Db3HsY@TgI0B$!A58QW&yly4)Uu(aX-=9! zEv|c0uJ5p<@WIPddRA`+&Cg}P5l>usYE)X*&y=&ap+(fxRN{w%!Q1Lqy_vV&nfLDe zC9k46Uli3)^s-u+4DT@kx1vBJXMAN(Bb`f=QxGI?-RxHh&J#f_tteV3KMb`F$8C+C z*g@|J&!m~<5ixyvm*-|M`P+<$`)KzmAL<6$sX9n=vNEb6mnz~Eo?l@G+ za%_6ddcL21h-?uu&_sk2uTh>VbvMhj@Y7_3B*M;=cQuqW$1-O)KMPJnX(4U5PRwO2 zqnN54wE@n+fIgGe#Q_HGai3ce?B}?%Vr|4qc#|q0WhhZG8DYsx=bJLcwokdRDXJ;r zCXH8-h>cuKNKhTI-l~i`kAEj(Nba^Kw!i#}YpUCG8I!wQQDw-DHe)NJ$XuX{q6G&7WBu2z zO3zrw(#XKv%ILF;(hT88sU^m!Uw4PE&6Wk*W-t$3uzWUB+-^$SfD!O2{jH{eg^X4C zz~EvPm7tar7#WjzS@g9*N~PCS6?OfWOzbn&#rhLUp60)j(#(C|LIoq(Ir@qG%-QJ( zDTS!Z9~y3lfZ-9b)T&D@M=6V6xh}*>#U5oX)D3pg<&~bm@M~|WRIEIX;rSa`X-w@6 z?_AEKrU>%26U5&^Ig71dgopPy-IOi~yhfTw+)-|l12lZF&z>4M;;#=K@T-64sco(7 zZS)KnK(E?&`t1fhwbno_-iH7KZS?R%8I|>&AC zlgQd6>*&@+_bp`5zf&T)r<2?%NdM9o`3m>Nl6h$PTPBw@8lzhjD$FJEko_>yQX}p* zCpLEHUC4j+rr(9Bw%2d=+JM6Y-+Mi93c+q{ESa{5UhT-?p^%vQ?#m90HF&sE;5Jw+-@mJHT>mqDg*9G{-kLj?EA z#=A2(y02bsbR{IqAmzA73|%mPa%-sdZBMY-tKB&vHOEh8>YBZ&XWn=_L4v07I3xbO zL409^o|UDciO~^as8;r|N$!~!Bp8^?&l2Oy8?9}uObztx=zu|kj-8${oxXwLXVVLb z;J2z}L~U^76CLM7AmA31gz&*$Kx5SWQ;)@?u!SKH-!>#dzXus&r3q5S@41@1t`m0?X+Xh_a>FCr-{X+vC+)kS|6zGNDvD(%o{|v?MRo(QJ0;I0a5e;^m*i`KTv&GEzvina6t|HrNw5z>PmvVq&qlbOq zKNiL-Z3%eY<1&v!tIfVaRzYGtb<0M>9~Mb#?Afp_HO39{p0{0D(brzw@5kSm>CJwg zYf!fE#722o+WO|qCr5&MdyWm5lS#n!vs3nsDcc^Q!nZy`?07M;>wT(ib{;l?z?E!bo8CUKKeAdX-tGzcqZZj-! zaU#Jp&}wH4>rM5PY>|gXyuWvk)@r3Yl;MN_W=`KUsims%OC9R99)0L*?^uboY$`+s z+wyZ4q1FT;Ka?bW@Xo17tbZ%p7P^=hNwny_fku3HGol_+f8`9^qMejegaC1Md3qC^ZXs?z6+VJrPqVtZf2 z;&VpU?Tw!HS?n|GzEWILLeoM$&sF#-O+$~pfngFBBXT!CnFQf#u6zYjVHy&BQ-3ic zCq@rbh1`Y4r~dIVund0h`$R;*?_i3(HkdL$P|L=mTT*+ zRt>SBR(+((#Fod`7T0im_3)B!hQWKRz;A$gTX0um*I#E&l%W^*U(3s0PkHGq7p%IL zJ%o31_{jFNWc-pBJQ6vY$_P6Q0$(Ktq$A%?zw#&nWy4zb$mgRCy*bc_>>aVViTBYL+~iBsth)AFMRlf^wyiRJ@Oi;0a}q zqqcUJ>79hv+B!QbCkg3i7mIT!*c|cO-#m8ie$k&0+IV|G@MQvuZwTAPwzntG1=CL} z&PMO+JRJ8k|30<*WqW7~e4U|WLG z(F3L~ejrQfm&>xyF|quCK#kK8sAsNYYin>YdHYd?hP(?d-JWRz_xL`iD_Gdx*3Qa8 z_RAT8*#6Oil7XF_iKUUPyp5HEiN1jiy`H(Py`r?GHzVqxZO~;jWnV}Ug?p;mQKI~& z$exIVXlf;bAKIz2ZPxn&WdYUZ0`6h!IFUw|A#mm{_B{`X5xF-4jbjfp;wd6TMCTei zsbS43Kp}RmJ+*J>Ri&H-?u0sajVGq>C{$Y_*_o#om|6ok-IIzmsYIcp^}At&>KoBY zP|hpUD5R1jNJqVe8W?|aCt31o+a|=4mb;Fqe3Hv}2vsl|v1vP!;GkW+H!oY_xX!s0 zZYq}tF*xZdPb3jB9RO9s??lDOM=Ex<5O3mg_>pvMjL*ywbVA~wpa|vED(<}0l;h>P z=?K=(x!l*gP)wFiBjcjN@0dH7!!EhIFneEGuKyj3ALmCz_R1N+i>JEv?~Nktn0o5) z86{WBh8mv+Z+fItX6qzt_(?CH0XdER>9l30@eTw|<3r$|kiTR)>GqC==$$OgWkYJ< zfjJ+M#m%7bie7bPk&dVf8_=_ElJ{WxNbTNqw3W)Aono)UOR|_uy{&{zHL3g{mtD$l z-1c1OBh}J(2DZwD*K142UQQ7Pj@w8mKR9R02`>Na?#wN5qM|lPc+_J#C& zB-adOLat>*6kSgk4QnSmb1jP+P+tp1VXc%AA`wX~Nl`y2DIExgQbN1Cn{1#0r<}L;);%x3#f=JN~0bo6HeOQ?pC><;$G_-gw2im^HDguCGDoai8VzyOsC1Gi&1(`^O0&t9qcpz+8T(awILR%?&IJ zEbVmcOsp*TJXW-lpye1NV&yg_4;SY}6j*qu7E1tO3FVbJNQ}5V-)J)7yBf4uo7+-_ z`Wj;RDLfm*wtflcoqHA$dg`*oUW80b8bkBv>v)r*YBCfEi_-GEgUJ+i5)$(kyja?_ zoAQ48EfyW)us7MDpntuo7-E1XDTIk}xD#Hrx!2TqQUh7HgfUmDKyYzuVqEJjFYgx zvZbG4_1bfI?#0;v*JZU~6w%kj2`)0Xtfh@?)t{8{-F9)w8M`FX#aynC;WY&lrS-{V zbr`k?yfvz09GUVuEpFx`0#-pKFcuS~PxClW%LZX4__@w4%JmMK8oPFT1a&p%4kVc~ z5f;ZZxt`;1Oj}M2zSCN`WK+Kxlp{84=xPO>jgp4HyYXhF4PjbO!Rf}^hVAu{)u?xc zC@C;hx81e^=%F0LI+NeYM58g6P-_R9IvAQrUg?c*fQE_%G}Pa-1bhjd_e`@GXqv!?xpF0_N>E;e5LMLlO0gn+Zr3X0v&#WWdL(+d zcd0!dA|clZFhtJ^UvM@uGJ5ucIutTsWW*CwC8tXUz9*)qnmY!b1|z=6kr@(#LBcK< z^+P5b`eT!;&<2S%VgMUO6%Nk_b&g<@IiqJix>L0z0WmeY_%F0*`Z>UIyq!buL}@D6 z(B6pS8$_z-X-_QtNL1kwaknEo{R*|(q7Iot8g@3G$_{7V#f&Cs4B3zLRyZ415pLh- z-D=9#j~#i2^0ID|6{c@!GV|l`-B)Fy6MbYV)E`7yKU|lqFk;hS6CR|6JR_sN))crA zY>4YekcdFmM!bZ`-O5Da`e3=Qgogv8|J+TUO5PD;8p^XmrV?irx!G57vDWJov|ae< zC!DPM9>{7)GH2!VaYCB4E#Wu7h_q}H1&6v7z~?Y@n8Htm7~A6@-H^T{!Pc~@KOnP4 zCRW?(3LzpN43qGIk`;lN*yc`tKB;#Wxv^{*>RJ%^on5y}-SDqp=1hU7l~as939#lo zUpKivwdnq^=17S7ZPfI8mbh=o@wZz}SK|Yz3gFTf478r#s+_}{&afI}uf5I5#~@DX zb9~hZ?}rsa+yw#9rYU6e-9Fg6no^YDXzPWNMq7sLvSKSL4QsRV}m)wJA5(StLz_gek%W-d&hDzwS1*-Rj6kQQ1p9V^ysV zxPj_Aw+2aM=atp|PH@TW{aD?b>ZgH~*0s{KO^)UkX9UHfJoH{(XmfG!B?oAE1-Z?x zKh6JSlk7jn;bClcUeB{H??&pn^2(Ph*YIPyj;IrDv^j%5XMOZd8 zVIfVLv}80tNO{WKbFqYziFRo+?bMO@VEB?Ia}yaaJl1=&8M6|Sx8OM$WxMk0*^%>{ z;+;bSR1;)UuJ8KZMM2GV5kFSSmtIe{n##*&pMnF{>C}H#eI*-kG)Yt_>mSZp;K689v@{UYwYLtE#Z(8g0UPkI`p?RTZWK zZ=h{AJT#W#wp39{I@T^zj9kwZofdbIif6CO(3l_%VUZxbT{bAb2oX}3sBY9zrJr$? zqu(0QG(mTMfmI7SdANMc3DGja$d~X6P4^9sbt7LW7nwzE^%lhy-7F|1doI#ZNp|Y1 z;?p0f8{xE?W<8RdwtrAVbV^oXrMp)TbdZKTZjPsvRsae9j#Ye3#_n%OBmHM=hk&Me z8EB>vr(>c&0`nb!^!xw+t9@F8x5(8qBKj=4!P7X#d!Y(Z-r*&lhl4^}0<)Z5z+KLG zmODJ(+`+r*8OHMVr3Cx-;;>F?mUR%f{h$LpWjU)g&q7z@_Q0L>= z6hqzKtVjv>_Hxpf7C4>bnf6rEck(Ja2Xm20FQ%jvB6YDqVsc!MDOZNx4(5D=DM3OU z?8AHsD#G)A^{sP3)#qK{F`(Px9!<%OKk;nj54i9l3FaP0!)>ZCUT2rGs3*51G?{Ni ztC4Oy3+=8FsRs|0(>%2vN#cDG=9cKv);b;1gT2H7TjpFmf>Vo90w6*=RHve&)8tcNC%$TD=>NMeP#1$ zAFsA9CTXxohr~{Z47DPq7~ZeHFIhnX&yYazu$I!obxx4Bg@2+%;8w zhb2%GYkgzzeBq#;3bpDX(Nxi7l%#aSASI5|;Dfn&UR(*iD-NY8wsS`fI#_sY7>SYg z=7z^)RaTBoX16B?2U_LbDMV|HzUIcf>esnWK5p!1i_&6Wq~Fdjw1X%1=kxNYz-(!q zW7)x|gI}KMVZ+nNP#psks>-YsY~M_WR}R zJtTPV%;z6Ye3XHj+1tbV-l{RJV?^{@bcP=st)4~*7U;d!E-Y;V&utdijhmpaZX-qM zyxwdQVd2Z17)<(PcXwjVFRWgdy;3}^_124qGtVO!thh0}I?l6H!o<+QsZwr;QPwC4 zUMS&wKUA8S+a!*76}|n++mtbv$^4WawDNA%>M8cJS%DxEhnmNZG*h_-sspsTg@Kq4 zP#=&%OMLbScJDhvPYy2ZJ9|HLGq8%9Wxo+)fy3gEm zew3p7QL1=VWuC&dO=wNZ15g<&D%t-;YQZLc4y11w3>et2-{c&k@R#NIyNM|f zcUx3yEP;nPz?zs!bh`Lq$ac~qlgNwtvXCZI?YK=6=uv4YrlW6f7fJRbJ`?EE;ZzCv zX#RA4Ae6#1RH_kuO^Q?pu{SXWn*5b&2f-^?DiWz0bTmx~S9QH|LC)qp+@aBhBs4FYZ#Q@;Z`K)l zBU)FSCF**I!6+KBv2;~S4! z!@;`HduWFhBxsB~djcm-jWQQM*1Q;nRMM+1d|ZHUuj5Hi6GksuQ`IJ*YF&-+%qv1; z1+KXw2HsM$8--UrKSiCwv4@9yR*3zYFPE#WcHiv#g2oEGYhlT0Yk9)cHzrGHI#MDm zu1TqqMWks))D`nRZo;IvYMnakGcTD{nfg%e`6t-cENccR;o4NqqMLA~?Tz&WcfY#m z<3|$#r)!-5hGr*NYLTaFC>~LPSKNPrcb(^9|za)q1 zg(4tPL__^PzX(lv+q_obuKiqBlC?s^(>HQL`H&J8#aC2Jr^s#@whpvP&dm(uw9B({ zXg{75N{J1pXIjAI>bDh=fZ~ARBgs%4LknbnYzw>b3ev0-SG!7^*zckN+?+}Ycws9A zS%_@Be7Zgchk%iE2RokmyK#gh5*g2ljC5SQ(px%ij4iehXeb0mU0w;!kIw+=><7X8 zUkBF_L?GN8Wv3@x&#Ov8)Zr@vWKxy0F4Z{5!!v<(m&9Z%nhQ{SMJ=A~4k!%LL3z~j zy(pg9oA~5bc@ZAjK z9`wDxG_9~(v1Zu`O>g96XX7fSx6x|p6iX?|6G^VSQi&RS&knKI2JW_;shNh1_2%=@ z8d-X6RF>RmKXL{Hp^v_mfqb+k54xVk`CfR!g}Io4QKHPr_c*K^?UTnVJCVq^+1>h@ zf@L+S$rm3)Xrzy8Ug}^wZ^#%6PK=2`^YcK z#gk)>(%srw=Hq{nezv{(3B`ifvH~ZF!jf8)U9Gz2(&1TfvBm$STv1EMrP&8$=nHe!N?wa*^x^70=%k;O{W|&g4 z7Lbr~TWbN>&$uEt=@P?QO@!_sb2FT=SYV>u-Y~F`etjAGw#NLj9a`iD1ZabI+?WA> zP?LYJP4DX|vAMJ&52zmzAmbc=m!3o|j}xwCuUgD!(K(3Ml))K{ftu3k{V2M5e=f|j zIJ=<8)SFMpOf}Cxr^3IT zX<6(xtnfgVT?EFAzjPP(b@lk{%|VX!Z~^LZ##zX}kn;uBovvU$62V#m=ip$%wX!)^ z{vNVP9>&-lVW!9uan};zKH8dj{L47bFm@Fc0q8K^f_$6nHtg0Uc0*_bYj-6gua|`k z`XKY5L)KWM%ED$)TUVHK)%Abxfq2(jKN&DQt~0bL<`X&Xsb?wP%NS9 zDLkyPZh3$x{ zmtL`Rl9u-lb*-BuVWxsNXBliR zEkhGbClZ6Gkd1(`=|U#zFi))XxFtGFC%B1GLEtz*G0g)0-`j@Go(*n7NM8;bZr`@0@(* z9@fW*=su;U>_(*1$ttTf1BczIGD;3Lw;5I9Cw9ADbJS+_EkjY`mAnBcG-HkXd|hvi zwr%G~kro`JZfG#7rVwr|ZbQC>OuR?Ht{6S^$GTd{aV&BsI0D&qYYF58s zE9iJ@aDD_GVx%4`rQ20l#GOk34G5*zg!z4T2@zFsD=?-{YOzQsuRXDaF705kPvjJS zp}e9lw6zg?^}_hIh1Eq|H(n)Y{VAB{kWcO{yEiVa=N55Zx@LKe&f(2v8R`2z)@#8G z?eHs@p|=M3kkyes^qmbw@$%TVQ;)Hfre6Hu4|Pf5#d0u~9HKRc%ouaIaf)U6Ir?yZ zTcZK*3hjY6m}VOu;!D0;-H0X6*VHqHC4pz|6fSac>~&LFHWB1Fq?ec$kb5fx#;h6G zz`AhS3w-1_v)0wnyXS$tK+zDvb*sdMfG zqS&jV5B~#bj>0>7>TVF;A_c_++>xwc$S%lG#X*x=?O zZg>lPTV3TNW>lZs887shSta}idw$sJ>5jQE9gJk6&ev6*i3?qN4-#tcd=zTT->K&c zBC&N3FC>rLF2B^Ev#1!(QD;rzuYnWZV%`HgSYu=QY&NK^l9kTO{jNSzjgOy(U~CgU zbx{yY56jY7om`(c2$Kw(_{=)hO_S&DkNdfpT+q9<8j~}go$H>iideNHtwL7Xjmx(= zn@iDV^l^yRIb&RGHflibUd7jP-{W*R#hu@YbolOBJAD!a7AlYtQ-6pRUqF2ctB#+t zhuqq~lKQz8W7ZQ@sL3A1H;8apxlHwreuujGT+@*9e19Z+6#w<=Qi3*jZnHa^PYjBM zW#~69J8-tJKx_Lw-<+p)xy-;|$--8O{%AVV$6=;A8uyB8yg`VBV}?C^m#h+jZlDRy z6}RXq4z8!&bUxH*&*wyt8oPK`Sc>yelHkRPd!xiWg6?Gq$@0FSxsIZNPJ#VVn7dyQ zN0UO>up&FUo=^%m6PMJ@$CNS)VyTWgLqWVrt+AXkj)7>ricL)C1iubo4BUV33J zgfANX-cz^O#LBP7o5v}2Itjbahjk~rwbKbTJ_`lyD?!5dy%?!&Hh(?PdpQ94>Ut_( z?2z#9ChU~IoT*!c3GU@kMAtbR%1j|fq0>@QpgAtgLW^;m5~oMRQHxeqo36a5gpR zuy((#r1t&8+WmVY?tLZ_&aB()6;@jUndtJnbai<9`IU&5r1yw8IEC@Z459-eAO*qK zQltw(W8BxRV5F*7JDt3kJ+TDSD%MQ~XCU6!F#@u?=d`ZxMrgZ*dVYnsoqQ?vUhy3E=wDWc{F(dZuOAI}(fkmu9f_(Yf8a@EZi7mK=+F{pC-{h*F%Y+sGoZWQyQKvJBJ zF`=f$E6q(T1vegCS^L^x>-e_sm)?l^nhW`kb$?%{e>?qXh?FK?0cjTwq@VZc)9**w zJrE7u!UThW6>&g+DB6;Zj$P>q>lNINAliixm7V~n>?oamH^9lUV!ns#QS4Lr=&gsJ zl4A}n0nyuZmuaj_* zLI8tpZlnteWx12|gPEzt8vf->9$Iu;n9H}B64`NuKO*^mG7H44feLDXCY&m~hmErB z48hmrQr4~9x%qadJEY~^oM=?GLVw)dKAnM?F_aq^q~3EHQtO*9lzD7dU+b((sUzOZ zdw89N!}bTazk~k{jsNW=&E?H~Q41uf4X}{tH>@W=PUC=RId&=Q&*-tnXJlj;X)`#9(l&c7 zzPiVxpI6TjC06`K0|UY!9LEwDOKmXnA?qUt_R=zT{1SF9&sqcB{nxlO4dug zKGf0`D3la426hl=yUZqo4R}p5wjR83EK=g^F7lMwKI5$CG>A#&|D^h=_90O0;VzY-~=io=;8@d_t|1a zr0I%s6wbEoT)z(f zK#^q*nTyIi7KVQy6=|HhFd{3WYsDDgNxVQgfdP94r%|wxz#U;NwJlR=x|nK0?_OR( ztbUE(s>hBEwaHWS{L0|u4`cmVZpB^_=pd?4ZYK8OzVdN5S5dBJSph0%XE zmapA=4}<@USo_&mf7{LXMl=4S{B|%bwpJ{*ul>bTa;iicP zVvpN5$5*j?46;4@lVT70?;Qx)pUR_c{XlZk08Rg0$akCk9Y{VtyuHS7`_JC+z<~bx zr-*ydCho9p9BmW_5|Rc``e_kI+s6H9I7}cxpS^J~=Fbl2bEh@XP>ySBuh-iCv!|{8 z|ML288khZOAO~#Ulf;Jg9|rP`q;KaGn5RE^fP1~z4hER;E5&~4%yzQuy?Z(bvSH8t z!XbZ_{iPW2WB_~jb`AhIpALY7JR=CmzB@SDyA1>-4X}K=Kn`!U#ZK2 zF7PuAP=j+Sa|cr0zefBuDn1JRXqPY$dSH}=d#cdCsCTah^C+folc9UJWQsRo4j~SgO#Qf1 z$-xl*Nta->r!oXkF~g659IVZIk-78f0{M9)9|1V*8ZChP_HW5{@~@}-w?zF60JLE^0J3i}M|&n5%;x0j z0@>TS{L2msM=>95EpUL@mgQ8H_Y;`EvncK>{OAq;1MpeE?DHwk{14E977AHA*(0-grozxX>)AGy5!Bc<+Lz6Cu%rE>oPc;8Xu@sMNh z4($H3w-_-2lYhT#^1r-j??xYsG#2|@_`Oan*={xOgzs`TjnM-bpNfR)gxqdlAw{W;YhUgNMt{y5kZ zReba^-hqlmMSm0OlL|5ZiNAZd%0N#VfJXc;fcqUaA76tGyZ@`eGv`zCWhbREj$ZaU zP-5@tgAv=`Jpa_yuY>jLpR{QUJWD?%ZGyJwKLWFNM`-`qTiKURAIwp0eiQucuS!Sn z41w^a0o;MVviOu$bqw9nZ~ROi!x04ibLh4!`}@(KkjOZC0SAOL4G;x9`~BT2{-^6W zKces9=vYzyG|>NJ8so5gY&<;>C#5ouUQjuhPZ5Pv(+CL6cjHb_N#eoW?Hl*E+iG7B zOyFl4;4W~Z_3N2;Ca{)q~251E)(0&W#!=c>2(e2@5y9+9R6zGYn zJ$j$uK((@}r;hkX(el4h@m@JT=t%=S1$v@W=EA=Le4?t4R>dD6UsnH3$WMxz|BbTu pYOFy|8bA%0IQ}il{wK)as2du1-C*xm^U8qD08fL0ukQWr{{TM{Zeaib literal 0 HcmV?d00001 diff --git a/packages/sfpowerscripts-cli/tests/core/package/propertyFetchers/AssignPermissionSetFetcher.test.ts b/packages/sfpowerscripts-cli/tests/core/package/propertyFetchers/AssignPermissionSetFetcher.test.ts new file mode 100644 index 000000000..dd2c050fe --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/propertyFetchers/AssignPermissionSetFetcher.test.ts @@ -0,0 +1,55 @@ +import { jest, expect } from '@jest/globals'; +import { Logger } from '@flxblio/sfp-logger'; +import AssignPermissionSetFetcher from '../../../../src/core/package/propertyFetchers/AssignPermissionSetFetcher'; +import PropertyFetcher from '../../../../src/core/package/propertyFetchers/PropertyFetcher'; +import SfpPackage from '../../../../src/core/package/SfpPackage'; +import SfpPackageBuilder from '../../../../src/core/package/SfpPackageBuilder'; + +jest.mock('../../../../src/core/package/SfpPackageBuilder', () => { + class SfpPackageBuilder { + + public assignPermSetsPreDeployment?: string[]; + public assignPermSetsPostDeployment?: string[]; + + public static async buildPackageFromProjectDirectory( + logger: Logger, + projectDirectory: string, + sfdx_package: string + ) { + let propertyFetchers: PropertyFetcher[] = [new AssignPermissionSetFetcher()]; + + let sfpPackage: SfpPackage = new SfpPackage(); + sfpPackage.packageDescriptor = packageDescriptor; + for (const propertyFetcher of propertyFetchers) { + await propertyFetcher.getsfpProperties(sfpPackage, logger); + } + return sfpPackage; + } + } + + return SfpPackageBuilder; +}); + +describe('Given a package descriptor with assignPermSetsPreDeployment or assignPermSetsPostDeployment', () => { + it('Should set assignPermSetsPreDeployment property in SfpPackage', async () => { + let assignPermissionSetFetcher: AssignPermissionSetFetcher = new AssignPermissionSetFetcher(); + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory(null, null, null); + assignPermissionSetFetcher.getsfpProperties(sfpPackage); + expect(sfpPackage.assignPermSetsPreDeployment).toStrictEqual(['PermSetB']); + }); + + it('Should set assignPermSetsPostDeployment property in SfpPackage', async () => { + let assignPermissionSetFetcher: AssignPermissionSetFetcher = new AssignPermissionSetFetcher(); + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory(null, null, null); + assignPermissionSetFetcher.getsfpProperties(sfpPackage); + expect(sfpPackage.assignPermSetsPostDeployment).toStrictEqual(['PermSetA']); + }); +}); + +const packageDescriptor: any = { + path: 'force-app', + package: 'force-app', + versionNumber: '1.0.0.NEXT', + assignPermSetsPostDeployment: ['PermSetA'], + assignPermSetsPreDeployment: ['PermSetB'], +}; diff --git a/packages/sfpowerscripts-cli/tests/core/package/propertyFetchers/DestructiveManifestPathFetcher.test.ts b/packages/sfpowerscripts-cli/tests/core/package/propertyFetchers/DestructiveManifestPathFetcher.test.ts new file mode 100644 index 000000000..9f8d33dd4 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/propertyFetchers/DestructiveManifestPathFetcher.test.ts @@ -0,0 +1,85 @@ +import { jest, expect } from '@jest/globals'; +import DestructiveManifestPathFetcher from '../../../../src/core/package/propertyFetchers/DestructiveManifestPathFetcher'; +import SfpPackage from '../../../../src/core/package/SfpPackage'; +const fs = require('fs-extra'); +import { Logger } from '@flxblio/sfp-logger'; +import PropertyFetcher from '../../../../src/core/package/propertyFetchers/PropertyFetcher'; +import SfpPackageBuilder from '../../../../src/core/package/SfpPackageBuilder'; + + +jest.mock('../../../../src/core/package/SfpPackageBuilder', () => { + class SfpPackageBuilder { + + public assignPermSetsPreDeployment?: string[]; + public assignPermSetsPostDeployment?: string[]; + + public static async buildPackageFromProjectDirectory( + logger: Logger, + projectDirectory: string, + sfdx_package: string + ) { + let propertyFetchers: PropertyFetcher[] = [new DestructiveManifestPathFetcher()]; + + let sfpPackage: SfpPackage = new SfpPackage(); + sfpPackage.packageDescriptor = packageDescriptor; + for (const propertyFetcher of propertyFetchers) { + await propertyFetcher.getsfpProperties(sfpPackage, logger); + } + + return sfpPackage; + } + } + + return SfpPackageBuilder; +}); + + +describe('Given a package descriptor with a destructiveChangePath', () => { + beforeEach(() => { + jest.spyOn(fs, 'readFileSync').mockImplementation(() => { + return destructiveChangesXml; + }); + }); + + it('Should set destructiveChangesPath property in SfpPackage', async () => { + let destructiveManifestPathFetcher: DestructiveManifestPathFetcher = new DestructiveManifestPathFetcher(); + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory(null, null, null); + await destructiveManifestPathFetcher.getsfpProperties(sfpPackage); + expect(sfpPackage.destructiveChangesPath).toBe('destructiveChanges.xml'); + }); + + it('Should set destructiveChanges property in SfpPackage', async () => { + let destructiveManifestPathFetcher: DestructiveManifestPathFetcher = new DestructiveManifestPathFetcher(); + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory(null, null, null); + await destructiveManifestPathFetcher.getsfpProperties(sfpPackage); + expect(sfpPackage.destructiveChanges).toEqual(destructiveChanges); + }); +}); + +const packageDescriptor: any = { + path: 'force-app', + package: 'force-app', + versionNumber: '1.0.0.NEXT', + destructiveChangePath: 'destructiveChanges.xml', +}; + +const destructiveChangesXml: string = ` + + + + MyCustomObject__c + CustomObject + + +`; +const destructiveChanges: any = { + Package: { + $: { + xmlns: 'http://soap.sforce.com/2006/04/metadata', + }, + types: { + members: 'MyCustomObject__c', + name: 'CustomObject', + }, + }, +}; diff --git a/packages/sfpowerscripts-cli/tests/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.test.ts b/packages/sfpowerscripts-cli/tests/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.test.ts new file mode 100644 index 000000000..d7b7a5c22 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/package/propertyFetchers/ReconcileProfilePropertyFetcher.test.ts @@ -0,0 +1,48 @@ +import { jest, expect } from '@jest/globals'; +import { Logger } from '@flxblio/sfp-logger'; +import PropertyFetcher from '../../../../src/core/package/propertyFetchers/PropertyFetcher'; +import ReconcileProfilePropertyFetcher from '../../../../src/core/package/propertyFetchers/ReconcileProfilePropertyFetcher'; +import SfpPackage from '../../../../src/core/package/SfpPackage'; +import SfpPackageBuilder from '../../../../src/core/package/SfpPackageBuilder'; + +jest.mock('../../../../src/core/package/SfpPackageBuilder', () => { + class SfpPackageBuilder { + + public assignPermSetsPreDeployment?: string[]; + public assignPermSetsPostDeployment?: string[]; + + public static async buildPackageFromProjectDirectory( + logger: Logger, + projectDirectory: string, + sfdx_package: string + ) { + let propertyFetchers: PropertyFetcher[] = [new ReconcileProfilePropertyFetcher()]; + + let sfpPackage: SfpPackage = new SfpPackage(); + sfpPackage.packageDescriptor = packageDescriptor; + for (const propertyFetcher of propertyFetchers) { + await propertyFetcher.getsfpProperties(sfpPackage, logger); + } + + return sfpPackage; + } + } + + return SfpPackageBuilder; +}); + +describe('Given a package descriptor with reconcileProfiles', () => { + it('Should set reconcileProfiles property in SfpPackage', async () => { + let reconcileProfilePropertyFetcher: ReconcileProfilePropertyFetcher = new ReconcileProfilePropertyFetcher(); + let sfpPackage: SfpPackage = await SfpPackageBuilder.buildPackageFromProjectDirectory(null, null, null); + reconcileProfilePropertyFetcher.getsfpProperties(sfpPackage); + expect(sfpPackage.reconcileProfiles).toBe(false); + }); +}); + +const packageDescriptor: any = { + path: 'force-app', + package: 'force-app', + versionNumber: '1.0.0.NEXT', + reconcileProfiles: false, +}; diff --git a/packages/sfpowerscripts-cli/tests/core/permsets/AssignPermissionSets.test.ts b/packages/sfpowerscripts-cli/tests/core/permsets/AssignPermissionSets.test.ts new file mode 100644 index 000000000..8c8660ca4 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/permsets/AssignPermissionSets.test.ts @@ -0,0 +1,198 @@ +const child_process = require('child_process'); +import AssignPermissionSetsImpl from '../../../src/core/permsets/AssignPermissionSetsImpl'; +import { jest, expect } from '@jest/globals'; +import { VoidLogger } from '@flxblio/sfp-logger'; +import { AuthInfo, Connection, OrgConfigProperties } from '@salesforce/core'; +import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup'; + +const $$ = new TestContext(); + +jest.mock('../../../src/core/permsets/PermissionSetFetcher', () => { + class PermissionSetFetcher { + constructor(private username: string, private conn: Connection) {} + fetchAllPermsetAssignment(): any[] { + return [ + { + attributes: { + type: 'PermissionSetAssignment', + url: '/services/data/v50.0/sobjects/PermissionSetAssignment/0Pa2s000000PC8fCAG', + }, + Id: '0Pa2s000000PC8fCAG', + PermissionSet: { + attributes: { + type: 'PermissionSet', + url: '/services/data/v50.0/sobjects/PermissionSet/0PS2s000000bldoGAA', + }, + Name: 'Salesforce_DX_Permissions', + }, + Assignee: { + attributes: { + type: 'User', + url: '/services/data/v50.0/sobjects/User/0052s000000kuInAAI', + }, + Username: 'test-sfvulqawd2w0@example.com', + }, + }, + { + attributes: { + type: 'PermissionSetAssignment', + url: '/services/data/v50.0/sobjects/PermissionSetAssignment/0Pa2s000000PC8aCAG', + }, + Id: '0Pa2s000000PC8aCAG', + PermissionSet: { + attributes: { + type: 'PermissionSet', + url: '/services/data/v50.0/sobjects/PermissionSet/0PS6F000004MA6gWAG', + }, + Name: 'X00ex00000018ozT_128_09_43_34_1', + }, + Assignee: { + attributes: { + type: 'User', + url: '/services/data/v50.0/sobjects/User/0052s000000kuInAAI', + }, + Username: 'test-sfvulqawd2w0@example.com', + }, + }, + ]; + } + } + return PermissionSetFetcher; +}); + +describe('Given a set of permsets, assign it to the user who is deploying the packages', () => { + it('should assign a set of permset, if its not previously assigned', async () => { + + const testData = new MockTestOrgData(); + await $$.stubConfig({ [OrgConfigProperties.TARGET_ORG]: testData.username }); + await $$.stubAuths(testData); + const connection: Connection = await Connection.create({ + authInfo: await AuthInfo.create({ username: testData.username }), + }); + + let assignPermSetImpl: AssignPermissionSetsImpl = new AssignPermissionSetsImpl( + connection, + ['test1', 'test2'], + null, + new VoidLogger() + ); + const child_processMock = jest.spyOn(child_process, 'execSync'); + child_processMock + .mockImplementationOnce(() => { + return Buffer.from(`{ + "status": 0, + "result": { + "successes": [{ + "name": "test-sfvulqawd2w0@example.com", + "message": "Succesfully applied the permsets" + }] + } + }`); + }) + .mockImplementationOnce(() => { + return Buffer.from(`{ + "status": 0, + "result": { + "successes": [{ + "name": "test-sfvulqawd2w0@example.com", + "message": "Succesfully applied the permsets" + }] + } + }`); + }); + + let results = await assignPermSetImpl.exec(); + expect(results.successfullAssignments).toHaveLength(2); + expect(results.failedAssignments).toHaveLength(0); + }); + + it('should assign a partial set of permset, if any of them fails', async () => { + const testData = new MockTestOrgData(); + await $$.stubConfig({ [OrgConfigProperties.TARGET_ORG]: testData.username }); + await $$.stubAuths(testData); + const connection: Connection = await Connection.create({ + authInfo: await AuthInfo.create({ username: testData.username }), + }); + + + let assignPermSetImpl: AssignPermissionSetsImpl = new AssignPermissionSetsImpl( + connection, + ['test1', 'test2'], + null, + new VoidLogger() + ); + const child_processMock = jest.spyOn(child_process, 'execSync'); + child_processMock + .mockImplementationOnce(() => { + return Buffer.from(`{ + "status": 1, + "result": { + "successes": [{ + "name": "test-sfvulqawd2w0@example.com", + "message": "Permset cannot be applied" + }] + } + }`); + }) + .mockImplementationOnce(() => { + return Buffer.from(`{ + "status": 0, + "result": { + "successes": [{ + "name": "test-sfvulqawd2w0@example.com", + "message": "Succesfully applied the permsets" + }] + } + }`); + }); + + let results = await assignPermSetImpl.exec(); + expect(results.successfullAssignments).toHaveLength(1); + expect(results.failedAssignments).toHaveLength(1); + }); + + it('should assign none, if all of them fails', async () => { + const testData = new MockTestOrgData(); + $$.setConfigStubContents('AuthInfoConfig', { + contents: await testData.getConfig(), + }); + const connection: Connection = await Connection.create({ + authInfo: await AuthInfo.create({ username: testData.username }), + }); + + let assignPermSetImpl: AssignPermissionSetsImpl = new AssignPermissionSetsImpl( + connection, + ['test1', 'test2'], + null, + new VoidLogger() + ); + const child_processMock = jest.spyOn(child_process, 'execSync'); + child_processMock + .mockImplementationOnce(() => { + return Buffer.from(`{ + "status": 1, + "result": { + "successes": [{ + "name": "test-sfvulqawd2w0@example.com", + "message": "Permset cannot be applied" + }] + } + }`); + }) + .mockImplementationOnce(() => { + return Buffer.from(`{ + "status": 1, + "result": { + "successes": [{ + "name": "test-sfvulqawd2w0@example.com", + "message": "Permset cannot be applied" + }] + } + }`); + }); + + let results = await assignPermSetImpl.exec(); + expect(results.successfullAssignments).toHaveLength(0); + expect(results.failedAssignments).toHaveLength(2); + }); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/permsets/PermissionSetFetcher.test.ts b/packages/sfpowerscripts-cli/tests/core/permsets/PermissionSetFetcher.test.ts new file mode 100644 index 000000000..cc8bffb51 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/permsets/PermissionSetFetcher.test.ts @@ -0,0 +1,115 @@ +import { expect } from '@jest/globals'; +import PermissionSetFetcher from '../../../src/core/permsets/PermissionSetFetcher'; +import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup'; +import { AnyJson } from '@salesforce/ts-types'; +import { AuthInfo, Connection, OrgConfigProperties } from '@salesforce/core'; +const $$ = new TestContext(); + +describe('Retrieve assigned permsets provided username and a target org', () => { + it('should return all the permsets for the provided username', async () => { + const testData = new MockTestOrgData(); + await $$.stubConfig({ [OrgConfigProperties.TARGET_ORG]: testData.username }); + await $$.stubAuths(testData); + + let records: AnyJson = { + records: [ + { + attributes: { + type: 'PermissionSetAssignment', + url: '/services/data/v50.0/sobjects/PermissionSetAssignment/0Pa2s000000PC8fCAG', + }, + Id: '0Pa2s000000PC8fCAG', + PermissionSet: { + attributes: { + type: 'PermissionSet', + url: '/services/data/v50.0/sobjects/PermissionSet/0PS2s000000bldoGAA', + }, + Name: 'Salesforce_DX_Permissions', + }, + Assignee: { + attributes: { + type: 'User', + url: '/services/data/v50.0/sobjects/User/0052s000000kuInAAI', + }, + Username: testData.username, + }, + }, + { + attributes: { + type: 'PermissionSetAssignment', + url: '/services/data/v50.0/sobjects/PermissionSetAssignment/0Pa2s000000PC8aCAG', + }, + Id: '0Pa2s000000PC8aCAG', + PermissionSet: { + attributes: { + type: 'PermissionSet', + url: '/services/data/v50.0/sobjects/PermissionSet/0PS6F000004MA6gWAG', + }, + Name: 'X00ex00000018ozT_128_09_43_34_1', + }, + Assignee: { + attributes: { + type: 'User', + url: '/services/data/v50.0/sobjects/User/0052s000000kuInAAI', + }, + Username: testData.username, + }, + }, + ], + }; + $$.fakeConnectionRequest = (request: AnyJson): Promise => { + return Promise.resolve(records); + }; + const connection: Connection = await Connection.create({ + authInfo: await AuthInfo.create({ username: testData.username }), + }); + + let permsetListImpl: PermissionSetFetcher = new PermissionSetFetcher(testData.username, connection); + let permsetRecords = await permsetListImpl.fetchAllPermsetAssignment(); + expect(permsetRecords).toHaveLength(2); + }); + + it('should return an empty array, if no permsets are assigned', async () => { + const testData = new MockTestOrgData(); + await $$.stubConfig({ [OrgConfigProperties.TARGET_ORG]: testData.username }); + await $$.stubAuths(testData); + + let records: AnyJson = { records: [] }; + $$.fakeConnectionRequest = (request: AnyJson): Promise => { + return Promise.resolve(records); + }; + + const connection: Connection = await Connection.create({ + authInfo: await AuthInfo.create({ username: testData.username }), + }); + + let permsetListImpl: PermissionSetFetcher = new PermissionSetFetcher(testData.username, connection); + let permsetRecords = await permsetListImpl.fetchAllPermsetAssignment(); + expect(permsetRecords).toHaveLength(0); + }); + + it('should throw an error, if unable to query permsets', async () => { + const testData = new MockTestOrgData(); + + $$.setConfigStubContents('AuthInfoConfig', { + contents: await testData.getConfig(), + }); + + let records: AnyJson = { records: [] }; + $$.fakeConnectionRequest = (request: AnyJson): Promise => { + throw new Error('Unable to fetch records'); + }; + + const connection: Connection = await Connection.create({ + authInfo: await AuthInfo.create({ username: testData.username }), + }); + + let permsetListImpl: PermissionSetFetcher = new PermissionSetFetcher(testData.username, connection); + + try { + await permsetListImpl.fetchAllPermsetAssignment(); + } catch (error) { + expect(error).toBeDefined(); + } + }, 500000); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/permsets/PermissionSetGroupUpdateAwaiter.test.ts b/packages/sfpowerscripts-cli/tests/core/permsets/PermissionSetGroupUpdateAwaiter.test.ts new file mode 100644 index 000000000..6afcb47cd --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/permsets/PermissionSetGroupUpdateAwaiter.test.ts @@ -0,0 +1,35 @@ +import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup'; +import { AuthInfo, Connection, OrgConfigProperties } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +const $$ = new TestContext(); +import PermissionSetGroupUpdateAwaiter from '../../../src/core/permsets/PermissionSetGroupUpdateAwaiter'; +import { expect } from '@jest/globals'; + +describe('Await till permissionsets groups are updated', () => { + it('should return if all permsets groups are updated', async () => { + const testData = new MockTestOrgData(); + + await $$.stubConfig({ [OrgConfigProperties.TARGET_ORG]: testData.username }); + await $$.stubAuths(testData); + $$.setConfigStubContents('AuthInfoConfig', { + contents: await testData.getConfig(), + }); + + let records: AnyJson = { + records: [], + }; + $$.fakeConnectionRequest = (request: AnyJson): Promise => { + return Promise.resolve(records); + }; + + const connection: Connection = await Connection.create({ + authInfo: await AuthInfo.create({ username: testData.username }), + }); + + let permissionSetGroupUpdateAwaiter: PermissionSetGroupUpdateAwaiter = new PermissionSetGroupUpdateAwaiter( + connection, + null + ); + await expect(permissionSetGroupUpdateAwaiter.waitTillAllPermissionSetGroupIsUpdated()).resolves.toBeUndefined(); + }); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/project/ProjectConfig.test.ts b/packages/sfpowerscripts-cli/tests/core/project/ProjectConfig.test.ts new file mode 100644 index 000000000..2bb7616a2 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/project/ProjectConfig.test.ts @@ -0,0 +1,278 @@ +const fs = require("fs-extra"); +import { jest, expect } from '@jest/globals'; +import { PackageType } from '../../../src/core/package/SfpPackage'; +import ProjectConfig from '../../../src/core/project/ProjectConfig'; + +describe('Given a project directory or sfdx-project.json with multiple packages', () => { + //given the below sfdx-project.json + let sfdx_project = { + packageDirectories: [ + { + path: 'packages/temp', + default: true, + package: 'temp', + versionName: 'temp', + versionNumber: '1.0.0.0', + ignoreOnStage: ['prepare', 'validate', 'build'], + }, + { + path: 'packages/domains/core', + package: 'core', + default: false, + versionName: 'core', + versionNumber: '1.0.0.0', + }, + { + path: 'packages/frameworks/mass-dataload', + package: 'mass-dataload', + default: false, + type: 'data', + versionName: 'mass-dataload', + versionNumber: '1.0.0.0', + }, + { + path: 'packages/access-mgmt', + package: 'access-mgmt', + default: false, + versionName: 'access-mgmt', + versionNumber: '1.0.0.0', + reconcileProfiles: 'true', + }, + { + path: 'packages/bi', + package: 'bi', + default: false, + versionName: 'bi', + versionNumber: '1.0.0.0', + ignoreOnStage: ['prepare', 'validate'], + }, + ], + namespace: '', + sfdcLoginUrl: 'https://login.salesforce.com', + sourceApiVersion: '50.0', + packageAliases: { bi: '0x002232323232' }, + }; + + beforeEach(() => { + const fsextraMock = jest.spyOn(fs, 'readFileSync'); + fsextraMock.mockImplementation((path: any, options: string | { encoding?: string; flag?: string }) => { + return JSON.stringify(sfdx_project); + }); + }); + + it('Get the package id of an unlocked package', () => { + expect(ProjectConfig.getPackageId(sfdx_project, 'bi')).toBe('0x002232323232'); + }); + + it('Throws an error, if the package id is missing in PackageAlias', () => { + expect(() => { + ProjectConfig.getPackageId(sfdx_project, 'bi2'); + }).toThrowError('No Package Id found in sfdx-project.json. Please ensure package alias have the package added'); + }); + + it('Fetches all the package', () => { + const manifestHelperMock = jest.spyOn(ProjectConfig, 'getSFDXProjectConfig'); + manifestHelperMock.mockImplementation((projectDirectory: string) => { + return sfdx_project; + }); + expect(ProjectConfig.getAllPackages(null)).toStrictEqual([ + 'temp', + 'core', + 'mass-dataload', + 'access-mgmt', + 'bi', + ]); + }); + + it('Fetches all the package from a project config', () => { + + expect(ProjectConfig.getAllPackagesFromProjectConfig(sfdx_project)).toStrictEqual([ + 'temp', + 'core', + 'mass-dataload', + 'access-mgmt', + 'bi', + ]); + }); + + it('Get manifest, provided a directory', () => { + expect(ProjectConfig.getSFDXProjectConfig(null)).toStrictEqual(sfdx_project); + }); + + it('Gets the type of a package', () => { + expect(ProjectConfig.getPackageType(sfdx_project, 'bi')).toBe(PackageType.Unlocked); + expect(ProjectConfig.getPackageType(sfdx_project, 'core')).toBe(PackageType.Source); + expect(ProjectConfig.getPackageType(sfdx_project, 'mass-dataload')).toBe(PackageType.Data); + }); + + + + + it('Gets the package descriptor of a provided package,provided directory', () => { + let corePackage = { + path: 'packages/domains/core', + package: 'core', + default: false, + versionName: 'core', + versionNumber: '1.0.0.0', + }; + expect(ProjectConfig.getSFDXPackageDescriptor(null, 'core')).toStrictEqual(corePackage); + }); + + it('Gets the package descriptor of a provided package', () => { + let corePackage = { + path: 'packages/domains/core', + package: 'core', + default: false, + versionName: 'core', + versionNumber: '1.0.0.0', + }; + expect(ProjectConfig.getPackageDescriptorFromConfig('core', sfdx_project)).toStrictEqual(corePackage); + }); + + it('Gets the default package, provided directory', () => { + let defaultPackage = { + path: 'packages/temp', + default: true, + package: 'temp', + versionName: 'temp', + versionNumber: '1.0.0.0', + ignoreOnStage: ['prepare', 'validate', 'build'], + }; + + expect(ProjectConfig.getDefaultSFDXPackageDescriptor(null)).toStrictEqual(defaultPackage); + }); + + it('Cleans any other package, than the one provided', () => { + let cleaned_sfdx_project = { + packageDirectories: [ + { + path: 'packages/temp', + default: true, + package: 'temp', + versionName: 'temp', + versionNumber: '1.0.0.0', + ignoreOnStage: ['prepare', 'validate', 'build'], + }, + ], + namespace: '', + sfdcLoginUrl: 'https://login.salesforce.com', + sourceApiVersion: '50.0', + packageAliases: { bi: '0x002232323232' }, + }; + + expect(ProjectConfig.cleanupMPDFromProjectDirectory(null, 'temp')).toStrictEqual(cleaned_sfdx_project); + }); + + it(`Gets all the external dependencies of a project`,()=>{ + let sfdx_project = { + packageDirectories: [ + { + path: 'packages/temp', + default: true, + package: 'temp', + versionName: 'temp', + versionNumber: '1.0.0.0', + ignoreOnStage: ['prepare', 'validate', 'build'], + }, + { + path: 'packages/domains/core', + package: 'core', + default: false, + versionName: 'core', + versionNumber: '1.0.0.0', + }, + { + path: 'packages/frameworks/mass-dataload', + package: 'mass-dataload', + default: false, + type: 'data', + versionName: 'mass-dataload', + versionNumber: '1.0.0.0', + }, + { + path: 'packages/access-mgmt', + package: 'access-mgmt', + default: false, + versionName: 'access-mgmt', + versionNumber: '1.0.0.0', + reconcileProfiles: 'true', + }, + { + path: 'packages/bi', + package: 'bi', + default: false, + versionName: 'bi', + versionNumber: '1.0.0.0', + ignoreOnStage: ['prepare', 'validate'], + }, + ], + namespace: '', + sfdcLoginUrl: 'https://login.salesforce.com', + sourceApiVersion: '50.0', + packageAliases: { bi: '0H432322321',bi2:'0H43232232' }, + }; + + + + expect(ProjectConfig.getAllExternalPackages(sfdx_project)).toEqual([{ + alias:'bi2', + Package2IdOrSubscriberPackageVersionId:"0H43232232" + }]) + + }); + + it(`Returns empty if there are no external dependencies`,()=>{ + let sfdx_project = { + packageDirectories: [ + { + path: 'packages/temp', + default: true, + package: 'temp', + versionName: 'temp', + versionNumber: '1.0.0.0', + ignoreOnStage: ['prepare', 'validate', 'build'], + }, + { + path: 'packages/domains/core', + package: 'core', + default: false, + versionName: 'core', + versionNumber: '1.0.0.0', + }, + { + path: 'packages/frameworks/mass-dataload', + package: 'mass-dataload', + default: false, + type: 'data', + versionName: 'mass-dataload', + versionNumber: '1.0.0.0', + }, + { + path: 'packages/access-mgmt', + package: 'access-mgmt', + default: false, + versionName: 'access-mgmt', + versionNumber: '1.0.0.0', + reconcileProfiles: 'true', + }, + { + path: 'packages/bi', + package: 'bi', + default: false, + versionName: 'bi', + versionNumber: '1.0.0.0', + ignoreOnStage: ['prepare', 'validate'], + }, + ], + namespace: '', + sfdcLoginUrl: 'https://login.salesforce.com', + sourceApiVersion: '50.0', + packageAliases: { bi: '0H432322321' }, + }; + + expect(ProjectConfig.getAllExternalPackages(sfdx_project)).toEqual([]) + + }); + +}); diff --git a/packages/sfpowerscripts-cli/tests/core/queryHelper/ChunkCollection.test.ts b/packages/sfpowerscripts-cli/tests/core/queryHelper/ChunkCollection.test.ts new file mode 100644 index 000000000..85c598341 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/queryHelper/ChunkCollection.test.ts @@ -0,0 +1,30 @@ +import { expect } from '@jest/globals'; +import chunkCollection from '../../../src/core/queryHelper/ChunkCollection'; + +describe('Given a collection', () => { + + it('should return a single chunk for a collection less than 3000 chars', () => { + const collection = ["ApexClassA", "ApexClassB", "ApexClassC"]; + const result = chunkCollection(collection); + expect(result.length).toBe(1); + expect(result).toEqual([ + ["ApexClassA", "ApexClassB", "ApexClassC"] + ]); + }); + + + it('should return N chunks for a collection exceeding the chunk size', () => { + const collection = ["ApexClassA", "ApexClassB", "ApexClassC", "ApexClassD"]; + const result = chunkCollection(collection, 1050, 1000); + expect(result.length).toBe(2); + expect(result).toEqual([ + ["ApexClassA", "ApexClassB", "ApexClassC"], + ["ApexClassD"] + ]); + }); + + it('should throw an error if single element in collection exceeds chunk size', () => { + const collection = ["ApexClassWithAnExceedinglyLongNameGreaterThanTheChunkSize"]; + expect(() => {chunkCollection(collection, 1050, 1000)}).toThrow(); + }); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/utils/ChunkArray.test.ts b/packages/sfpowerscripts-cli/tests/core/utils/ChunkArray.test.ts new file mode 100644 index 000000000..15d92cd24 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/utils/ChunkArray.test.ts @@ -0,0 +1,24 @@ +import { expect } from '@jest/globals'; +import { chunkArray } from '../../../src/core/utils/ChunkArray'; + +describe('Given an input array', () => { + const input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + it('should chunk for even chunk size', () => { + const result = chunkArray(2, input); + expect(result.length).toBe(5); + expect(result).toEqual([ + [1, 2], + [3, 4], + [5, 6], + [7, 8], + [9, 10], + ]); + }); + + it('should chunk for odd chunk size', () => { + const result = chunkArray(3, input); + expect(result.length).toBe(4); + expect(result).toEqual([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]); + }); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/utils/FileSystem.test.ts b/packages/sfpowerscripts-cli/tests/core/utils/FileSystem.test.ts new file mode 100644 index 000000000..8686ef38c --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/utils/FileSystem.test.ts @@ -0,0 +1,35 @@ +import { expect } from '@jest/globals'; +import FileSystem from '../../../src/core/utils/FileSystem'; +const path = require('path'); + +describe('Given a search directory', () => { + it('should return nested files', () => { + const resourcesDir = path.join(__dirname, 'resources'); + let files = FileSystem.readdirRecursive(path.join(resourcesDir, 'a'), false, false); + expect(files).toEqual(expectedFiles); + + files = FileSystem.readdirRecursive(path.join(resourcesDir, 'a'), true, false); + expect(files).toEqual(expectedFilesIncludingDirs); + + files = FileSystem.readdirRecursive(path.join(resourcesDir, 'a'), false, true); + expect(files).toEqual(expectedFiles.map((elem) => path.join(resourcesDir, 'a', elem))); + + files = FileSystem.readdirRecursive(path.join(resourcesDir, 'a'), true, true); + expect(files).toEqual(expectedFilesIncludingDirs.map((elem) => path.join(resourcesDir, 'a', elem))); + }); +}); + +const expectedFiles = ['b/b1.file', 'b/c/c1.file', 'b/c/c2.file', 'b/d/d1.file', 'b/d/x/x1.file', 'b/e/e1.file']; +const expectedFilesIncludingDirs = [ + 'b', + 'b/b1.file', + 'b/c', + 'b/c/c1.file', + 'b/c/c2.file', + 'b/d', + 'b/d/d1.file', + 'b/d/x', + 'b/d/x/x1.file', + 'b/e', + 'b/e/e1.file', +]; diff --git a/packages/sfpowerscripts-cli/tests/core/utils/extractDomainFromUrl.test.ts b/packages/sfpowerscripts-cli/tests/core/utils/extractDomainFromUrl.test.ts new file mode 100644 index 000000000..a0fa39e73 --- /dev/null +++ b/packages/sfpowerscripts-cli/tests/core/utils/extractDomainFromUrl.test.ts @@ -0,0 +1,34 @@ +import { expect } from '@jest/globals'; +import extractDomainFromUrl from '../../../src/core/utils/extractDomainFromUrl'; + +describe('Given a URL', () => { + it('should extract the domain name for https', () => { + expect(extractDomainFromUrl('https://force-power-8147.cs115.my.salesforce.com')).toBe( + 'force-power-8147.cs115.my.salesforce.com' + ); + }); + + it('should extract the domain name for http', () => { + expect(extractDomainFromUrl('https://force-power-8147.cs115.my.salesforce.com')).toBe( + 'force-power-8147.cs115.my.salesforce.com' + ); + }); + + it('should extract only the domain name', () => { + expect( + extractDomainFromUrl( + 'https://company.lightning.force.com/lightning/o/Account/list?filterName=00B4Y000000VyMDUA0' + ) + ).toBe('company.lightning.force.com'); + }); + + it('should return null for protocol other than http/s', () => { + expect(extractDomainFromUrl('ftp://ftp.example.com/files/fileA')).toBe(null); + }); + + it('should return input for falsy values', () => { + expect(extractDomainFromUrl('')).toBe(''); + expect(extractDomainFromUrl(undefined)).toBe(undefined); + expect(extractDomainFromUrl(null)).toBe(null); + }); +}); diff --git a/packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/b1.file b/packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/b1.file new file mode 100644 index 000000000..e69de29bb diff --git a/packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/c/c1.file b/packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/c/c1.file new file mode 100644 index 000000000..e69de29bb diff --git a/packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/c/c2.file b/packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/c/c2.file new file mode 100644 index 000000000..e69de29bb diff --git a/packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/d/d1.file b/packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/d/d1.file new file mode 100644 index 000000000..e69de29bb diff --git a/packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/d/x/x1.file b/packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/d/x/x1.file new file mode 100644 index 000000000..e69de29bb diff --git a/packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/e/e1.file b/packages/sfpowerscripts-cli/tests/core/utils/resources/a/b/e/e1.file new file mode 100644 index 000000000..e69de29bb diff --git a/packages/sfpowerscripts-cli/tests/impl/changelog/CommitUpdater.test.ts b/packages/sfpowerscripts-cli/tests/impl/changelog/CommitUpdater.test.ts index 854d187e7..ce1ba2f70 100644 --- a/packages/sfpowerscripts-cli/tests/impl/changelog/CommitUpdater.test.ts +++ b/packages/sfpowerscripts-cli/tests/impl/changelog/CommitUpdater.test.ts @@ -1,7 +1,7 @@ import { expect } from '@jest/globals'; import CommitUpdater from '../../../src/impl/changelog/CommitUpdater'; import ReadPackageChangelog from '../../../src/impl/changelog/ReadPackageChangelog'; -import { Changelog as PackageChangelog } from '@dxatscale/sfpowerscripts.core/lib/changelog/interfaces/GenericChangelogInterfaces'; +import { Changelog as PackageChangelog } from '../../../src/core/changelog/interfaces/GenericChangelogInterfaces'; import { Release } from '../../../src/impl/changelog/ReleaseChangelog'; const path = require('path'); import * as fs from 'fs-extra'; diff --git a/packages/sfpowerscripts-cli/tests/impl/changelog/OrgUpdater.test.ts b/packages/sfpowerscripts-cli/tests/impl/changelog/OrgUpdater.test.ts index b34314cbb..712f4a477 100644 --- a/packages/sfpowerscripts-cli/tests/impl/changelog/OrgUpdater.test.ts +++ b/packages/sfpowerscripts-cli/tests/impl/changelog/OrgUpdater.test.ts @@ -204,7 +204,7 @@ const referenceReleaseChangelog: ReleaseChangelog = { author: 'Azlam', message: 'Add persist credential to PR (#6)', body: - '* Add persist credential to PR\r\n\r\n* switch to alpha\r\n\r\n* Increment versions\r\n\r\n* Revert "Increment versions"\r\n\r\nThis reverts commit 39a68617d8e92de46604b883b6681e8022d2e403.\r\n\r\n* Test abs path\r\n\r\n* Revert "Test abs path"\r\n\r\nThis reverts commit 5ab30a8b52eddf16025acf6aec57d1bb9262549d.\r\n\r\n* Cleanup and switch to prod version of sfpowerscripts\r\n\r\nCo-authored-by: sfpowerscripts \r\nCo-authored-by: Alan Ly ', + '* Add persist credential to PR\r\n\r\n* switch to alpha\r\n\r\n* Increment versions\r\n\r\n* Revert "Increment versions"\r\n\r\nThis reverts commit 39a68617d8e92de46604b883b6681e8022d2e403.\r\n\r\n* Test abs path\r\n\r\n* Revert "Test abs path"\r\n\r\nThis reverts commit 5ab30a8b52eddf16025acf6aec57d1bb9262549d.\r\n\r\n* Cleanup and switch to prod version of sfp\r\n\r\nCo-authored-by: sfp \r\nCo-authored-by: Alan Ly ', }, ], }, diff --git a/packages/sfpowerscripts-cli/tests/impl/changelog/WorkItemUpdater.test.ts b/packages/sfpowerscripts-cli/tests/impl/changelog/WorkItemUpdater.test.ts index d4bba472e..c49c64934 100644 --- a/packages/sfpowerscripts-cli/tests/impl/changelog/WorkItemUpdater.test.ts +++ b/packages/sfpowerscripts-cli/tests/impl/changelog/WorkItemUpdater.test.ts @@ -36,7 +36,7 @@ describe('Given a WorkItemUpdater', () => { author: 'Azlam', message: 'NGV-626 Add persist credential to PR (#6)', body: - '* Add persist credential to PR\r\n\r\n* switch to alpha\r\n\r\n* Increment versions\r\n\r\n* Revert "Increment versions"\r\n\r\nThis reverts commit 39a68617d8e92de46604b883b6681e8022d2e403.\r\n\r\n* Test abs path\r\n\r\n* Revert "Test abs path"\r\n\r\nThis reverts commit 5ab30a8b52eddf16025acf6aec57d1bb9262549d.\r\n\r\n* Cleanup and switch to prod version of sfpowerscripts\r\n\r\nCo-authored-by: sfpowerscripts \r\nCo-authored-by: Alan Ly ', + '* Add persist credential to PR\r\n\r\n* switch to alpha\r\n\r\n* Increment versions\r\n\r\n* Revert "Increment versions"\r\n\r\nThis reverts commit 39a68617d8e92de46604b883b6681e8022d2e403.\r\n\r\n* Test abs path\r\n\r\n* Revert "Test abs path"\r\n\r\nThis reverts commit 5ab30a8b52eddf16025acf6aec57d1bb9262549d.\r\n\r\n* Cleanup and switch to prod version of sfp\r\n\r\nCo-authored-by: sfp \r\nCo-authored-by: Alan Ly ', }, { commitId: 'd7124579', diff --git a/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ESBaseCodeLWCChangelog.json b/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ESBaseCodeLWCChangelog.json index 0270de439..2fa66c2ed 100644 --- a/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ESBaseCodeLWCChangelog.json +++ b/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ESBaseCodeLWCChangelog.json @@ -7,7 +7,7 @@ "date": "2021-01-25T11:01:55+11:00", "author": "Azlam", "message": "Add persist credential to PR (#6)", - "body": "* Add persist credential to PR\r\n\r\n* switch to alpha\r\n\r\n* Increment versions\r\n\r\n* Revert \"Increment versions\"\r\n\r\nThis reverts commit 39a68617d8e92de46604b883b6681e8022d2e403.\r\n\r\n* Test abs path\r\n\r\n* Revert \"Test abs path\"\r\n\r\nThis reverts commit 5ab30a8b52eddf16025acf6aec57d1bb9262549d.\r\n\r\n* Cleanup and switch to prod version of sfpowerscripts\r\n\r\nCo-authored-by: sfpowerscripts \r\nCo-authored-by: Alan Ly " + "body": "* Add persist credential to PR\r\n\r\n* switch to alpha\r\n\r\n* Increment versions\r\n\r\n* Revert \"Increment versions\"\r\n\r\nThis reverts commit 39a68617d8e92de46604b883b6681e8022d2e403.\r\n\r\n* Test abs path\r\n\r\n* Revert \"Test abs path\"\r\n\r\nThis reverts commit 5ab30a8b52eddf16025acf6aec57d1bb9262549d.\r\n\r\n* Cleanup and switch to prod version of sfp\r\n\r\nCo-authored-by: sfp \r\nCo-authored-by: Alan Ly " }, { "commitId": "d7124579", diff --git a/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ExpectedResults/should_update_latestRelease_with_all_commits.json b/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ExpectedResults/should_update_latestRelease_with_all_commits.json index d88712474..070bcd232 100644 --- a/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ExpectedResults/should_update_latestRelease_with_all_commits.json +++ b/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ExpectedResults/should_update_latestRelease_with_all_commits.json @@ -14,7 +14,7 @@ "date": "2021-01-25T11:01:55+11:00", "author": "Azlam", "message": "Add persist credential to PR (#6)", - "body": "* Add persist credential to PR\r\n\r\n* switch to alpha\r\n\r\n* Increment versions\r\n\r\n* Revert \"Increment versions\"\r\n\r\nThis reverts commit 39a68617d8e92de46604b883b6681e8022d2e403.\r\n\r\n* Test abs path\r\n\r\n* Revert \"Test abs path\"\r\n\r\nThis reverts commit 5ab30a8b52eddf16025acf6aec57d1bb9262549d.\r\n\r\n* Cleanup and switch to prod version of sfpowerscripts\r\n\r\nCo-authored-by: sfpowerscripts \r\nCo-authored-by: Alan Ly " + "body": "* Add persist credential to PR\r\n\r\n* switch to alpha\r\n\r\n* Increment versions\r\n\r\n* Revert \"Increment versions\"\r\n\r\nThis reverts commit 39a68617d8e92de46604b883b6681e8022d2e403.\r\n\r\n* Test abs path\r\n\r\n* Revert \"Test abs path\"\r\n\r\nThis reverts commit 5ab30a8b52eddf16025acf6aec57d1bb9262549d.\r\n\r\n* Cleanup and switch to prod version of sfp\r\n\r\nCo-authored-by: sfp \r\nCo-authored-by: Alan Ly " }, { "commitId": "d7124579", diff --git a/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ExpectedResults/should_update_latestRelease_with_subset_of_commits.json b/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ExpectedResults/should_update_latestRelease_with_subset_of_commits.json index 3b3e51726..62ea88528 100644 --- a/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ExpectedResults/should_update_latestRelease_with_subset_of_commits.json +++ b/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ExpectedResults/should_update_latestRelease_with_subset_of_commits.json @@ -15,7 +15,7 @@ "date": "2021-01-25T11:01:55+11:00", "author": "Azlam", "message": "Add persist credential to PR (#6)", - "body": "* Add persist credential to PR\r\n\r\n* switch to alpha\r\n\r\n* Increment versions\r\n\r\n* Revert \"Increment versions\"\r\n\r\nThis reverts commit 39a68617d8e92de46604b883b6681e8022d2e403.\r\n\r\n* Test abs path\r\n\r\n* Revert \"Test abs path\"\r\n\r\nThis reverts commit 5ab30a8b52eddf16025acf6aec57d1bb9262549d.\r\n\r\n* Cleanup and switch to prod version of sfpowerscripts\r\n\r\nCo-authored-by: sfpowerscripts \r\nCo-authored-by: Alan Ly " + "body": "* Add persist credential to PR\r\n\r\n* switch to alpha\r\n\r\n* Increment versions\r\n\r\n* Revert \"Increment versions\"\r\n\r\nThis reverts commit 39a68617d8e92de46604b883b6681e8022d2e403.\r\n\r\n* Test abs path\r\n\r\n* Revert \"Test abs path\"\r\n\r\nThis reverts commit 5ab30a8b52eddf16025acf6aec57d1bb9262549d.\r\n\r\n* Cleanup and switch to prod version of sfp\r\n\r\nCo-authored-by: sfp \r\nCo-authored-by: Alan Ly " }, { "commitId": "d7124579", diff --git a/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ExpectedResults/should_update_latestRelease_with_work_items.json b/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ExpectedResults/should_update_latestRelease_with_work_items.json index 32b27072b..0af6bd844 100644 --- a/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ExpectedResults/should_update_latestRelease_with_work_items.json +++ b/packages/sfpowerscripts-cli/tests/impl/changelog/resources/ExpectedResults/should_update_latestRelease_with_work_items.json @@ -19,7 +19,7 @@ "date": "2021-01-25T11:01:55+11:00", "author": "Azlam", "message": "NGV-626 Add persist credential to PR (#6)", - "body": "* Add persist credential to PR\r\n\r\n* switch to alpha\r\n\r\n* Increment versions\r\n\r\n* Revert \"Increment versions\"\r\n\r\nThis reverts commit 39a68617d8e92de46604b883b6681e8022d2e403.\r\n\r\n* Test abs path\r\n\r\n* Revert \"Test abs path\"\r\n\r\nThis reverts commit 5ab30a8b52eddf16025acf6aec57d1bb9262549d.\r\n\r\n* Cleanup and switch to prod version of sfpowerscripts\r\n\r\nCo-authored-by: sfpowerscripts \r\nCo-authored-by: Alan Ly " + "body": "* Add persist credential to PR\r\n\r\n* switch to alpha\r\n\r\n* Increment versions\r\n\r\n* Revert \"Increment versions\"\r\n\r\nThis reverts commit 39a68617d8e92de46604b883b6681e8022d2e403.\r\n\r\n* Test abs path\r\n\r\n* Revert \"Test abs path\"\r\n\r\nThis reverts commit 5ab30a8b52eddf16025acf6aec57d1bb9262549d.\r\n\r\n* Cleanup and switch to prod version of sfp\r\n\r\nCo-authored-by: sfp \r\nCo-authored-by: Alan Ly " }, { "commitId": "d7124579", diff --git a/packages/sfpowerscripts-cli/tests/impl/dependency/ShrinkImpl.test.ts b/packages/sfpowerscripts-cli/tests/impl/dependency/ShrinkImpl.test.ts index 9cb9834f6..7a6d6879a 100644 --- a/packages/sfpowerscripts-cli/tests/impl/dependency/ShrinkImpl.test.ts +++ b/packages/sfpowerscripts-cli/tests/impl/dependency/ShrinkImpl.test.ts @@ -23,7 +23,7 @@ const setupFakeConnection = async () => { return conn; } -jest.mock('../../../../core/src/git/Git', () => { +jest.mock('../../../src/core/git/Git', () => { class Git { static async initiateRepo() { @@ -34,7 +34,7 @@ jest.mock('../../../../core/src/git/Git', () => { return Git; }); -jest.mock('../../../../core/src/git/GitTags', () => { +jest.mock('../../../src/core/git/GitTags', () => { class GitTags { async listTagsOnBranch(): Promise { return gitTags; @@ -205,7 +205,7 @@ const projectConfig = { "sfdc-framework":"04t1000x00x00x" }, "plugins": { - "sfpowerscripts": { + "sfp": { "disableShrinkImpl": false, "externalDependencyMap": { "tech-framework@2.0.0.38": [ diff --git a/packages/sfpowerscripts-cli/tests/impl/parallelBuilder/BuildCollections.test.ts b/packages/sfpowerscripts-cli/tests/impl/parallelBuilder/BuildCollections.test.ts index 0fca1bad2..876858d10 100644 --- a/packages/sfpowerscripts-cli/tests/impl/parallelBuilder/BuildCollections.test.ts +++ b/packages/sfpowerscripts-cli/tests/impl/parallelBuilder/BuildCollections.test.ts @@ -2,7 +2,7 @@ import { jest, expect } from '@jest/globals'; import BuildCollections from '../../../src/impl/parallelBuilder/BuildCollections'; let packageManifest = null; -jest.mock('../../../../core/lib/project/ProjectConfig', () => { +jest.mock('../../../src/core/project/ProjectConfig', () => { class ProjectConfig { static getSFDXProjectConfig(projectDirectory: string) { return packageManifest;