From 9920913558516edd0a4167a099eb9164ab273ce6 Mon Sep 17 00:00:00 2001 From: LouisSung <37973545+LouisSung@users.noreply.github.com> Date: Sun, 31 Jan 2021 20:58:19 +0800 Subject: [PATCH 1/2] Support build info restoring when using Pipeline Artifacts --- .../V3/ReleaseNotesFunctions.ts | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/Extensions/XplatGenerateReleaseNotes/V3/ReleaseNotesFunctions.ts b/Extensions/XplatGenerateReleaseNotes/V3/ReleaseNotesFunctions.ts index 9c94b3c95..bded2196a 100644 --- a/Extensions/XplatGenerateReleaseNotes/V3/ReleaseNotesFunctions.ts +++ b/Extensions/XplatGenerateReleaseNotes/V3/ReleaseNotesFunctions.ts @@ -41,6 +41,8 @@ export class UnifiedArtifactDetails { } } +import { ClientApiBase } from "azure-devops-node-api/ClientApiBases"; +import * as vsom from "azure-devops-node-api/VsoClient"; import * as restm from "typed-rest-client/RestClient"; import { PersonalAccessTokenCredentialHandler, BasicCredentialHandler } from "typed-rest-client/Handlers"; import tl = require("azure-pipelines-task-lib/task"); @@ -124,6 +126,80 @@ export function getSimpleArtifactArray(artifacts: Artifact[]): SimpleArtifact[] return result; } +export async function restoreAzurePipelineArtifactsBuildInfo(artifactsInRelease: SimpleArtifact[], webApi: WebApi): Promise { + let existAzurePipelineArtifacts = false; + for (const artifactInRelease of artifactsInRelease) { + if (artifactInRelease.artifactType === "PackageManagement") { + existAzurePipelineArtifacts = true; + // FIXME #937: workaround for missing PackagingApi library. Should replace with `const packagingApi = await organisation.getPackagingApi();` when available + interface PackagingPackage { id: string; string; url: string; versions: {id: string; normalizedVersion: string}[]; } + interface PackagingVersionProvenance { TeamProjectId: string; provenance: {data: {"System.DefinitionId": string; "Build.BuildId": string; "Build.BuildNumber": string}}; } + interface IPackagingApi { + getPackage(project: string, feedId: string, packageId: string, includeAllVersions?: boolean): Promise; + getPackageVersionProvenance(project: string, feedId: string, packageId: string, packageVersionId: string): Promise; + } + const PackagingApi = class extends ClientApiBase implements IPackagingApi { + constructor(...args) { super(args[0], args[1], "node-Packaging-api", args[2]); } + public static readonly RESOURCE_AREA_ID = "7ab4e64e-c4d8-4f50-ae73-5ef2e21642a5"; + public async getPackage(project: string, feedId: string, packageId: string, includeAllVersions?: boolean): Promise { + return new Promise(async (resolve, reject) => { + let routeValues: any = {project: project, feedId: feedId, packageId: packageId}; + let queryValues: any = {includeAllVersions: includeAllVersions}; + try { + let verData: vsom.ClientVersioningData = await this.vsoClient.getVersioningData("6.1-preview.1", "Packaging", "7a20d846-c929-4acc-9ea2-0d5a7df1b197", routeValues, queryValues); + let res = await this.rest.get(verData.requestUrl!, this.createRequestOptions("application/json", verData.apiVersion)); + resolve(this.formatResponse(res.result, null, true)); + } catch (err) { + reject(err); + } + }); + } + public async getPackageVersionProvenance(project: string, feedId: string, packageId: string, packageVersionId: string): Promise { + return new Promise(async (resolve, reject) => { + let routeValues: any = { + project: project, + feedId: feedId, + packageId: packageId, + packageVersionId: packageVersionId + }; + try { + let verData: vsom.ClientVersioningData = await this.vsoClient.getVersioningData("6.1-preview.1", "Packaging", "0aaeabd4-85cd-4686-8a77-8d31c15690b8", routeValues); + let res = await this.rest.get(verData.requestUrl!, this.createRequestOptions("application/json", verData.apiVersion)); + resolve(this.formatResponse(res.result, null, true)); + } catch (err) { + reject(err); + } + }); + } + }; + // const packagingApi = await organisation.getPackagingApi(); + const packagingApi = await(async (serverUrl?: string, handlers?: IRequestHandler[]): Promise => { + const this_ = webApi as any; + serverUrl = await this_._getResourceAreaUrl(serverUrl || this_.serverUrl, PackagingApi.RESOURCE_AREA_ID); + handlers = handlers || [this_.authHandler]; + return new PackagingApi(serverUrl, handlers, this_.options); + })(); + // END FIXME #937 + const guids = artifactInRelease.sourceId.match(/([0-9a-f-]{36})\/([0-9a-f-]{36})/u); + if (guids !== null) { + const [projectId, feedId] = [guids[1], guids[2]]; + const [packageId, packageVersion] = [artifactInRelease.buildDefinitionId, artifactInRelease.buildNumber]; + const artifactPackageInfo = await packagingApi.getPackage(projectId, feedId, packageId, true); + const packageVersionId = (artifactPackageInfo.versions.find((version) => version.normalizedVersion === packageVersion) || {id: ""}).id; + const artifactBuildInfo = (await packagingApi.getPackageVersionProvenance(projectId, feedId, packageId, packageVersionId)); + Object.assign(artifactInRelease, { + artifactType: "Build", + buildId: artifactBuildInfo.provenance.data["Build.BuildId"], + buildDefinitionId: artifactBuildInfo.provenance.data["System.DefinitionId"], + buildNumber: artifactBuildInfo.provenance.data["Build.BuildNumber"], + sourceId: artifactBuildInfo.TeamProjectId + } as SimpleArtifact); + } + } + } + return existAzurePipelineArtifacts ? parseInt(artifactsInRelease[0].buildId) : NaN; +} + export async function getPullRequests( gitApi: GitApi, projectName: string @@ -1038,6 +1114,7 @@ export async function generateReleaseNotes( agentApi.logInfo(`Getting all artifacts in the current release...`); var arifactsInThisRelease = getSimpleArtifactArray(currentRelease.artifacts); + buildId = await restoreAzurePipelineArtifactsBuildInfo(arifactsInThisRelease, organisation) || buildId; // update build id if using pipeline artifacts agentApi.logInfo(`Found ${arifactsInThisRelease.length}`); let arifactsInMostRecentRelease: SimpleArtifact[] = []; @@ -1046,6 +1123,7 @@ export async function generateReleaseNotes( mostRecentSuccessfulDeploymentRelease = await releaseApi.getRelease(teamProject, mostRecentSuccessfulDeployment.release.id); agentApi.logInfo(`Getting all artifacts in the most recent successful release [${mostRecentSuccessfulDeployment.release.name}]...`); arifactsInMostRecentRelease = getSimpleArtifactArray(mostRecentSuccessfulDeployment.release.artifacts); + await restoreAzurePipelineArtifactsBuildInfo(arifactsInMostRecentRelease, organisation); mostRecentSuccessfulDeploymentName = mostRecentSuccessfulDeployment.release.name; agentApi.logInfo(`Found ${arifactsInMostRecentRelease.length}`); } else { From b83eb4995c9ca22c9146df3d818a9eb220ea77ed Mon Sep 17 00:00:00 2001 From: LouisSung <37973545+LouisSung@users.noreply.github.com> Date: Sun, 31 Jan 2021 21:58:26 +0800 Subject: [PATCH 2/2] Fix typo arifacts as artifacts --- .../V3/ReleaseNotesFunctions.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Extensions/XplatGenerateReleaseNotes/V3/ReleaseNotesFunctions.ts b/Extensions/XplatGenerateReleaseNotes/V3/ReleaseNotesFunctions.ts index bded2196a..b3839658a 100644 --- a/Extensions/XplatGenerateReleaseNotes/V3/ReleaseNotesFunctions.ts +++ b/Extensions/XplatGenerateReleaseNotes/V3/ReleaseNotesFunctions.ts @@ -1113,29 +1113,29 @@ export async function generateReleaseNotes( let isInitialRelease = false; agentApi.logInfo(`Getting all artifacts in the current release...`); - var arifactsInThisRelease = getSimpleArtifactArray(currentRelease.artifacts); - buildId = await restoreAzurePipelineArtifactsBuildInfo(arifactsInThisRelease, organisation) || buildId; // update build id if using pipeline artifacts - agentApi.logInfo(`Found ${arifactsInThisRelease.length}`); + var artifactsInThisRelease = getSimpleArtifactArray(currentRelease.artifacts); + buildId = await restoreAzurePipelineArtifactsBuildInfo(artifactsInThisRelease, organisation) || buildId; // update build id if using pipeline artifacts + agentApi.logInfo(`Found ${artifactsInThisRelease.length}`); - let arifactsInMostRecentRelease: SimpleArtifact[] = []; + let artifactsInMostRecentRelease: SimpleArtifact[] = []; if (mostRecentSuccessfulDeployment) { // Get the release that the deployment was a part of - This is required for the templating. mostRecentSuccessfulDeploymentRelease = await releaseApi.getRelease(teamProject, mostRecentSuccessfulDeployment.release.id); agentApi.logInfo(`Getting all artifacts in the most recent successful release [${mostRecentSuccessfulDeployment.release.name}]...`); - arifactsInMostRecentRelease = getSimpleArtifactArray(mostRecentSuccessfulDeployment.release.artifacts); - await restoreAzurePipelineArtifactsBuildInfo(arifactsInMostRecentRelease, organisation); + artifactsInMostRecentRelease = getSimpleArtifactArray(mostRecentSuccessfulDeployment.release.artifacts); + await restoreAzurePipelineArtifactsBuildInfo(artifactsInMostRecentRelease, organisation); mostRecentSuccessfulDeploymentName = mostRecentSuccessfulDeployment.release.name; - agentApi.logInfo(`Found ${arifactsInMostRecentRelease.length}`); + agentApi.logInfo(`Found ${artifactsInMostRecentRelease.length}`); } else { agentApi.logInfo(`Skipping fetching artifact in the most recent successful release as there isn't one.`); // we need to set the last successful as the current release to templates can get some data mostRecentSuccessfulDeploymentRelease = currentRelease; mostRecentSuccessfulDeploymentName = "Initial Deployment"; - arifactsInMostRecentRelease = arifactsInThisRelease; + artifactsInMostRecentRelease = artifactsInThisRelease; isInitialRelease = true; } - for (var artifactInThisRelease of arifactsInThisRelease) { + for (var artifactInThisRelease of artifactsInThisRelease) { agentApi.logInfo(`Looking at artifact [${artifactInThisRelease.artifactAlias}]`); agentApi.logInfo(`Artifact type [${artifactInThisRelease.artifactType}]`); agentApi.logInfo(`Build Definition ID [${artifactInThisRelease.buildDefinitionId}]`); @@ -1143,10 +1143,10 @@ export async function generateReleaseNotes( agentApi.logInfo(`Is Primary: [${artifactInThisRelease.isPrimary}]`); if ((showOnlyPrimary === false) || (showOnlyPrimary === true && artifactInThisRelease.isPrimary === true)) { - if (arifactsInMostRecentRelease.length > 0) { + if (artifactsInMostRecentRelease.length > 0) { if (artifactInThisRelease.artifactType === "Build") { agentApi.logInfo(`Looking for the [${artifactInThisRelease.artifactAlias}] in the most recent successful release [${mostRecentSuccessfulDeploymentName}]`); - for (var artifactInMostRecentRelease of arifactsInMostRecentRelease) { + for (var artifactInMostRecentRelease of artifactsInMostRecentRelease) { if (artifactInThisRelease.artifactAlias.toLowerCase() === artifactInMostRecentRelease.artifactAlias.toLowerCase()) { agentApi.logInfo(`Found artifact [${artifactInMostRecentRelease.artifactAlias}] with build number [${artifactInMostRecentRelease.buildNumber}] in release [${mostRecentSuccessfulDeploymentName}]`);