Skip ADO feed 403 'already contains' when re-publishing an existing version#4813
Merged
timotheeguerin merged 12 commits intoJul 3, 2026
Merged
Conversation
Azure DevOps npm feeds are immutable and reject re-publishing an existing version with an HTTP 403 'already contains file ... in package ...' message rather than the npm 409 / EPUBLISHCONFLICT. Match that specific phrase so a re-publish is treated as a no-op, while genuine auth/permission 403s still fail the job.
Contributor
|
No changes needing a change description found. |
Contributor
|
You can try these changes here
|
Contributor
⚡ Benchmark Results
Full details – comparing
|
| Metric | Baseline | Current | Change |
|---|---|---|---|
| total | 🔴 934.1ms | 🔴 969.3ms | +3.8% |
| loader | 🟡 281.0ms | 🟡 287.1ms | +2.2% |
| resolver | 🟢 36.5ms | 🟢 41.0ms | +12.4% 🔴 |
| checker | 🟡 333.0ms | 🟡 350.1ms | +5.1% 🔴 |
| validation | 🟢 78.4ms | 🟢 86.2ms | +9.9% 🔴 |
| ↳ validation/@azure-tools/typespec-azure-core | 🟡 11.4ms | 🟡 11.9ms | +4.2% |
| ↳ validation/@typespec/http | 🟡 13.8ms | 🟡 15.0ms | +8.0% 🔴 |
| ↳ validation/@typespec/rest | 🟢 1.5ms | 🟢 1.8ms | +20.3% |
| ↳ validation/@typespec/versioning | 🔴 48.0ms | 🔴 53.4ms | +11.4% 🔴 |
| ↳ validation/compiler | 🟢 3.5ms | 🟢 4.0ms | +12.0% |
| linter | 🟢 198.8ms | 🟡 203.6ms | +2.4% |
| ↳ linter/@azure-tools/typespec-azure-core/auth-required | 🟢 0.1ms | 🟢 0.1ms | +9.2% |
| ↳ linter/@azure-tools/typespec-azure-core/bad-record-type | 🟢 0.5ms | 🟢 0.6ms | +13.5% |
| ↳ linter/@azure-tools/typespec-azure-core/byos | 🟢 7.8ms | 🟢 8.2ms | +5.0% |
| ↳ linter/@azure-tools/typespec-azure-core/casing-style | 🟢 1.3ms | 🟢 1.4ms | +8.6% |
| ↳ linter/@azure-tools/typespec-azure-core/composition-over-inheritance | 🟢 0.2ms | 🟢 0.2ms | +12.0% |
| ↳ linter/@azure-tools/typespec-azure-core/documentation-required | 🟢 1.8ms | 🟢 1.9ms | +9.7% |
| ↳ linter/@azure-tools/typespec-azure-core/friendly-name | 🟢 1.3ms | 🟢 1.4ms | +7.1% |
| ↳ linter/@azure-tools/typespec-azure-core/key-visibility-required | 🟢 0.3ms | 🟢 0.4ms | +7.1% |
| ↳ linter/@azure-tools/typespec-azure-core/known-encoding | 🟢 0.4ms | 🟢 0.5ms | +4.4% |
| ↳ linter/@azure-tools/typespec-azure-core/long-running-polling-operation-required | 🟢 0.7ms | 🟢 0.8ms | +10.9% |
| ↳ linter/@azure-tools/typespec-azure-core/no-case-mismatch | 🟢 0.6ms | 🟢 0.7ms | +7.8% |
| ↳ linter/@azure-tools/typespec-azure-core/no-closed-literal-union | 🟢 0.9ms | 🟢 1.0ms | +14.3% |
| ↳ linter/@azure-tools/typespec-azure-core/no-enum | 🟢 0.2ms | 🟢 0.2ms | +12.3% |
| ↳ linter/@azure-tools/typespec-azure-core/no-error-status-codes | 🟢 0.3ms | 🟢 0.3ms | +10.3% |
| ↳ linter/@azure-tools/typespec-azure-core/no-explicit-routes-resource-ops | 🟢 0.2ms | 🟢 0.2ms | +6.7% |
| ↳ linter/@azure-tools/typespec-azure-core/no-format | 🟢 0.8ms | 🟢 0.8ms | +4.3% |
| ↳ linter/@azure-tools/typespec-azure-core/no-generic-numeric | 🟢 0.8ms | 🟢 0.9ms | +11.1% |
| ↳ linter/@azure-tools/typespec-azure-core/no-header-explode | 🔴 24.6ms | 🔴 24.3ms | -1.2% |
| ↳ linter/@azure-tools/typespec-azure-core/no-legacy-usage | 🟢 1.8ms | 🟢 2.0ms | +9.6% |
| ↳ linter/@azure-tools/typespec-azure-core/no-multiple-discriminator | 🟢 0.2ms | 🟢 0.2ms | +5.0% |
| ↳ linter/@azure-tools/typespec-azure-core/no-nullable | 🟢 0.4ms | 🟢 0.4ms | +4.0% |
| ↳ linter/@azure-tools/typespec-azure-core/no-offsetdatetime | 🟢 1.8ms | 🟢 1.9ms | +4.9% |
| ↳ linter/@azure-tools/typespec-azure-core/no-openapi | 🟢 2.2ms | 🟢 2.3ms | +3.4% |
| ↳ linter/@azure-tools/typespec-azure-core/no-private-usage | 🟢 2.9ms | 🟢 3.1ms | +9.3% |
| ↳ linter/@azure-tools/typespec-azure-core/no-query-explode | 🔴 25.9ms | 🔴 25.6ms | -1.0% |
| ↳ linter/@azure-tools/typespec-azure-core/no-response-body | 🔴 31.1ms | 🔴 31.9ms | +2.7% |
| ↳ linter/@azure-tools/typespec-azure-core/no-rest-library-interfaces | 🟢 0.1ms | 🟢 0.1ms | +12.4% |
| ↳ linter/@azure-tools/typespec-azure-core/no-route-parameter-name-mismatch | 🟢 7.1ms | 🟢 7.1ms | -0.1% |
| ↳ linter/@azure-tools/typespec-azure-core/no-rpc-path-params | 🟢 0.4ms | 🟢 0.4ms | +6.8% |
| ↳ linter/@azure-tools/typespec-azure-core/no-string-discriminator | 🟢 0.1ms | 🟢 0.1ms | +8.2% |
| ↳ linter/@azure-tools/typespec-azure-core/no-unknown | 🟢 0.3ms | 🟢 0.3ms | +2.2% |
| ↳ linter/@azure-tools/typespec-azure-core/no-unnamed-union | 🟢 0.7ms | 🟢 0.8ms | +9.4% |
| ↳ linter/@azure-tools/typespec-azure-core/operation-missing-api-version | 🟢 0.3ms | 🟢 0.4ms | +13.2% |
| ↳ linter/@azure-tools/typespec-azure-core/request-body-problem | 🟢 0.4ms | 🟢 0.5ms | +6.4% |
| ↳ linter/@azure-tools/typespec-azure-core/require-versioned | 🟢 0.1ms | 🟢 0.1ms | +11.2% |
| ↳ linter/@azure-tools/typespec-azure-core/response-schema-problem | 🔴 30.3ms | 🔴 31.0ms | +2.4% |
| ↳ linter/@azure-tools/typespec-azure-core/rpc-operation-request-body | 🟢 0.6ms | 🟢 0.7ms | +5.3% |
| ↳ linter/@azure-tools/typespec-azure-core/spread-discriminated-model | 🟢 0.4ms | 🟢 0.4ms | +4.6% |
| ↳ linter/@azure-tools/typespec-azure-core/use-standard-names | 🟢 7.1ms | 🟢 7.2ms | +1.5% |
| ↳ linter/@azure-tools/typespec-azure-core/use-standard-operations | 🟢 0.2ms | 🟢 0.3ms | +14.4% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-agent-base-type-child-resources | 🟡 18.3ms | 🟡 19.4ms | +6.4% 🔴 |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-agent-base-type-lifecycle-operations | 🟢 0.2ms | 🟢 0.2ms | +6.2% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-common-types-version | 🟡 10.8ms | 🟡 11.3ms | +5.0% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-custom-resource-no-key | 🟢 0.2ms | 🟢 0.2ms | -0.5% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-custom-resource-usage-discourage | 🟢 0.1ms | 🟢 0.1ms | +3.9% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-delete-operation-response-codes | 🟢 5.1ms | 🟢 8.2ms | +61.7% 🔴 |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-path-casing-conflicts | 🟡 15.1ms | 🟡 16.2ms | +7.3% 🔴 |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-record | 🟢 0.6ms | 🟢 0.6ms | +3.8% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes | 🟢 1.3ms | 🟢 1.3ms | +3.5% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-put-operation-response-codes | 🟢 0.2ms | 🟢 0.2ms | +20.6% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-action-no-segment | 🟢 0.4ms | 🟢 0.4ms | +4.7% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-duplicate-property | 🟢 0.3ms | 🟢 0.3ms | +4.4% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-interface-requires-decorator | 🟢 0.1ms | 🟢 0.1ms | +12.6% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-action-verb | 🟢 0.1ms | 🟢 0.2ms | +7.0% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-envelope-property | 🟢 0.2ms | 🟢 0.2ms | +3.0% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-version-format | 🟢 0.2ms | 🟢 0.2ms | +8.3% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-key-invalid-chars | 🟢 0.4ms | 🟢 0.4ms | +2.2% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-name-pattern | 🟢 0.1ms | 🟢 0.1ms | +7.4% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation | 🟢 0.5ms | 🟢 0.5ms | +4.8% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation-response | 🟢 8.2ms | 🟢 8.4ms | +3.3% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-patch | 🟢 0.8ms | 🟢 0.8ms | +8.6% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-path-segment-invalid-chars | 🟢 0.4ms | 🟢 0.4ms | +4.4% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-provisioning-state | 🟢 0.4ms | 🟢 0.4ms | +7.6% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/beyond-nesting-levels | 🟢 0.2ms | 🟢 0.2ms | +4.0% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/empty-updateable-properties | 🟢 0.4ms | 🟢 0.4ms | +4.1% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/improper-subscription-list-operation | 🟢 0.1ms | 🟢 0.1ms | +6.8% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/lro-location-header | 🟡 18.1ms | 🟡 17.7ms | -2.1% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-operations-endpoint | 🟢 0.1ms | 🟢 0.1ms | +5.6% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-x-ms-identifiers | 🟢 0.9ms | 🟢 1.0ms | +11.2% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/no-empty-model | 🟢 0.2ms | 🟢 0.2ms | +1.4% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/no-override-props | 🟢 0.3ms | 🟢 0.3ms | +10.8% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/no-resource-delete-operation | 🟢 0.4ms | 🟢 0.4ms | -0.1% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/no-response-body | 🔴 27.5ms | 🔴 27.0ms | -1.7% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/patch-envelope | 🟢 0.4ms | 🟢 0.4ms | +4.3% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/resource-name | 🟢 0.4ms | 🟢 0.4ms | +3.9% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/secret-prop | 🟢 4.9ms | 🟢 5.4ms | +10.8% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/unsupported-type | 🟢 0.6ms | 🟢 0.7ms | +5.0% |
| ↳ linter/@azure-tools/typespec-azure-resource-manager/version-progression | 🟢 0.3ms | 🟢 0.3ms | +13.3% |
| ↳ linter/@azure-tools/typespec-client-generator-core/property-name-conflict | 🟢 2.1ms | 🟢 2.2ms | +6.4% |
| ↳ linter/@azure-tools/typespec-client-generator-core/require-client-suffix | 🟢 1.4ms | 🟢 1.6ms | +14.5% |
| emit | 🔴 5.80s | 🔴 5.86s | +0.9% |
| ↳ emit/@Azure-Tools | 🟢 0.0ms | 🟢 0.0ms | +0.0% |
| ↳ emit/@azure-tools/typespec-autorest | 🟢 116.0ms | 🟢 121.0ms | +4.4% |
| ↳ emit/@azure-tools/typespec-python | 🔴 2.18s | 🔴 2.18s | +0.1% |
| ↳ emit/@typespec | 🟢 0.0ms | 🟢 0.0ms | +0.0% |
| ↳ emit/@typespec/http-client-js | 🔴 539.1ms | 🔴 557.4ms | +3.4% |
| ↳ emit/@typespec/openapi3 | 🟢 100.4ms | 🟢 105.8ms | +5.4% 🔴 |
| ↳ emit/@typespec/openapi3/compute | 🟢 87.2ms | 🟢 91.9ms | +5.3% 🔴 |
| ↳ emit/@typespec/openapi3/write | 🟢 12.9ms | 🟢 13.6ms | +5.2% |
Averaged across 3 specs (azure-arm-resource-manager, azure-core-dataplane, azure-full).
Threshold: changes > ±5% are highlighted.
🟢 Fast · 🟡 Moderate (stages >200ms, rules >10ms) · 🔴 Slow (stages >400ms, rules >20ms)
The feed also rejects a publish with a 403 'cannot be published to the feed because it exists in at least one of the feed's upstream sources' when the version is already available upstream (npmjs). Treat this as a no-op too.
The ADO 'pwsh' task runs with $ErrorActionPreference='Stop' and pwsh 7.4+ defaults $PSNativeCommandUseErrorActionPreference to $true, so a non-zero 'npm publish' exit (e.g. version already exists) threw a terminating error before the $LASTEXITCODE inspection, defeating the skip logic. Relax both so the skip check runs.
Use core's publish-npm-packages.ts (skips already-existing versions, fails on real errors) and bump the core submodule to pick it up. The deployment job now checks out sources (with submodules) and pins Node to run the TypeScript directly.
Do not merge: temporarily comments out non-essential build-for-publish steps (tests, python/ts e2e, coverage, bundler/manifest/playground uploads) to shorten publish-feed retry cycles.
1ES release jobs forbid 'checkout', so the shared Node script cannot run in the publish deployment job. Go back to inline pwsh and fix the real failure: a skipped 'npm publish' left a non-zero $LASTEXITCODE that leaked as the pwsh process exit code and failed the task. Capture the exit code immediately, classify the output, and exit 0 explicitly when there is no genuine error. Bump the core submodule to pick up the matching change.
The ADO PowerShell task appends 'exit $LASTEXITCODE' after the inline script (unless ignoreLASTEXITCODE is set). After a skipped 'npm publish' the leftover non-zero code fails the task even though we handled it. Reset $global:LASTEXITCODE explicitly (0 on success/skip, 1 on genuine error) plus our own exit. Bump core submodule to match.
Replace the inline pwsh publish with the shared publish-npm-packages.ts (from core) run by node. It classifies each package as published/skipped/failed (treating ADO feed immutability and existing-upstream 403s as skippable no-ops), emits Azure DevOps log groups with colored status, prints a summary, and controls its own exit code. 1ES release jobs forbid checkout, so the script is copied into the packages artifact at pack time and the release job downloads the whole artifact and runs it with a NodeTool-pinned Node. Bump core submodule to match.
Re-enables tests, python/ts e2e, coverage and the bundler/manifest/playground uploads that were temporarily commented out (6374f45) to speed up ADO-feed publish retries.
…feed-skip-already-contains # Conflicts: # core
NodeTool@0 is deprecated; switch to UseNode@1 (input 'version' instead of 'versionSpec'). Keeps this template byte-identical with the core copy.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The ADO
azure-sdk-for-jsfeed publish step (added in #4807) fails when a package version already exists in the feed:Azure DevOps Artifacts npm feeds are immutable: re-publishing an already-existing version returns an HTTP 403 with an
already contains file ... in package ...message, not the npm409/EPUBLISHCONFLICTthat the skip logic currently matches. As a result a harmless re-publish fails the pipeline.There is no npm flag (
--force,--skip-existing) that bypasses feed immutability, so the 403 must be tolerated explicitly.Change
Extend the "already published, treat as no-op" detection in
ado-feed-release.ymlto also match the ADO immutability message (already contains file,already contains ... in package). The match is intentionally specific — a bare403is not matched — so genuine auth/permission 403s still fail the job.Notes
microsoft/typespecand receives the same fix in Skip ADO feed 403 'already contains' when re-publishing an existing version microsoft/typespec#11164.