diff --git a/common/config/azure-pipelines/npm-post-publish.yaml b/common/config/azure-pipelines/npm-post-publish.yaml index 0dbe3b3b7e..7e4b70e5ab 100644 --- a/common/config/azure-pipelines/npm-post-publish.yaml +++ b/common/config/azure-pipelines/npm-post-publish.yaml @@ -20,6 +20,7 @@ resources: - pipeline: esrpPublishRushstack source: 'rushstack-esrp-publish' project: GitHubProjectsPublish + branch: refs/heads/main trigger: enabled: true branches: @@ -28,6 +29,7 @@ resources: - pipeline: esrpPublishRush source: 'rushstack-esrp-publish-rush' project: GitHubProjectsPublish + branch: refs/heads/main trigger: enabled: true branches: @@ -115,12 +117,52 @@ extends: --verbose DisplayName: 'Rush Build (repo-toolbox)' - # Query the public feed directly to bypass propagation latency in the private feed. + # Use the versions recorded during publishing to avoid propagation latency in the package feeds. + - bash: | + set -e + + triggering_alias="$(resources.triggeringAlias)" + rushstack_pipeline_id="$(resources.pipeline.esrpPublishRushstack.pipelineID)" + rushstack_run_id="$(resources.pipeline.esrpPublishRushstack.runID)" + rush_pipeline_id="$(resources.pipeline.esrpPublishRush.pipelineID)" + rush_run_id="$(resources.pipeline.esrpPublishRush.runID)" + + if [[ "$triggering_alias" == "esrpPublishRushstack" ]]; then + pipeline_id="$rushstack_pipeline_id" + run_id="$rushstack_run_id" + elif [[ "$triggering_alias" == "esrpPublishRush" ]]; then + pipeline_id="$rush_pipeline_id" + run_id="$rush_run_id" + # If this pipeline was not resource-triggered, use whichever publishing pipeline ran latest. + elif (( rushstack_run_id > rush_run_id )); then + pipeline_id="$rushstack_pipeline_id" + run_id="$rushstack_run_id" + else + pipeline_id="$rush_pipeline_id" + run_id="$rush_run_id" + fi + + echo "Using publishing pipeline $pipeline_id, run $run_id (trigger: ${triggering_alias:-manual})" + echo "##vso[task.setvariable variable=PublishingPipelineId]$pipeline_id" + echo "##vso[task.setvariable variable=PublishingRunId]$run_id" + displayName: 'Select publishing pipeline run' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download published package versions' + inputs: + source: specific + project: GitHubProjectsPublish + pipeline: $(PublishingPipelineId) + runVersion: specific + runId: $(PublishingRunId) + artifact: published-versions + path: $(Pipeline.Workspace)/published-versions + - template: /common/config/azure-pipelines/templates/run-repo-toolbox.yaml@self parameters: Arguments: > bump-decoupled-local-dependencies - --feed-url https://registry.npmjs.org/ + --published-versions-path "$(Pipeline.Workspace)/published-versions/published-versions.json" DisplayName: 'Bump decoupled local dependencies' # If Rush itself was updated by bump-decoupled-local-dependencies, we need to bootstrap diff --git a/repo-scripts/repo-toolbox/src/cli/actions/BumpDecoupledLocalDependencies.ts b/repo-scripts/repo-toolbox/src/cli/actions/BumpDecoupledLocalDependencies.ts index 77b26b03b6..f14b734644 100644 --- a/repo-scripts/repo-toolbox/src/cli/actions/BumpDecoupledLocalDependencies.ts +++ b/repo-scripts/repo-toolbox/src/cli/actions/BumpDecoupledLocalDependencies.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import * as path from 'node:path'; import type { ChildProcess } from 'node:child_process'; import { Async, Executable, FileSystem, type FolderItem, JsonFile } from '@rushstack/node-core-library'; @@ -12,8 +13,15 @@ import { CommandLineAction, type CommandLineStringParameter } from '@rushstack/t async function _getLatestPublishedVersionAsync( terminal: ITerminal, packageName: string, + publishedVersions: Record | undefined, feedUrl: string | undefined ): Promise { + const recordedVersion: string | undefined = publishedVersions?.[packageName]; + if (recordedVersion) { + terminal.writeLine(`Found version "${recordedVersion}" for "${packageName}" in published versions file`); + return recordedVersion; + } + const npmArgs: string[] = ['view', packageName, 'version']; if (feedUrl) { npmArgs.push('--registry', feedUrl); @@ -22,11 +30,20 @@ async function _getLatestPublishedVersionAsync( const childProcess: ChildProcess = Executable.spawn('npm', npmArgs, { stdio: ['ignore', 'pipe', 'pipe'] }); - const { stdout: version } = await Executable.waitForExitAsync(childProcess, { - encoding: 'utf-8', - throwOnNonZeroExitCode: true, - throwOnSignal: true + const { + stdout: version, + exitCode, + signal, + stderr + } = await Executable.waitForExitAsync(childProcess, { + encoding: 'utf-8' }); + if (exitCode !== 0 || signal) { + throw new Error( + `Failed to get latest published version for "${packageName}". Exit code: ${exitCode}, Signal: ${signal}, Stderr: ${stderr}` + ); + } + terminal.writeLine(`Found version "${version}" for "${packageName}"`); return version; } @@ -40,6 +57,7 @@ interface IProjectLike { export class BumpDecoupledLocalDependencies extends CommandLineAction { readonly #feedUrlParameter: CommandLineStringParameter; + readonly #publishedVersionsPathParameter: CommandLineStringParameter; readonly #terminal: ITerminal; public constructor(terminal: ITerminal) { @@ -53,14 +71,24 @@ export class BumpDecoupledLocalDependencies extends CommandLineAction { this.#feedUrlParameter = this.defineStringParameter({ parameterLongName: '--feed-url', - description: 'The package feed URL to query for the latest published versions.', + description: 'The package feed URL to query for published versions not found in the input file.', argumentName: 'FEED_URL' }); + + this.#publishedVersionsPathParameter = this.defineStringParameter({ + parameterLongName: '--published-versions-path', + description: 'The path to the published-versions.json file.', + argumentName: 'PATH' + }); } protected override async onExecuteAsync(): Promise { const terminal: ITerminal = this.#terminal; const feedUrl: string | undefined = this.#feedUrlParameter.value; + const publishedVersionsPath: string | undefined = this.#publishedVersionsPathParameter.value; + const publishedVersions: Record | undefined = publishedVersionsPath + ? await JsonFile.loadAsync(path.resolve(publishedVersionsPath)) + : undefined; const rushConfiguration: RushConfiguration = RushConfiguration.loadFromDefaultLocation({ startingFolder: process.cwd() }); @@ -137,6 +165,7 @@ export class BumpDecoupledLocalDependencies extends CommandLineAction { const version: string = await _getLatestPublishedVersionAsync( terminal, decoupledLocalDependencyName, + publishedVersions, feedUrl ); decoupledLocalDependencyVersionsByName.set(decoupledLocalDependencyName, version); @@ -194,6 +223,7 @@ export class BumpDecoupledLocalDependencies extends CommandLineAction { const latestRushVersion: string = await _getLatestPublishedVersionAsync( terminal, '@microsoft/rush', + publishedVersions, feedUrl ); const rushJson: IRushConfigurationJson = await JsonFile.loadAsync(rushJsonFile);