diff --git a/core b/core index 9e8bb238a4..d9d9ab6419 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 9e8bb238a44076c2f913a7c1cd6d77cd7df6f89e +Subproject commit d9d9ab64199fb9bb23ca3b824425c86d915f3238 diff --git a/eng/pipelines/jobs/publish-npm.yml b/eng/pipelines/jobs/publish-npm.yml index 5222c35099..da9cfbb155 100644 --- a/eng/pipelines/jobs/publish-npm.yml +++ b/eng/pipelines/jobs/publish-npm.yml @@ -25,7 +25,9 @@ jobs: inputs: - input: pipelineArtifact artifactName: ${{ parameters.artifactName }} - itemPattern: "**/*.tgz" + # Download the whole artifact (the .tgz packages plus publish-npm-packages.ts, which the + # checkout-less release job runs to publish them). + itemPattern: "**" targetPath: $(Pipeline.Workspace)/${{ parameters.artifactName }}/ strategy: diff --git a/eng/pipelines/templates/pack.yml b/eng/pipelines/templates/pack.yml index 73adf132c8..c663383f38 100644 --- a/eng/pipelines/templates/pack.yml +++ b/eng/pipelines/templates/pack.yml @@ -15,5 +15,8 @@ steps: echo "Create manifest of unpublished packages in $summaryFilePath" node core/eng/tsp-core/scripts/filter-unpublished-packages.ts "$outDir" --manifest $summaryFilePath + + echo "Copy the publish script into the artifact so the (checkout-less) release job can run it" + Copy-Item core/eng/tsp-core/scripts/publish-npm-packages.ts "$outDir/publish-npm-packages.ts" name: pack_${{ parameters.name }} displayName: Pack packages ${{ parameters.artifactName }} diff --git a/eng/pipelines/templates/release/ado-feed-release.yml b/eng/pipelines/templates/release/ado-feed-release.yml index b53aa59934..b1d4bf9ba3 100644 --- a/eng/pipelines/templates/release/ado-feed-release.yml +++ b/eng/pipelines/templates/release/ado-feed-release.yml @@ -1,4 +1,3 @@ -# cspell:ignore EPUBLISHCONFLICT parameters: - name: tag type: string @@ -8,6 +7,9 @@ parameters: - name: registryUrl type: string default: https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ + - name: nodeVersion + type: string + default: 24.16.0 steps: # Publishing the packages directly to the Azure DevOps `azure-sdk-for-js` feed bypasses the @@ -18,32 +20,17 @@ steps: npmrcPath: ${{ parameters.path }}/.npmrc registryUrl: ${{ parameters.registryUrl }} - - pwsh: | - $packageFiles = Get-ChildItem -Path . -Filter '*.tgz' - $hadError = $false - foreach ($file in $packageFiles.Name) { - Write-Host "npm publish $file --verbose --access public --tag ${{ parameters.tag }}" - $output = npm publish $file --verbose --access public --tag ${{ parameters.tag }} 2>&1 - $output | ForEach-Object { Write-Host $_ } - if ($LASTEXITCODE -ne 0) { - $text = $output -join "`n" - # Treat "version already exists" as a no-op, matching the npm/ESRP behavior of not - # republishing an existing version. Any other failure is a genuine error and must fail. - if ($text -match 'EPUBLISHCONFLICT' -or - $text -match 'cannot publish over the previously published versions' -or - $text -match 'You cannot publish over the previously published versions' -or - $text -match 'previously published version' -or - $text -match '\b409\b') { - Write-Warning "Version for $file already exists in the feed, skipping." - } else { - Write-Error "Failed to publish $file to the DevOps feed." - $hadError = $true - } - } - } - if ($hadError) { - exit 1 - } + # This is a 1ES release (deployment) job, which forbids `checkout`, so publish-npm-packages.ts is + # shipped inside the packages artifact (copied there by the pack step) and run from there. + - task: UseNode@1 + inputs: + version: ${{ parameters.nodeVersion }} + displayName: Install Node.js + retryCountOnTaskFailure: 3 + + # The script publishes every *.tgz, treating "version already exists" (ADO feed immutability or an + # existing upstream copy on npmjs) as a skippable no-op while failing on any genuine error. It + # controls its own exit code, so there is no leaked pwsh $LASTEXITCODE to fail the task on a skip. + - script: node "${{ parameters.path }}/publish-npm-packages.ts" "${{ parameters.path }}" --tag ${{ parameters.tag }} displayName: Publish to DevOps feed (${{ parameters.tag }}) condition: and(succeeded(), ne(variables['SkipPublishing'], 'true')) - workingDirectory: ${{ parameters.path }}