diff --git a/README.md b/README.md index 0323a7234..7677b81bf 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![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) [![Build Status](https://dev.azure.com/dxatscale/sfpowerscripts/_apis/build/status/Release?branchName=develop)](https://dev.azure.com/dxatscale/sfpowerscripts/_build/latest?definitionId=40&branchName=develop) ![npm](https://img.shields.io/npm/v/@dxatscale/sfpowerscripts)![NPM](https://img.shields.io/npm/l/@dxatscale/sfpowerscripts) ![Visual Studio Marketplace Installs - Azure DevOps Extension](https://img.shields.io/visual-studio-marketplace/azure-devops/installs/total/AzlamSalam.sfpowerscripts?label=visualstudio%20marketplace%20installations) +

sfpowerscripts

diff --git a/packages/azpipelines/BuildTasks/CheckoutProjectFromArtifactTask/CheckoutProjectFromArtifact.ts b/packages/azpipelines/BuildTasks/CheckoutProjectFromArtifactTask/CheckoutProjectFromArtifact.ts index 32fe333f7..1e647f380 100644 --- a/packages/azpipelines/BuildTasks/CheckoutProjectFromArtifactTask/CheckoutProjectFromArtifact.ts +++ b/packages/azpipelines/BuildTasks/CheckoutProjectFromArtifactTask/CheckoutProjectFromArtifact.ts @@ -1,72 +1,75 @@ import tl = require("azure-pipelines-task-lib/task"); -var fs = require("fs-extra"); -const path = require("path"); import simplegit from "simple-git/promise"; import { isNullOrUndefined } from "util"; -var shell = require("shelljs"); -import ArtifactFilePathFetcher from "../Common/ArtifactFilePathFetcher"; +import ArtifactFilePathFetcher from "@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactFilePathFetcher"; import PackageMetadata from "@dxatscale/sfpowerscripts.core/lib/PackageMetadata"; +import ArtifactHelper from "../Common/ArtifactHelper"; + +const fs = require("fs-extra"); +const path = require("path"); async function run() { try { - - - let artifact_directory = tl.getVariable("system.artifactsDirectory"); - const artifact = tl.getInput("artifact", true); - const artifactProvider = tl.getInput("artifactProvider", true); let sfdx_package = tl.getInput("package", false); - let skip_on_missing_artifact: boolean = tl.getBoolInput("skip_on_missing_artifact",false); - + let artifactDir = tl.getInput("aritfactDir",false); + let skip_on_missing_artifact: boolean = tl.getBoolInput( + "skip_on_missing_artifact", + false + ); let version_control_provider: string; let token: string; let username: string; //Read Git User Endpoint - version_control_provider = tl.getInput("versionControlProvider", true); + version_control_provider = tl.getInput("versionControlProvider", true); - let connection: string; - let vcsAuthDetails = getVCSAuthDetails(version_control_provider, connection); - token=vcsAuthDetails.token; - username=vcsAuthDetails.username; + let connection: string; + let vcsAuthDetails = getVCSAuthDetails( + version_control_provider, + connection + ); + token = vcsAuthDetails.token; + username = vcsAuthDetails.username; - //Fetch Artifact + //Fetch Artifact let artifactFilePaths = ArtifactFilePathFetcher.fetchArtifactFilePaths( - artifact, - artifactProvider, + ArtifactHelper.getArtifactDirectory(artifactDir), sfdx_package ); - ArtifactFilePathFetcher.missingArtifactDecider( - artifactFilePaths[0].packageMetadataFilePath, - skip_on_missing_artifact + ArtifactHelper.skipTaskWhenArtifactIsMissing( + ArtifactFilePathFetcher.missingArtifactDecider( + artifactFilePaths[0].packageMetadataFilePath, + skip_on_missing_artifact + ) ); + //Read package metadata + let packageMetadataFromArtifact: PackageMetadata = JSON.parse( + fs.readFileSync(artifactFilePaths[0].packageMetadataFilePath, "utf8") + ); - //Read package metadata - let packageMetadataFromArtifact: PackageMetadata = JSON.parse(fs.readFileSync(artifactFilePaths[0].packageMetadataFilePath, "utf8")); - - - console.log("##[command]Package Metadata:"+JSON.stringify(packageMetadataFromArtifact,(key:string,value:any)=>{ - if(key=="payload") - return undefined; - else - return value; - })); - - + console.log( + "##[command]Package Metadata:" + + JSON.stringify( + packageMetadataFromArtifact, + (key: string, value: any) => { + if (key == "payload") return undefined; + else return value; + } + ) + ); //Create Location - //For Backward Compatibility, packageName could be null when upgraded - let local_source_directory = isNullOrUndefined(sfdx_package) - ? path.join(artifact_directory, artifact, "source") - : path.join(artifact_directory, artifact, sfdx_package, "source"); + let local_source_directory = path.join(ArtifactHelper.getArtifactDirectory(artifactDir), sfdx_package, "source"); - shell.mkdir("-p", local_source_directory); + fs.ensureDirSync(local_source_directory); - console.log(`Source Directory created at ${local_source_directory}`); + + console.log(`Source Directory created at ${local_source_directory}`); if ( packageMetadataFromArtifact.package_type === "source" || @@ -75,7 +78,9 @@ async function run() { //Strinp https const removeHttps = (input) => input.replace(/^https?:\/\//, ""); - let repository_url = removeHttps(packageMetadataFromArtifact.repository_url); + let repository_url = removeHttps( + packageMetadataFromArtifact.repository_url + ); const git = simplegit(local_source_directory); @@ -101,19 +106,22 @@ async function run() { if (version_control_provider == "hostedAgentGit") await git .silent(false) - .clone(packageMetadataFromArtifact.repository_url, local_source_directory); + .clone( + packageMetadataFromArtifact.repository_url, + local_source_directory + ); else await git.silent(false).clone(remote, local_source_directory); //Checkout the particular commit await git.checkout(packageMetadataFromArtifact.sourceVersion); - console.log(`Checked Out ${packageMetadataFromArtifact.sourceVersion} sucessfully`); + console.log( + `Checked Out ${packageMetadataFromArtifact.sourceVersion} sucessfully` + ); } else if (packageMetadataFromArtifact.package_type === "delta") { - let delta_artifact_location; - if(!isNullOrUndefined(artifactFilePaths[0].sourceDirectoryPath)) - { - delta_artifact_location=artifactFilePaths[0].sourceDirectoryPath; + if (!isNullOrUndefined(artifactFilePaths[0].sourceDirectoryPath)) { + delta_artifact_location = artifactFilePaths[0].sourceDirectoryPath; } tl.debug("Copying Files to a source directory"); @@ -131,12 +139,13 @@ async function run() { } catch (err) { tl.setResult(tl.TaskResult.Failed, err.message); } - } -function getVCSAuthDetails(version_control_provider: string, connection: string) { - - let token,username; +function getVCSAuthDetails( + version_control_provider: string, + connection: string +) { + let token, username; switch (version_control_provider) { case "github": connection = tl.getInput("github_connection", true); @@ -151,28 +160,26 @@ function getVCSAuthDetails(version_control_provider: string, connection: string) if (version_control_provider == "azureRepo") { token = tl.getVariable("system.accessToken"); - } - else if (version_control_provider == "github" || - version_control_provider == "githubEnterprise") { + } else if ( + version_control_provider == "github" || + version_control_provider == "githubEnterprise" + ) { token = tl.getEndpointAuthorizationParameter( connection, "AccessToken", true ); - } - else if (version_control_provider == "bitbucket") { + } else if (version_control_provider == "bitbucket") { token = tl.getEndpointAuthorizationParameter( connection, "AccessToken", true ); - } - else if (version_control_provider == "otherGit") { + } else if (version_control_provider == "otherGit") { username = tl.getInput("username", true); token = tl.getInput("password", true); } - return {token, username }; + return { token, username }; } - run(); diff --git a/packages/azpipelines/BuildTasks/CheckoutProjectFromArtifactTask/package.json b/packages/azpipelines/BuildTasks/CheckoutProjectFromArtifactTask/package.json index 0e02c4783..692797046 100644 --- a/packages/azpipelines/BuildTasks/CheckoutProjectFromArtifactTask/package.json +++ b/packages/azpipelines/BuildTasks/CheckoutProjectFromArtifactTask/package.json @@ -16,8 +16,6 @@ "@dxatscale/sfpowerscripts.core": "^2.0.0", "azure-pipelines-task-lib": "^2.8.0", "fs-extra": "^8.1.0", - "glob": "^7.1.6", - "shelljs": "^0.8.3", "simple-git": "2.0.0" } } diff --git a/packages/azpipelines/BuildTasks/CheckoutProjectFromArtifactTask/task.json b/packages/azpipelines/BuildTasks/CheckoutProjectFromArtifactTask/task.json index 014d3c386..590a12995 100644 --- a/packages/azpipelines/BuildTasks/CheckoutProjectFromArtifactTask/task.json +++ b/packages/azpipelines/BuildTasks/CheckoutProjectFromArtifactTask/task.json @@ -16,14 +16,7 @@ ], "instanceNameFormat": "Checkout $(artifact) from associated build pipeline", "inputs": [ - { - "name": "artifact", - "type": "string", - "label": "Name of the artifact (alias) attached to this pipeline that needs to be checked out", - "defaultValue": "_source", - "required": true, - "helpMarkDown": "Name of the artifact that is generated from the build pipeline for checking out source, Please note this is not a generic utility and will only work for artifacts created by sfpowerscripts" - }, + { "name": "package", "type": "string", @@ -33,17 +26,13 @@ "helpMarkDown": "Name of the package that generated this artifact, Leave blank to support artifacts generated by older version of Create Tasks" }, { - "name": "artifactProvider", - "type": "pickList", - "label": "Artifact Provider for the attached artifact", - "defaultValue": "BuildArtifact", - "options": { - "BuildArtifact": "Build Artifact", - "AzureArtifact": "Azure Artifact", - "PipelineArtifact":"Pipeline Artifact" - }, - "required": true, - "helpMarkDown": "Select the artifact provider for the artifact that is attached to the pipeline" + "name": "aritfactDir", + "type": "string", + "label": "Path to the directory where artifacts are downloaded", + "defaultValue": "", + "required": false, + "helpMarkDown": "Path to the artifact directory where the artifacts are downloaded, If not provided, the default values will be automatically used" + }, { "name": "versionControlProvider", diff --git a/packages/azpipelines/BuildTasks/Common/ArtifactFilePathFetcher.ts b/packages/azpipelines/BuildTasks/Common/ArtifactFilePathFetcher.ts deleted file mode 100644 index 9023fe7aa..000000000 --- a/packages/azpipelines/BuildTasks/Common/ArtifactFilePathFetcher.ts +++ /dev/null @@ -1,186 +0,0 @@ -import * as tl from "azure-pipelines-task-lib/task"; -import path = require("path"); -import fs = require("fs"); -const glob = require("glob"); - -export default class ArtifactFilePathFetcher { - - /** - * Decider for which artifact retrieval method to use - * - * @param artifactAlias - * @param artifactType - * @param sfdx_package - */ - public static fetchArtifactFilePaths( - artifactAlias: string, - artifactType: string, - sfdx_package?: string - ): ArtifactFilePaths[] { - let artifacts_filepaths: ArtifactFilePaths[]; - - if (artifactType === "Build" || - artifactType === "PackageManagement" || - artifactType === "AzureArtifact" || - artifactType === "BuildArtifact" - ) { - artifacts_filepaths = ArtifactFilePathFetcher.fetchArtifactFilePathFromBuildArtifact( - artifactAlias, - sfdx_package - ); - } - else if (artifactType === "PipelineArtifact") { - artifacts_filepaths = ArtifactFilePathFetcher.fetchArtifactFilePathFromPipelineArtifacts( - sfdx_package - ); - } - else { - console.log(`Unsupported artifact type ${artifactType}`); - console.log(`Defaulting to Build artifact...`); - - artifacts_filepaths = ArtifactFilePathFetcher.fetchArtifactFilePathFromBuildArtifact( - artifactAlias, - sfdx_package - ); - } - - return artifacts_filepaths; - } - - /** - * Helper method for retrieving the ArtifactFilePaths of packages - * contained in an artifact alias directory - * - * @param artifactAlias - * @param sfdx_package - */ - private static fetchArtifactFilePathFromBuildArtifact( - artifactAlias: string, - sfdx_package: string - ): ArtifactFilePaths[] { - const artifacts_filepaths: ArtifactFilePaths[] = []; - - let systemArtifactsDirectory = tl.getVariable("system.artifactsDirectory"); - - // Search artifact alias directory for files matching artifact_metadata.json - let packageMetadataFilepaths: string[] = glob.sync( - `**/artifact_metadata.json`, - { - cwd: path.join(systemArtifactsDirectory, artifactAlias), - absolute: true, - } - ); - - if (sfdx_package) { - // Filter and only return ArtifactFilePaths for sfdx_package - packageMetadataFilepaths = packageMetadataFilepaths.filter((filepath) => { - let artifactMetadata = JSON.parse(fs.readFileSync(filepath, "utf8")); - return artifactMetadata["package_name"] === sfdx_package; - }); - } - - for (let packageMetadataFilepath of packageMetadataFilepaths) { - let sourceDirectory = path.join( - path.dirname(packageMetadataFilepath), - `source` - ); - - let changelogFilepath = path.join( - path.dirname(packageMetadataFilepath), - `changelog.json` - ); - - artifacts_filepaths.push({ - packageMetadataFilePath: packageMetadataFilepath, - sourceDirectoryPath: sourceDirectory, - changelogFilePath: changelogFilepath - }); - } - - return artifacts_filepaths; - } - - /** - * Helper method for retrieving the ArtifactFilePaths of a pipeline artifact - * @param sfdx_package - */ - private static fetchArtifactFilePathFromPipelineArtifacts( - sfdx_package: string - ): ArtifactFilePaths[] { - const artifacts_filepaths: ArtifactFilePaths[] = []; - - let artifactDirectory = tl.getVariable("pipeline.workspace"); - - // Search entire pipeline workspace for files matching artifact_metadata.json - let packageMetadataFilepaths: string[] = glob.sync( - `**/artifact_metadata.json`, - { - cwd: artifactDirectory, - absolute: true - } - ); - - - if (sfdx_package) { - // Filter and only return ArtifactFilePaths for sfdx_package - packageMetadataFilepaths = packageMetadataFilepaths.filter((filepath) => { - let artifactMetadata = JSON.parse(fs.readFileSync(filepath, "utf8")); - return artifactMetadata["package_name"] === sfdx_package; - }); - } - - for (let packageMetadataFilepath of packageMetadataFilepaths) { - let sourceDirectory = path.join( - path.dirname(packageMetadataFilepath), - `source` - ); - - let changelogFilepath = path.join( - path.dirname(packageMetadataFilepath), - `changelog.json` - ); - - artifacts_filepaths.push({ - packageMetadataFilePath: packageMetadataFilepath, - sourceDirectoryPath: sourceDirectory, - changelogFilePath: changelogFilepath - }); - } - - return artifacts_filepaths; - } - - /** - * Decider for task outcome if the artifact cannot be found - * @param packageMetadataFilePath - * @param isToSkipOnMissingArtifact - */ - public static missingArtifactDecider( - packageMetadataFilePath: string, - isToSkipOnMissingArtifact: boolean - ): void { - if (!fs.existsSync(packageMetadataFilePath) && !isToSkipOnMissingArtifact) { - throw new Error( - `Artifact not found at ${packageMetadataFilePath}.. Please check the inputs` - ); - } else if ( - !fs.existsSync(packageMetadataFilePath) && - isToSkipOnMissingArtifact - ) { - console.log( - `Skipping task as artifact is missing, and 'Skip If no artifact is found' ${isToSkipOnMissingArtifact}` - ); - tl.setResult( - tl.TaskResult.Skipped, - `Skipping task as artifact is missing, and 'Skip If no artifact is found' ${isToSkipOnMissingArtifact}` - ); - process.exit(0); - } - } -} - -export interface ArtifactFilePaths { - packageMetadataFilePath: string; - sourceDirectoryPath?: string; - changelogFilePath?: string; -} diff --git a/packages/azpipelines/BuildTasks/Common/ArtifactHelper.ts b/packages/azpipelines/BuildTasks/Common/ArtifactHelper.ts new file mode 100644 index 000000000..45450e6d7 --- /dev/null +++ b/packages/azpipelines/BuildTasks/Common/ArtifactHelper.ts @@ -0,0 +1,35 @@ +import * as tl from 'azure-pipelines-task-lib/task'; +import { isNullOrUndefined } from 'util'; + +export default class ArtifactHelper +{ + + public static getArtifactDirectory(artifactDir:string):string + { + if(isNullOrUndefined(artifactDir)) + { + const taskType: string = tl.getVariable("Release.ReleaseId") ? "Release" : "Build"; + if(taskType=='Release') + return tl.getVariable("system.artifactsDirectory"); + else + return tl.getVariable("pipeline.workspace"); + } + else + return artifactDir; + } + + public static skipTaskWhenArtifactIsMissing(isToBeSkipped:boolean) + { + if(isToBeSkipped) + { + console.log( + `Skipping task as artifact is missing, and 'Skip If no artifact is found is true'` + ); + tl.setResult( + tl.TaskResult.Skipped, + `Skipping task as artifact is missing, and 'Skip If no artifact is found is true'` + ); + process.exit(0); + } + } +} \ No newline at end of file diff --git a/packages/azpipelines/BuildTasks/GenerateChangelogTask/GenerateChangelog.ts b/packages/azpipelines/BuildTasks/GenerateChangelogTask/GenerateChangelog.ts index a691aa657..673fa9e9d 100644 --- a/packages/azpipelines/BuildTasks/GenerateChangelogTask/GenerateChangelog.ts +++ b/packages/azpipelines/BuildTasks/GenerateChangelogTask/GenerateChangelog.ts @@ -1,14 +1,13 @@ import tl = require("azure-pipelines-task-lib/task"); -import { getWebAPIWithoutToken } from "../Common/WebAPIHelper"; -import { IReleaseApi } from "azure-devops-node-api/ReleaseApi"; -import { Release as AzpRelease } from "azure-devops-node-api/interfaces/ReleaseInterfaces"; -import ArtifactFilePathFetcher, { ArtifactFilePaths } from "../Common/ArtifactFilePathFetcher"; +import ArtifactFilePathFetcher, { ArtifactFilePaths } from "@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactFilePathFetcher"; import PackageMetadata from "@dxatscale/sfpowerscripts.core/lib/PackageMetadata"; import generateMarkdown from "@dxatscale/sfpowerscripts.core/lib/changelog/GenerateChangelogMarkdown"; import { ReleaseChangelog, Release } from "@dxatscale/sfpowerscripts.core/lib/changelog/interfaces/ReleaseChangelogInterfaces"; import { Changelog as PackageChangelog } from "@dxatscale/sfpowerscripts.core/lib/changelog/interfaces/GenericChangelogInterfaces"; import authVCS from "../Common/VersionControlAuth"; import simplegit, { SimpleGit } from "simple-git/promise"; +import ArtifactHelper from "../Common/ArtifactHelper"; + import fs = require("fs"); const tmp = require('tmp'); const path = require("path"); @@ -17,6 +16,7 @@ async function run() { let tempDir = tmp.dirSync({unsafeCleanup: true}); try { const taskType: string = tl.getVariable("Release.ReleaseId") ? "Release" : "Build"; + const artifactDir = tl.getInput("aritfactDir",false); const repositoryUrl: string = tl.getInput("repositoryUrl", true); const branch: string = tl.getInput("branchName", true); @@ -36,17 +36,6 @@ async function run() { await git.checkout(branch); - console.log("Getting latest release definition"); - - // Get latest release definition using Azure Release API - const webApi = await getWebAPIWithoutToken(); - const releaseApi: IReleaseApi = await webApi.getReleaseApi(); - - let project: string = tl.getVariable('System.TeamProject'); - let releaseId: number = parseInt(tl.getVariable('Release.ReleaseId'), 10); - let release: AzpRelease = await releaseApi.getRelease(project, releaseId); - - let packageChangelogMap: {[P:string]: string} = {}; let latestReleaseDefinition: Release = { name: tl.getInput("releaseName", true), @@ -54,17 +43,15 @@ async function run() { artifacts: [] }; - for (let artifact of release.artifacts) { - let artifactType: string; + + if (taskType === "Release") { // Use artifact type from Release API - artifactType = artifact.type; console.log( `Fetching artifacts from artifact directory ${tl.getVariable("system.artifactsDirectory")}` ); } else { // Default to Pipeline artifact type for Build task type - artifactType = "PipelineArtifact"; console.log( `Build pipeline detected.`, `Fetching artifacts from pipeline workspace ${tl.getVariable("pipeline.workspace")}` @@ -72,8 +59,7 @@ async function run() { } let artifacts_filepaths: ArtifactFilePaths[] = ArtifactFilePathFetcher.fetchArtifactFilePaths( - artifact.alias, - artifactType + ArtifactHelper.getArtifactDirectory(ArtifactHelper.getArtifactDirectory(artifactDir)) ); for (let artifactFilepaths of artifacts_filepaths) { @@ -92,7 +78,7 @@ async function run() { packageChangelogMap[packageMetadata["package_name"]] = artifactFilepaths.changelogFilePath; } - } + console.log("Generating changelog..."); diff --git a/packages/azpipelines/BuildTasks/GenerateChangelogTask/task.json b/packages/azpipelines/BuildTasks/GenerateChangelogTask/task.json index edd22d460..c98756b4f 100644 --- a/packages/azpipelines/BuildTasks/GenerateChangelogTask/task.json +++ b/packages/azpipelines/BuildTasks/GenerateChangelogTask/task.json @@ -16,6 +16,14 @@ ], "instanceNameFormat": "Generate a changelog for the current release", "inputs": [ + { + "name": "aritfactDir", + "type": "string", + "label": "Path to the directory where artifacts are downloaded", + "defaultValue": "", + "required": false, + "helpMarkDown": "Path to the artifact directory where the artifacts are downloaded, If not provided, the default values will be automatically used" + }, { "name": "releaseName", "type": "string", diff --git a/packages/azpipelines/BuildTasks/InstallSourcePackageTask/InstallSourcePackageToOrg.ts b/packages/azpipelines/BuildTasks/InstallSourcePackageTask/InstallSourcePackageToOrg.ts index e7a9385e0..e8163b6f3 100644 --- a/packages/azpipelines/BuildTasks/InstallSourcePackageTask/InstallSourcePackageToOrg.ts +++ b/packages/azpipelines/BuildTasks/InstallSourcePackageTask/InstallSourcePackageToOrg.ts @@ -8,13 +8,14 @@ import OrgDetails from "@dxatscale/sfpowerscripts.core/lib/org/OrgDetails"; import PackageMetadata from "@dxatscale/sfpowerscripts.core/lib/PackageMetadata"; import * as ExtensionManagementApi from "azure-devops-node-api/ExtensionManagementApi"; import { getWebAPIWithoutToken } from "../Common/WebAPIHelper"; -import ArtifactFilePathFetcher from "../Common/ArtifactFilePathFetcher"; +import ArtifactFilePathFetcher from "@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactFilePathFetcher"; import ManifestHelpers from "@dxatscale/sfpowerscripts.core/lib/manifest/ManifestHelpers"; import { getExtensionName, fetchPackageArtifactFromStorage, updatePackageDeploymentDetails, } from "../Common/PackageExtensionStorageHelper"; +import ArtifactHelper from "../Common/ArtifactHelper"; const fs = require("fs-extra"); const path = require("path"); @@ -27,8 +28,7 @@ async function run() { const target_org: string = tl.getInput("target_org", true); const sfdx_package: string = tl.getInput("package", false); - const package_installedfrom = tl.getInput("packageinstalledfrom", true); - const artifact = tl.getInput("artifact", false); + const artifactDir = tl.getInput("artifactDir", false); const skip_on_missing_artifact = tl.getBoolInput( "skip_on_missing_artifact", false @@ -48,15 +48,15 @@ async function run() { //Fetch Artifact let artifactFilePaths = ArtifactFilePathFetcher.fetchArtifactFilePaths( - artifact, - package_installedfrom, + ArtifactHelper.getArtifactDirectory(artifactDir), sfdx_package ); console.log("##[debug]Artifact Paths", JSON.stringify(artifactFilePaths)); - ArtifactFilePathFetcher.missingArtifactDecider( + + ArtifactHelper.skipTaskWhenArtifactIsMissing(ArtifactFilePathFetcher.missingArtifactDecider( artifactFilePaths[0].packageMetadataFilePath, skip_on_missing_artifact - ); + )); let packageMetadataFromArtifact: PackageMetadata = JSON.parse( fs.readFileSync(artifactFilePaths[0].packageMetadataFilePath, "utf8") @@ -171,7 +171,7 @@ async function run() { console.log("Failed to reconcile profiles:"+err); isReconcileErrored=true; } - } + //Reconcile Failed, Bring back the original profiles console.log("Restoring original profiles as preprocessing failed"); @@ -184,7 +184,7 @@ async function run() { ); }); } - + } //Construct Deploy Command let deploymentOptions = await generateDeploymentOptions( diff --git a/packages/azpipelines/BuildTasks/InstallSourcePackageTask/package.json b/packages/azpipelines/BuildTasks/InstallSourcePackageTask/package.json index 92ec33b05..bf4fea371 100644 --- a/packages/azpipelines/BuildTasks/InstallSourcePackageTask/package.json +++ b/packages/azpipelines/BuildTasks/InstallSourcePackageTask/package.json @@ -17,7 +17,6 @@ "azure-devops-node-api": "^10.1.1", "azure-pipelines-task-lib": "^2.8.0", "rimraf": "^3.0.0", - "fs-extra": "^8.1.0", - "glob": "^7.1.6" + "fs-extra": "^8.1.0" } } diff --git a/packages/azpipelines/BuildTasks/InstallSourcePackageTask/task.json b/packages/azpipelines/BuildTasks/InstallSourcePackageTask/task.json index 61df38fe9..010f57352 100644 --- a/packages/azpipelines/BuildTasks/InstallSourcePackageTask/task.json +++ b/packages/azpipelines/BuildTasks/InstallSourcePackageTask/task.json @@ -25,31 +25,19 @@ "helpMarkDown": "Alias or username of the target org where the code should be deployed" }, { - "name": "packageinstalledfrom", - "type": "pickList", - "label": "Package to be installed From", - "defaultValue": "BuildArtifact", - "options": { - "BuildArtifact": "Build Artifact", - "AzureArtifact": "Azure Artifact", - "PipelineArtifact": "Pipeline Artifact" - }, - "required": true, - "helpMarkDown": "Select the option from where the package version is to be picked up for install" - }, - { - "name": "artifact", + "name": "package", "type": "string", - "label": "Name of the artifact (source alias ) that is attached to this release pipeline", + "label": "Name of the package to be installed", "required": false, - "helpMarkDown": "Provide the name of the artifact which is the input to this pipeline. Please note only sfpowerkit based build artifact will work with this command" + "helpMarkDown": "Name of the package to be installed" }, { - "name": "package", + "name": "aritfactDir", "type": "string", - "label": "Name of the package to be installed", + "label": "Path to the directory where artifacts are downloaded", + "defaultValue": "", "required": false, - "helpMarkDown": "Name of the package to be installed" + "helpMarkDown": "Path to the artifact directory where the artifacts are downloaded, If not provided, the default values will be automatically used" }, { "name": "subdirectory", diff --git a/packages/azpipelines/BuildTasks/InstallUnlockedPackageTask/InstallUnlockedPackage.ts b/packages/azpipelines/BuildTasks/InstallUnlockedPackageTask/InstallUnlockedPackage.ts index dbd162da6..0d4c7c0c4 100644 --- a/packages/azpipelines/BuildTasks/InstallUnlockedPackageTask/InstallUnlockedPackage.ts +++ b/packages/azpipelines/BuildTasks/InstallUnlockedPackageTask/InstallUnlockedPackage.ts @@ -2,7 +2,7 @@ import tl = require("azure-pipelines-task-lib/task"); import InstallUnlockedPackageImpl, { PackageInstallationResult, } from "@dxatscale/sfpowerscripts.core/lib/sfdxwrappers/InstallUnlockedPackageImpl"; -import ArtifactFilePathFetcher from "../Common/ArtifactFilePathFetcher"; +import ArtifactFilePathFetcher from "@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactFilePathFetcher"; import PackageMetadata from "@dxatscale/sfpowerscripts.core/lib/PackageMetadata"; import { getWebAPIWithoutToken } from "../Common/WebAPIHelper"; import { @@ -11,15 +11,16 @@ import { updatePackageDeploymentDetails, } from "../Common/PackageExtensionStorageHelper"; import { isNullOrUndefined } from "util"; +import ArtifactHelper from "../Common/ArtifactHelper"; const fs = require("fs"); async function run() { try { - const target_org: string = tl.getInput("envname", true); + const target_org: string = tl.getInput("target_org", true); const sfdx_package: string = tl.getInput("package", true); const package_installedfrom = tl.getInput("packageinstalledfrom", true); - const artifact = tl.getInput("artifact", false); + const artifactDir = tl.getInput("artifactDir", false); const skip_on_missing_artifact = tl.getBoolInput( "skip_on_missing_artifact", false @@ -54,13 +55,15 @@ async function run() { //Fetch Artifact let artifactFilePaths = ArtifactFilePathFetcher.fetchArtifactFilePaths( - artifact, - package_installedfrom, + ArtifactHelper.getArtifactDirectory(artifactDir), sfdx_package ); - ArtifactFilePathFetcher.missingArtifactDecider( - artifactFilePaths[0].packageMetadataFilePath, - skip_on_missing_artifact + + ArtifactHelper.skipTaskWhenArtifactIsMissing( + ArtifactFilePathFetcher.missingArtifactDecider( + artifactFilePaths[0].packageMetadataFilePath, + skip_on_missing_artifact + ) ); let packageMetadataFromArtifact: PackageMetadata = JSON.parse(fs.readFileSync(artifactFilePaths[0].packageMetadataFilePath, "utf8")); diff --git a/packages/azpipelines/BuildTasks/InstallUnlockedPackageTask/package.json b/packages/azpipelines/BuildTasks/InstallUnlockedPackageTask/package.json index 1a8e1eba5..902a46c4e 100644 --- a/packages/azpipelines/BuildTasks/InstallUnlockedPackageTask/package.json +++ b/packages/azpipelines/BuildTasks/InstallUnlockedPackageTask/package.json @@ -15,7 +15,6 @@ "dependencies": { "@dxatscale/sfpowerscripts.core": "^2.0.0", "azure-devops-node-api": "^10.1.1", - "azure-pipelines-task-lib": "^2.8.0", - "glob": "^7.1.6" + "azure-pipelines-task-lib": "^2.8.0" } } diff --git a/packages/azpipelines/BuildTasks/InstallUnlockedPackageTask/task.json b/packages/azpipelines/BuildTasks/InstallUnlockedPackageTask/task.json index b66976809..aa04b5062 100644 --- a/packages/azpipelines/BuildTasks/InstallUnlockedPackageTask/task.json +++ b/packages/azpipelines/BuildTasks/InstallUnlockedPackageTask/task.json @@ -17,7 +17,7 @@ "instanceNameFormat": "Install a version of $(package) to $(envname) ", "inputs": [ { - "name": "envname", + "name": "target_org", "type": "string", "label": "Alias or username of the target environment", "defaultValue": "", @@ -35,11 +35,9 @@ "name": "packageinstalledfrom", "type": "pickList", "label": "Package to be installed From", - "defaultValue": "BuildArtifact", + "defaultValue": "Artifact", "options": { - "BuildArtifact": "Build Artifact", - "AzureArtifact": "Azure Artifact", - "PipelineArtifact":"Pipeline Artifact", + "Artifact": "From Artifacts", "Custom": "Custom" }, "required": true, @@ -54,12 +52,13 @@ "visibleRule": "packageinstalledfrom = Custom" }, { - "name": "artifact", + "name": "aritfactDir", "type": "string", - "label": "Name of the artifact (source alias ) that is attached to this release pipeline", + "label": "Path to the directory where artifacts are downloaded", + "defaultValue": "", "required": false, - "helpMarkDown": "Provide the name of the artifact which is the input to this pipeline. Please note only sfpowerkit based build artifact will work with this command", - "visibleRule": "packageinstalledfrom = BuildArtifact || packageinstalledfrom = AzureArtifact || packageinstalledfrom = PipelineArtifact" + "helpMarkDown": "Path to the artifact directory where the artifacts are downloaded, If not provided, the default values will be automatically used", + "visibleRule": "packageinstalledfrom = Artifact" }, { "name": "installationkey", diff --git a/packages/azpipelines/BuildTasks/PromoteUnlockedPackageTask/PromoteUnlockedPackage.ts b/packages/azpipelines/BuildTasks/PromoteUnlockedPackageTask/PromoteUnlockedPackage.ts index bef0e4b1b..433c11c2b 100644 --- a/packages/azpipelines/BuildTasks/PromoteUnlockedPackageTask/PromoteUnlockedPackage.ts +++ b/packages/azpipelines/BuildTasks/PromoteUnlockedPackageTask/PromoteUnlockedPackage.ts @@ -1,43 +1,42 @@ import tl = require("azure-pipelines-task-lib/task"); import PromoteUnlockedPackageImpl from "@dxatscale/sfpowerscripts.core/lib/sfdxwrappers/PromoteUnlockedPackageImpl"; -import ArtifactFilePathFetcher from "../Common/ArtifactFilePathFetcher"; +import ArtifactFilePathFetcher from "@dxatscale/sfpowerscripts.core/lib/artifacts/ArtifactFilePathFetcher"; import PackageMetadata from "@dxatscale/sfpowerscripts.core/lib/PackageMetadata"; +import ArtifactHelper from "../Common/ArtifactHelper"; import { isNullOrUndefined } from "util"; -var fs = require("fs"); +const fs = require("fs"); async function run() { try { - console.log(`SFPowerScript.. Promote Unlocked Package`); + console.log(`sfpowerscripts.. Promote Unlocked Package`); + - const package_installedfrom = tl.getInput("packagepromotedfrom", true); const sfdx_package: string = tl.getInput("package", true); + const artifactDir = tl.getInput("aritfactDir",false); const devhub_alias = tl.getInput("devhub_alias", true); - const projectDirectory = tl.getInput("project_directory", false); - const artifact = tl.getInput("artifact", false); const skip_on_missing_artifact = tl.getBoolInput( "skip_on_missing_artifact", false ); + const projectDir=tl.getInput("projectDirectory",false); let package_version_id,sourceDirectory; - if (package_installedfrom == "Custom") { - package_version_id = tl.getInput("package_version_id", false); - sourceDirectory = projectDirectory; - } else { - + + if(!isNullOrUndefined(projectDir)) + { //Fetch Artifact let artifactFilePaths = ArtifactFilePathFetcher.fetchArtifactFilePaths( - artifact, - package_installedfrom, + ArtifactHelper.getArtifactDirectory(artifactDir), sfdx_package ); - ArtifactFilePathFetcher.missingArtifactDecider( + + ArtifactHelper.skipTaskWhenArtifactIsMissing(ArtifactFilePathFetcher.missingArtifactDecider( artifactFilePaths[0].packageMetadataFilePath, skip_on_missing_artifact - ); - + )); + //Read package metadata let packageMetadataFromArtifact: PackageMetadata = JSON.parse(fs.readFileSync(artifactFilePaths[0].packageMetadataFilePath, "utf8")); @@ -55,24 +54,13 @@ async function run() { // Get Source Directory sourceDirectory = artifactFilePaths[0].sourceDirectoryPath; - - if(sourceDirectory==null) - { //Compatiblity Reasons - - //Check whether projectDirectory is provided.. - if(isNullOrUndefined(projectDirectory)) - { - tl.setResult(tl.TaskResult.Failed,"Path to the project directory with sfdx-project.json is required, Either provide the parameter, or update your package creation task to make use of new updates in package artifact"); - return; - } - else - { - sourceDirectory = projectDirectory - } - } - - } + else + { + console.log("Using project directory for prmoting package") + sourceDirectory = projectDir; + } + let promoteUnlockedPackageImpl: PromoteUnlockedPackageImpl = new PromoteUnlockedPackageImpl( sourceDirectory, diff --git a/packages/azpipelines/BuildTasks/PromoteUnlockedPackageTask/package.json b/packages/azpipelines/BuildTasks/PromoteUnlockedPackageTask/package.json index 66e25e6c5..4cd736e35 100644 --- a/packages/azpipelines/BuildTasks/PromoteUnlockedPackageTask/package.json +++ b/packages/azpipelines/BuildTasks/PromoteUnlockedPackageTask/package.json @@ -14,7 +14,6 @@ "license": "MIT", "dependencies": { "@dxatscale/sfpowerscripts.core": "^2.0.0", - "azure-pipelines-task-lib": "^2.8.0", - "glob": "^7.1.6" + "azure-pipelines-task-lib": "^2.8.0" } } diff --git a/packages/azpipelines/BuildTasks/PromoteUnlockedPackageTask/task.json b/packages/azpipelines/BuildTasks/PromoteUnlockedPackageTask/task.json index faadeb711..73478d177 100644 --- a/packages/azpipelines/BuildTasks/PromoteUnlockedPackageTask/task.json +++ b/packages/azpipelines/BuildTasks/PromoteUnlockedPackageTask/task.json @@ -24,42 +24,20 @@ "helpMarkDown": "Name of the package to be promoted" }, { - "name": "packagepromotedfrom", - "type": "pickList", - "label": "Package to be promoted From", - "defaultValue": "BuildArtifact", - "options": { - "BuildArtifact": "Build artifact", - "AzureArtifact": "Azure Artifacts", - "PipelineArtifact":"Pipeline Artifact", - "Custom": "Pass the package version id as an argument " - }, - "required": true, - "helpMarkDown": "Select the option from where the package version is to be picked up for promotion" - }, - { - "name": "package_version_id", - "type": "string", - "label": "Package Version ID", - "required": false, - "helpMarkDown": "Set the pipeline's build number to the the project's incremented version number", - "visibleRule": "packagepromotedfrom = Custom" - }, - { - "name": "artifact", + "name": "aritfactDir", "type": "string", - "label": "Name of the artifact that is attached to this release pipeline", + "label": "Path to the directory where artifacts are downloaded", + "defaultValue": "", "required": false, - "helpMarkDown": "Provide the name of the artifact which is the input to this pipeline. Please note only sfpowerkit based build artifact will work with this command", - "visibleRule": "packagepromotedfrom = BuildArtifact || packagepromotedfrom = AzureArtifact || packagepromotedfrom = PipelineArtifact" + "helpMarkDown": "Path to the artifact directory where the artifacts are downloaded, If not provided, the default values will be automatically used" }, { - "name": "project_directory", + "name": "projectDirectory", "type": "string", - "label": "Project Directory", + "label": "Project Directory, if not using artifact directory", "defaultValue": "", "required": false, - "helpMarkDown": "The project directory should contain a sfdx-project.json for this command to succeed, when used with Custom or older version of this task" + "helpMarkDown": "The project directory should contain a sfdx-project.json for this command to succeed" }, { "name": "devhub_alias", diff --git a/packages/core/src/artifacts/ArtifactFilePathFetcher.ts b/packages/core/src/artifacts/ArtifactFilePathFetcher.ts new file mode 100644 index 000000000..c126a01bc --- /dev/null +++ b/packages/core/src/artifacts/ArtifactFilePathFetcher.ts @@ -0,0 +1,103 @@ +import path = require("path"); +import fs = require("fs"); +const glob = require("glob"); + +export default class ArtifactFilePathFetcher { + /** + * Decider for which artifact retrieval method to use + * + * @param artifactDirectory + * @param artifactAlias + * @param artifactType + * @param sfdx_package + */ + public static fetchArtifactFilePaths( + artifactDirectory: string, + sfdx_package?: string + ): ArtifactFilePaths[] { + let artifacts_filepaths = ArtifactFilePathFetcher.fetchArtifactFilePathsFromArtifactDirectory( + artifactDirectory, + sfdx_package + ); + + return artifacts_filepaths; + } + + /** + * Helper method for retrieving the ArtifactFilePaths of a pipeline artifact + * @param sfdx_package + */ + private static fetchArtifactFilePathsFromArtifactDirectory( + artifactDirectory: string, + sfdx_package: string + ): ArtifactFilePaths[] { + const artifacts_filepaths: ArtifactFilePaths[] = []; + + // Search entire pipeline workspace for files matching artifact_metadata.json + let packageMetadataFilepaths: string[] = glob.sync( + `**/artifact_metadata.json`, + { + cwd: artifactDirectory, + absolute: true, + } + ); + + if (sfdx_package) { + // Filter and only return ArtifactFilePaths for sfdx_package + packageMetadataFilepaths = packageMetadataFilepaths.filter((filepath) => { + let artifactMetadata = JSON.parse(fs.readFileSync(filepath, "utf8")); + return artifactMetadata["package_name"] === sfdx_package; + }); + } + + for (let packageMetadataFilepath of packageMetadataFilepaths) { + let sourceDirectory = path.join( + path.dirname(packageMetadataFilepath), + `source` + ); + + let changelogFilepath = path.join( + path.dirname(packageMetadataFilepath), + `changelog.json` + ); + + artifacts_filepaths.push({ + packageMetadataFilePath: packageMetadataFilepath, + sourceDirectoryPath: sourceDirectory, + changelogFilePath: changelogFilepath, + }); + } + + return artifacts_filepaths; + } + + /** + * Decider for task outcome if the artifact cannot be found + * @param packageMetadataFilePath + * @param isToSkipOnMissingArtifact + */ + public static missingArtifactDecider( + packageMetadataFilePath: string, + isToSkipOnMissingArtifact: boolean + ): boolean { + if (!fs.existsSync(packageMetadataFilePath) && !isToSkipOnMissingArtifact) { + throw new Error( + `Artifact not found at ${packageMetadataFilePath}.. Please check the inputs` + ); + } else if ( + !fs.existsSync(packageMetadataFilePath) && + isToSkipOnMissingArtifact + ) { + console.log( + `Skipping task as artifact is missing, and 'Skip If no artifact is found' ${isToSkipOnMissingArtifact}` + ); + return true; + } + } +} + +export interface ArtifactFilePaths { + packageMetadataFilePath: string; + sourceDirectoryPath?: string; + changelogFilePath?: string; +} diff --git a/packages/core/src/sfdxwrappers/InstallUnlockedPackageImpl.ts b/packages/core/src/sfdxwrappers/InstallUnlockedPackageImpl.ts index dfb3fa2cd..7dbf30164 100644 --- a/packages/core/src/sfdxwrappers/InstallUnlockedPackageImpl.ts +++ b/packages/core/src/sfdxwrappers/InstallUnlockedPackageImpl.ts @@ -27,7 +27,7 @@ export default class InstallUnlockedPackageImpl { if (!isPackageInstalled) { //Print Metadata carried in the package - ManifestHelpers.printMetadataToDeploy(this.packageMetadata.payload); + ManifestHelpers.printMetadataToDeploy(this.packageMetadata?.payload); let command = this.buildPackageInstallCommand(); let child = child_process.exec(command, (error, stdout, stderr) => { diff --git a/packages/sfpowerscripts-cli/src/commands/sfpowerscripts/InstallUnlockedPackage.ts b/packages/sfpowerscripts-cli/src/commands/sfpowerscripts/InstallUnlockedPackage.ts index e36e45b47..baa8a3ea9 100644 --- a/packages/sfpowerscripts-cli/src/commands/sfpowerscripts/InstallUnlockedPackage.ts +++ b/packages/sfpowerscripts-cli/src/commands/sfpowerscripts/InstallUnlockedPackage.ts @@ -23,7 +23,7 @@ export default class InstallUnlockedPackage extends SfpowerscriptsCommand { protected static flagsConfig = { package: flags.string({char: 'n', description: messages.getMessage('packageFlagDescription')}), - envname: flags.string({char: 'u', description: messages.getMessage('envNameFlagDescription')}), + targetorg: flags.string({char: 'u', description: messages.getMessage('envNameFlagDescription')}), packageinstalledfrom: flags.boolean({char: 'i', description: messages.getMessage('packageInstalledFromFlagDescription')}), packageversionid: flags.string({char: 'v', description: messages.getMessage('packageVersionIdFlagDescription'), exclusive: ['packageinstalledfrom']}), installationkey : flags.string({char: 'k', description: messages.getMessage('installationKeyFlagDescription')}), diff --git a/tests/force-di b/tests/force-di index 2bd318d18..44271c1dd 160000 --- a/tests/force-di +++ b/tests/force-di @@ -1 +1 @@ -Subproject commit 2bd318d182d361707073c6ddaf53db0bed9b9b52 +Subproject commit 44271c1dddf7abc96aca4c4712e2fe38162b74d7