From 85bf1b47d8a84ec4ebe2ce3a7a30f489935c436e Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Wed, 8 Jul 2026 17:04:39 +0800 Subject: [PATCH 01/18] Add post-publish-sdk pipeline to regenerate TypeSpec SDKs Ports the post-publish SDK generation automation from Azure/autorest.java to this repo, adapted for the typespec-java emitter's new home in Azure/typespec-azure (packages/typespec-java). - eng/pipelines/post-publish-sdk.yaml: regenerates all TypeSpec-based SDKs and opens an automated draft PR. Uses the published @azure-tools/typespec-java npm package by default; when the TypeSpecJavaPRId parameter is set, builds the emitter dev package from that typespec-azure PR (turbo builds workspace deps, then Build-TypeSpec.ps1 builds and packs). - eng/pipelines/scripts/sync_sdk.py: regeneration script. - eng/pipelines/scripts/patches/: SDK patches applied after generation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/post-publish-sdk.yaml | 134 +++++ .../patches/0001-key-certificates-impl.patch | 239 ++++++++ ...evert-Impl-for-onlineexperimentation.patch | 56 ++ ...revert-azure-ai-speech-transcription.patch | 113 ++++ .../0001-revert-impl-in-communication.patch | 268 +++++++++ .../0001-revert-impl-in-contentsafety.patch | 99 ++++ ...-revert-impl-in-documentintelligence.patch | 534 ++++++++++++++++++ .../patches/0001-revert-impl-in-easm.patch | 135 +++++ .../patches/0001-revert-impl-in-monitor.patch | 58 ++ .../0001-revert-impl-in-translation.patch | 59 ++ eng/pipelines/scripts/sync_sdk.py | 270 +++++++++ 11 files changed, 1965 insertions(+) create mode 100644 eng/pipelines/post-publish-sdk.yaml create mode 100644 eng/pipelines/scripts/patches/0001-key-certificates-impl.patch create mode 100644 eng/pipelines/scripts/patches/0001-revert-Impl-for-onlineexperimentation.patch create mode 100644 eng/pipelines/scripts/patches/0001-revert-azure-ai-speech-transcription.patch create mode 100644 eng/pipelines/scripts/patches/0001-revert-impl-in-communication.patch create mode 100644 eng/pipelines/scripts/patches/0001-revert-impl-in-contentsafety.patch create mode 100644 eng/pipelines/scripts/patches/0001-revert-impl-in-documentintelligence.patch create mode 100644 eng/pipelines/scripts/patches/0001-revert-impl-in-easm.patch create mode 100644 eng/pipelines/scripts/patches/0001-revert-impl-in-monitor.patch create mode 100644 eng/pipelines/scripts/patches/0001-revert-impl-in-translation.patch create mode 100644 eng/pipelines/scripts/sync_sdk.py diff --git a/eng/pipelines/post-publish-sdk.yaml b/eng/pipelines/post-publish-sdk.yaml new file mode 100644 index 000000000000..e600d9041676 --- /dev/null +++ b/eng/pipelines/post-publish-sdk.yaml @@ -0,0 +1,134 @@ +# Regenerates all TypeSpec-based SDKs in azure-sdk-for-java using the typespec-java emitter, +# then opens an automated draft PR with the results. +# +# The typespec-java emitter lives in Azure/typespec-azure (packages/typespec-java). +# By default this pipeline generates using the published @azure-tools/typespec-java npm +# package. To validate an unreleased emitter change, provide the TypeSpecJavaPRId parameter +# pointing at a Pull Request in the Azure/typespec-azure repository; the emitter dev package +# is then built from that PR and used for generation. +trigger: none +pr: none + +parameters: +- name: TypeSpecJavaPRId + displayName: >- + Pull Request in the Azure/typespec-azure repo to build the typespec-java emitter from. + Accepts a PR number (e.g. 43312) or a full ref path (e.g. refs/pull/43312/merge). + Leave empty to use the published @azure-tools/typespec-java npm package. + type: string + default: '' + +jobs: +- job: Generate_SDK + + timeoutInMinutes: 120 + + variables: + - template: /eng/pipelines/templates/variables/globals.yml + - template: /eng/pipelines/templates/variables/image.yml + - name: NodeVersion + value: '24.x' + # Local clone target for the (public) Azure/typespec-azure emitter repo. + - name: TypeSpecAzureDirectory + value: $(Agent.BuildDirectory)/typespec-azure + - name: PullRequestTitleSuffix + ${{ if ne(parameters.TypeSpecJavaPRId, '') }}: + value: " DEV (typespec-azure PR ${{ parameters.TypeSpecJavaPRId }})" + ${{ else }}: + value: "" + + pool: + name: $(LINUXPOOL) + image: $(LINUXVMIMAGE) + os: linux + + steps: + # azure-sdk-for-java (self) is the only repo resource, so it is checked out to + # $(Build.SourcesDirectory). Azure/typespec-azure is public, so it is cloned directly + # (below) instead of via a repository resource + GitHub service connection. + - checkout: self + + - task: NodeTool@0 + displayName: 'Install Node.js $(NodeVersion)' + inputs: + versionSpec: '$(NodeVersion)' + + - template: /eng/pipelines/templates/steps/maven-authenticate.yml + parameters: + SourceDirectory: $(Build.SourcesDirectory) + + - template: /eng/common/pipelines/templates/steps/create-authenticated-npmrc.yml + parameters: + registryUrl: https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ + + # Clone the public Azure/typespec-azure repo (no service connection required). When a PR is + # specified via TypeSpecJavaPRId, fetch and check out that PR ref (number or full ref path). + - task: PowerShell@2 + displayName: 'Clone typespec-azure' + inputs: + pwsh: true + targetType: 'inline' + script: | + git clone https://github.com/Azure/typespec-azure.git "$(TypeSpecAzureDirectory)" + $prId = '${{ parameters.TypeSpecJavaPRId }}' + if ($prId) { + if ($prId -match '^\d+$') { + $ref = "refs/pull/$prId/merge" + } else { + $ref = $prId + } + Write-Host "Fetching typespec-azure ref $ref" + git -C "$(TypeSpecAzureDirectory)" fetch origin $ref + git -C "$(TypeSpecAzureDirectory)" checkout FETCH_HEAD + } + + # Build the emitter dev package (.tgz) from the typespec-azure PR. + # typespec-java depends on other workspace packages (workspace:^). First build only those + # upstream dependencies with turbo (the `^...` filter selects dependencies but excludes + # typespec-java itself). Then Build-TypeSpec.ps1 builds and packs typespec-java (its + # build:generator step runs Build-Generator.ps1, which needs JDK 11+ and Maven), producing + # the .tgz next to the package.json for sync_sdk.py to pick up. + - task: PowerShell@2 + retryCountOnTaskFailure: 1 + condition: and(succeeded(), ne('${{ parameters.TypeSpecJavaPRId }}', '')) + displayName: 'Build typespec-java dev package' + inputs: + pwsh: true + targetType: 'inline' + script: | + corepack enable + pnpm install + pnpm turbo run build --filter "@azure-tools/typespec-java^..." + ./packages/typespec-java/Build-TypeSpec.ps1 + workingDirectory: $(TypeSpecAzureDirectory) + + - script: | + npm install -g @azure-tools/typespec-client-generator-cli + displayName: 'Install tsp-client' + + - task: PowerShell@2 + displayName: 'Get Package Version' + inputs: + pwsh: true + targetType: 'inline' + script: | + $PACKAGE_VERSION = node -p -e "require('./packages/typespec-java/package.json').version" + Write-Host("##vso[task.setvariable variable=PackageVersion]$PACKAGE_VERSION") + workingDirectory: $(TypeSpecAzureDirectory) + + - script: | + python3 ./eng/pipelines/scripts/sync_sdk.py --sdk-root=$(Build.SourcesDirectory) --package-json-path=$(TypeSpecAzureDirectory)/packages/typespec-java/package.json --dev-package=${{ ne(parameters.TypeSpecJavaPRId, '') }} + displayName: 'Generate SDK' + workingDirectory: $(Build.SourcesDirectory) + + - template: /eng/common/pipelines/templates/steps/create-pull-request.yml + parameters: + WorkingDirectory: $(Build.SourcesDirectory) + ScriptDirectory: $(Build.SourcesDirectory)/eng/common/scripts + RepoName: azure-sdk-for-java + BaseBranchName: 'refs/heads/main' + PRBranchName: typespec-java-generation-$(Build.BuildId) + CommitMsg: '[Automation] Generate SDK based on TypeSpec $(PackageVersion)$(PullRequestTitleSuffix)' + PRTitle: '[Automation] Generate SDK based on TypeSpec $(PackageVersion)$(PullRequestTitleSuffix)' + PRLabels: 'DPG' + OpenAsDraft: 'true' diff --git a/eng/pipelines/scripts/patches/0001-key-certificates-impl.patch b/eng/pipelines/scripts/patches/0001-key-certificates-impl.patch new file mode 100644 index 000000000000..ea3813e85588 --- /dev/null +++ b/eng/pipelines/scripts/patches/0001-key-certificates-impl.patch @@ -0,0 +1,239 @@ +From e2df7de3f5643ff2006a8244e28ed9aa3598ecf3 Mon Sep 17 00:00:00 2001 +From: Weidong Xu +Date: Thu, 11 Jun 2026 20:39:16 +0800 +Subject: [PATCH] Revert "regen" + +This reverts commit 7ccefe36941cc38f14a52ca796167a95f263ce3f. +--- + .../models/CertificatePolicy.java | 66 ++++++++++++++----- + 1 file changed, 51 insertions(+), 15 deletions(-) + +diff --git a/sdk/keyvault/azure-security-keyvault-certificates/src/main/java/com/azure/security/keyvault/certificates/implementation/models/CertificatePolicy.java b/sdk/keyvault/azure-security-keyvault-certificates/src/main/java/com/azure/security/keyvault/certificates/implementation/models/CertificatePolicy.java +index e16ea8f5900..33d11980151 100644 +--- a/sdk/keyvault/azure-security-keyvault-certificates/src/main/java/com/azure/security/keyvault/certificates/implementation/models/CertificatePolicy.java ++++ b/sdk/keyvault/azure-security-keyvault-certificates/src/main/java/com/azure/security/keyvault/certificates/implementation/models/CertificatePolicy.java +@@ -1,6 +1,7 @@ + // Copyright (c) Microsoft Corporation. All rights reserved. + // Licensed under the MIT License. + // Code generated by Microsoft (R) TypeSpec Code Generator. ++ + package com.azure.security.keyvault.certificates.implementation.models; + + import com.azure.core.annotation.Fluent; +@@ -9,6 +10,7 @@ import com.azure.json.JsonReader; + import com.azure.json.JsonSerializable; + import com.azure.json.JsonToken; + import com.azure.json.JsonWriter; ++import com.azure.security.keyvault.certificates.models.PlatformManaged; + import java.io.IOException; + import java.util.List; + +@@ -17,7 +19,6 @@ import java.util.List; + */ + @Fluent + public final class CertificatePolicy implements JsonSerializable { +- + /* + * The certificate id. + */ +@@ -60,6 +61,12 @@ public final class CertificatePolicy implements JsonSerializable writer.writeJson(element)); + jsonWriter.writeJsonField("issuer", this.issuerParameters); + jsonWriter.writeJsonField("attributes", this.attributes); ++ jsonWriter.writeJsonField("platformManaged", this.platformManaged); + return jsonWriter.writeEndObject(); + } + + /** + * Reads an instance of CertificatePolicy from the JsonReader. +- * ++ * + * @param jsonReader The JsonReader being read. + * @return An instance of CertificatePolicy if the JsonReader was pointing to an instance of it, or null if it was + * pointing to JSON null. +@@ -241,6 +273,7 @@ public final class CertificatePolicy implements JsonSerializable +Date: Mon, 22 Sep 2025 15:32:34 +0800 +Subject: [PATCH] revert Impl for onlineexperimentation + +--- + .../OnlineExperimentationClientImpl.java | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/sdk/onlineexperimentation/azure-analytics-onlineexperimentation/src/main/java/com/azure/analytics/onlineexperimentation/implementation/OnlineExperimentationClientImpl.java b/sdk/onlineexperimentation/azure-analytics-onlineexperimentation/src/main/java/com/azure/analytics/onlineexperimentation/implementation/OnlineExperimentationClientImpl.java +index 32e6622cbc6..ed26312e71d 100644 +--- a/sdk/onlineexperimentation/azure-analytics-onlineexperimentation/src/main/java/com/azure/analytics/onlineexperimentation/implementation/OnlineExperimentationClientImpl.java ++++ b/sdk/onlineexperimentation/azure-analytics-onlineexperimentation/src/main/java/com/azure/analytics/onlineexperimentation/implementation/OnlineExperimentationClientImpl.java +@@ -233,7 +233,7 @@ public final class OnlineExperimentationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteMetric(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("experimentMetricId") String experimentMetricId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Delete("/experiment-metrics/{experimentMetricId}") + @ExpectedResponses({ 204 }) +@@ -243,7 +243,7 @@ public final class OnlineExperimentationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteMetricSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("experimentMetricId") String experimentMetricId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Get("/experiment-metrics") + @ExpectedResponses({ 200 }) +@@ -698,8 +698,9 @@ public final class OnlineExperimentationClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteMetricWithResponseAsync(String experimentMetricId, + RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteMetric(this.getEndpoint(), +- this.getServiceVersion().getVersion(), experimentMetricId, requestOptions, context)); ++ this.getServiceVersion().getVersion(), experimentMetricId, accept, requestOptions, context)); + } + + /** +@@ -730,8 +731,9 @@ public final class OnlineExperimentationClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteMetricWithResponse(String experimentMetricId, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return service.deleteMetricSync(this.getEndpoint(), this.getServiceVersion().getVersion(), experimentMetricId, +- requestOptions, Context.NONE); ++ accept, requestOptions, Context.NONE); + } + + /** +-- +2.51.0.windows.1 + diff --git a/eng/pipelines/scripts/patches/0001-revert-azure-ai-speech-transcription.patch b/eng/pipelines/scripts/patches/0001-revert-azure-ai-speech-transcription.patch new file mode 100644 index 000000000000..926230202d80 --- /dev/null +++ b/eng/pipelines/scripts/patches/0001-revert-azure-ai-speech-transcription.patch @@ -0,0 +1,113 @@ +From bc30c29232e16f5b29af07159d6f9bac6d3bf36c Mon Sep 17 00:00:00 2001 +From: Weidong Xu +Date: Thu, 21 May 2026 11:20:39 +0800 +Subject: [PATCH] revert azure-ai-speech-transcription + +--- + .../generated/TranscribeAudioFileTests.java | 10 +++++----- + .../generated/TranscribeAudioFromURLTests.java | 10 +++++----- + .../generated/TranscribeWithEnhancedModeTests.java | 10 +++++----- + 3 files changed, 15 insertions(+), 15 deletions(-) + +diff --git a/sdk/transcription/azure-ai-speech-transcription/src/test/java/com/azure/ai/speech/transcription/generated/TranscribeAudioFileTests.java b/sdk/transcription/azure-ai-speech-transcription/src/test/java/com/azure/ai/speech/transcription/generated/TranscribeAudioFileTests.java +index a6285e1cfe2..ac8c847552a 100644 +--- a/sdk/transcription/azure-ai-speech-transcription/src/test/java/com/azure/ai/speech/transcription/generated/TranscribeAudioFileTests.java ++++ b/sdk/transcription/azure-ai-speech-transcription/src/test/java/com/azure/ai/speech/transcription/generated/TranscribeAudioFileTests.java +@@ -24,7 +24,7 @@ public final class TranscribeAudioFileTests extends TranscriptionClientTestBase + // response assertion + Assertions.assertNotNull(response); + // verify property "duration" +- Assertions.assertEquals(2000, response.getDuration()); ++ Assertions.assertEquals(2000, response.getDuration().toMillis()); + // verify property "combinedPhrases" + List responseCombinedPhrases = response.getCombinedPhrases(); + ChannelCombinedPhrases responseCombinedPhrasesFirstItem = responseCombinedPhrases.iterator().next(); +@@ -34,15 +34,15 @@ public final class TranscribeAudioFileTests extends TranscriptionClientTestBase + List responsePhrases = response.getPhrases(); + TranscribedPhrase responsePhrasesFirstItem = responsePhrases.iterator().next(); + Assertions.assertNotNull(responsePhrasesFirstItem); +- Assertions.assertEquals(40, responsePhrasesFirstItem.getOffset()); +- Assertions.assertEquals(320, responsePhrasesFirstItem.getDuration()); ++ Assertions.assertEquals(40, responsePhrasesFirstItem.getOffset().toMillis()); ++ Assertions.assertEquals(320, responsePhrasesFirstItem.getDuration().toMillis()); + Assertions.assertEquals("Weather", responsePhrasesFirstItem.getText()); + List responsePhrasesFirstItemWords = responsePhrasesFirstItem.getWords(); + TranscribedWord responsePhrasesFirstItemWordsFirstItem = responsePhrasesFirstItemWords.iterator().next(); + Assertions.assertNotNull(responsePhrasesFirstItemWordsFirstItem); + Assertions.assertEquals("weather", responsePhrasesFirstItemWordsFirstItem.getText()); +- Assertions.assertEquals(40, responsePhrasesFirstItemWordsFirstItem.getOffset()); +- Assertions.assertEquals(320, responsePhrasesFirstItemWordsFirstItem.getDuration()); ++ Assertions.assertEquals(40, responsePhrasesFirstItemWordsFirstItem.getOffset().toMillis()); ++ Assertions.assertEquals(320, responsePhrasesFirstItemWordsFirstItem.getDuration().toMillis()); + Assertions.assertEquals("en-US", responsePhrasesFirstItem.getLocale()); + Assertions.assertEquals(0.78983736, responsePhrasesFirstItem.getConfidence()); + } +diff --git a/sdk/transcription/azure-ai-speech-transcription/src/test/java/com/azure/ai/speech/transcription/generated/TranscribeAudioFromURLTests.java b/sdk/transcription/azure-ai-speech-transcription/src/test/java/com/azure/ai/speech/transcription/generated/TranscribeAudioFromURLTests.java +index c904299bcab..e431b89cc08 100644 +--- a/sdk/transcription/azure-ai-speech-transcription/src/test/java/com/azure/ai/speech/transcription/generated/TranscribeAudioFromURLTests.java ++++ b/sdk/transcription/azure-ai-speech-transcription/src/test/java/com/azure/ai/speech/transcription/generated/TranscribeAudioFromURLTests.java +@@ -24,7 +24,7 @@ public final class TranscribeAudioFromURLTests extends TranscriptionClientTestBa + // response assertion + Assertions.assertNotNull(response); + // verify property "duration" +- Assertions.assertEquals(2000, response.getDuration()); ++ Assertions.assertEquals(2000, response.getDuration().toMillis()); + // verify property "combinedPhrases" + List responseCombinedPhrases = response.getCombinedPhrases(); + ChannelCombinedPhrases responseCombinedPhrasesFirstItem = responseCombinedPhrases.iterator().next(); +@@ -34,15 +34,15 @@ public final class TranscribeAudioFromURLTests extends TranscriptionClientTestBa + List responsePhrases = response.getPhrases(); + TranscribedPhrase responsePhrasesFirstItem = responsePhrases.iterator().next(); + Assertions.assertNotNull(responsePhrasesFirstItem); +- Assertions.assertEquals(40, responsePhrasesFirstItem.getOffset()); +- Assertions.assertEquals(320, responsePhrasesFirstItem.getDuration()); ++ Assertions.assertEquals(40, responsePhrasesFirstItem.getOffset().toMillis()); ++ Assertions.assertEquals(320, responsePhrasesFirstItem.getDuration().toMillis()); + Assertions.assertEquals("Weather", responsePhrasesFirstItem.getText()); + List responsePhrasesFirstItemWords = responsePhrasesFirstItem.getWords(); + TranscribedWord responsePhrasesFirstItemWordsFirstItem = responsePhrasesFirstItemWords.iterator().next(); + Assertions.assertNotNull(responsePhrasesFirstItemWordsFirstItem); + Assertions.assertEquals("weather", responsePhrasesFirstItemWordsFirstItem.getText()); +- Assertions.assertEquals(40, responsePhrasesFirstItemWordsFirstItem.getOffset()); +- Assertions.assertEquals(320, responsePhrasesFirstItemWordsFirstItem.getDuration()); ++ Assertions.assertEquals(40, responsePhrasesFirstItemWordsFirstItem.getOffset().toMillis()); ++ Assertions.assertEquals(320, responsePhrasesFirstItemWordsFirstItem.getDuration().toMillis()); + Assertions.assertEquals("en-US", responsePhrasesFirstItem.getLocale()); + Assertions.assertEquals(0.78983736, responsePhrasesFirstItem.getConfidence()); + } +diff --git a/sdk/transcription/azure-ai-speech-transcription/src/test/java/com/azure/ai/speech/transcription/generated/TranscribeWithEnhancedModeTests.java b/sdk/transcription/azure-ai-speech-transcription/src/test/java/com/azure/ai/speech/transcription/generated/TranscribeWithEnhancedModeTests.java +index 926e39c0905..b92423bd969 100644 +--- a/sdk/transcription/azure-ai-speech-transcription/src/test/java/com/azure/ai/speech/transcription/generated/TranscribeWithEnhancedModeTests.java ++++ b/sdk/transcription/azure-ai-speech-transcription/src/test/java/com/azure/ai/speech/transcription/generated/TranscribeWithEnhancedModeTests.java +@@ -24,7 +24,7 @@ public final class TranscribeWithEnhancedModeTests extends TranscriptionClientTe + // response assertion + Assertions.assertNotNull(response); + // verify property "duration" +- Assertions.assertEquals(2000, response.getDuration()); ++ Assertions.assertEquals(2000, response.getDuration().toMillis()); + // verify property "combinedPhrases" + List responseCombinedPhrases = response.getCombinedPhrases(); + ChannelCombinedPhrases responseCombinedPhrasesFirstItem = responseCombinedPhrases.iterator().next(); +@@ -34,15 +34,15 @@ public final class TranscribeWithEnhancedModeTests extends TranscriptionClientTe + List responsePhrases = response.getPhrases(); + TranscribedPhrase responsePhrasesFirstItem = responsePhrases.iterator().next(); + Assertions.assertNotNull(responsePhrasesFirstItem); +- Assertions.assertEquals(40, responsePhrasesFirstItem.getOffset()); +- Assertions.assertEquals(320, responsePhrasesFirstItem.getDuration()); ++ Assertions.assertEquals(40, responsePhrasesFirstItem.getOffset().toMillis()); ++ Assertions.assertEquals(320, responsePhrasesFirstItem.getDuration().toMillis()); + Assertions.assertEquals("天气", responsePhrasesFirstItem.getText()); + List responsePhrasesFirstItemWords = responsePhrasesFirstItem.getWords(); + TranscribedWord responsePhrasesFirstItemWordsFirstItem = responsePhrasesFirstItemWords.iterator().next(); + Assertions.assertNotNull(responsePhrasesFirstItemWordsFirstItem); + Assertions.assertEquals("天", responsePhrasesFirstItemWordsFirstItem.getText()); +- Assertions.assertEquals(0, responsePhrasesFirstItemWordsFirstItem.getOffset()); +- Assertions.assertEquals(0, responsePhrasesFirstItemWordsFirstItem.getDuration()); ++ Assertions.assertEquals(0, responsePhrasesFirstItemWordsFirstItem.getOffset().toMillis()); ++ Assertions.assertEquals(0, responsePhrasesFirstItemWordsFirstItem.getDuration().toMillis()); + Assertions.assertEquals("zh-CN", responsePhrasesFirstItem.getLocale()); + Assertions.assertEquals(0.78983736, responsePhrasesFirstItem.getConfidence()); + } +-- +2.53.0.windows.2 + diff --git a/eng/pipelines/scripts/patches/0001-revert-impl-in-communication.patch b/eng/pipelines/scripts/patches/0001-revert-impl-in-communication.patch new file mode 100644 index 000000000000..767d5d08bb4d --- /dev/null +++ b/eng/pipelines/scripts/patches/0001-revert-impl-in-communication.patch @@ -0,0 +1,268 @@ +From 46e470e80eeb94ef3da2346bce9e6368792ad4ae Mon Sep 17 00:00:00 2001 +From: Weidong Xu +Date: Thu, 7 Aug 2025 14:13:06 +0800 +Subject: [PATCH] revert impl in communication + +--- + .../JobRouterAdministrationClientImpl.java | 48 +++++++++++-------- + .../implementation/JobRouterClientImpl.java | 22 +++++---- + 2 files changed, 41 insertions(+), 29 deletions(-) + +diff --git a/sdk/communication/azure-communication-jobrouter/src/main/java/com/azure/communication/jobrouter/implementation/JobRouterAdministrationClientImpl.java b/sdk/communication/azure-communication-jobrouter/src/main/java/com/azure/communication/jobrouter/implementation/JobRouterAdministrationClientImpl.java +index 78b69849b85..3a598fb5a4e 100644 +--- a/sdk/communication/azure-communication-jobrouter/src/main/java/com/azure/communication/jobrouter/implementation/JobRouterAdministrationClientImpl.java ++++ b/sdk/communication/azure-communication-jobrouter/src/main/java/com/azure/communication/jobrouter/implementation/JobRouterAdministrationClientImpl.java +@@ -234,8 +234,8 @@ public final class JobRouterAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteDistributionPolicy(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, +- @PathParam("distributionPolicyId") String distributionPolicyId, RequestOptions requestOptions, +- Context context); ++ @PathParam("distributionPolicyId") String distributionPolicyId, @HeaderParam("Accept") String accept, ++ RequestOptions requestOptions, Context context); + + @Delete("/routing/distributionPolicies/{distributionPolicyId}") + @ExpectedResponses({ 204 }) +@@ -245,8 +245,8 @@ public final class JobRouterAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteDistributionPolicySync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, +- @PathParam("distributionPolicyId") String distributionPolicyId, RequestOptions requestOptions, +- Context context); ++ @PathParam("distributionPolicyId") String distributionPolicyId, @HeaderParam("Accept") String accept, ++ RequestOptions requestOptions, Context context); + + @Patch("/routing/classificationPolicies/{classificationPolicyId}") + @ExpectedResponses({ 200, 201 }) +@@ -324,8 +324,8 @@ public final class JobRouterAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteClassificationPolicy(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, +- @PathParam("classificationPolicyId") String classificationPolicyId, RequestOptions requestOptions, +- Context context); ++ @PathParam("classificationPolicyId") String classificationPolicyId, @HeaderParam("Accept") String accept, ++ RequestOptions requestOptions, Context context); + + @Delete("/routing/classificationPolicies/{classificationPolicyId}") + @ExpectedResponses({ 204 }) +@@ -335,8 +335,8 @@ public final class JobRouterAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteClassificationPolicySync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, +- @PathParam("classificationPolicyId") String classificationPolicyId, RequestOptions requestOptions, +- Context context); ++ @PathParam("classificationPolicyId") String classificationPolicyId, @HeaderParam("Accept") String accept, ++ RequestOptions requestOptions, Context context); + + @Patch("/routing/exceptionPolicies/{exceptionPolicyId}") + @ExpectedResponses({ 200, 201 }) +@@ -410,7 +410,7 @@ public final class JobRouterAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteExceptionPolicy(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("exceptionPolicyId") String exceptionPolicyId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Delete("/routing/exceptionPolicies/{exceptionPolicyId}") + @ExpectedResponses({ 204 }) +@@ -420,7 +420,7 @@ public final class JobRouterAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteExceptionPolicySync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("exceptionPolicyId") String exceptionPolicyId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Patch("/routing/queues/{queueId}") + @ExpectedResponses({ 200, 201 }) +@@ -494,7 +494,7 @@ public final class JobRouterAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteQueue(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("queueId") String queueId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Delete("/routing/queues/{queueId}") + @ExpectedResponses({ 204 }) +@@ -504,7 +504,7 @@ public final class JobRouterAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteQueueSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("queueId") String queueId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Get("{nextLink}") + @ExpectedResponses({ 200 }) +@@ -1032,8 +1032,9 @@ public final class JobRouterAdministrationClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteDistributionPolicyWithResponseAsync(String distributionPolicyId, + RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteDistributionPolicy(this.getEndpoint(), +- this.getServiceVersion().getVersion(), distributionPolicyId, requestOptions, context)); ++ this.getServiceVersion().getVersion(), distributionPolicyId, accept, requestOptions, context)); + } + + /** +@@ -1050,8 +1051,9 @@ public final class JobRouterAdministrationClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteDistributionPolicyWithResponse(String distributionPolicyId, + RequestOptions requestOptions) { ++ final String accept = "application/json"; + return service.deleteDistributionPolicySync(this.getEndpoint(), this.getServiceVersion().getVersion(), +- distributionPolicyId, requestOptions, Context.NONE); ++ distributionPolicyId, accept, requestOptions, Context.NONE); + } + + /** +@@ -1569,8 +1571,9 @@ public final class JobRouterAdministrationClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteClassificationPolicyWithResponseAsync(String classificationPolicyId, + RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteClassificationPolicy(this.getEndpoint(), +- this.getServiceVersion().getVersion(), classificationPolicyId, requestOptions, context)); ++ this.getServiceVersion().getVersion(), classificationPolicyId, accept, requestOptions, context)); + } + + /** +@@ -1587,8 +1590,9 @@ public final class JobRouterAdministrationClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteClassificationPolicyWithResponse(String classificationPolicyId, + RequestOptions requestOptions) { ++ final String accept = "application/json"; + return service.deleteClassificationPolicySync(this.getEndpoint(), this.getServiceVersion().getVersion(), +- classificationPolicyId, requestOptions, Context.NONE); ++ classificationPolicyId, accept, requestOptions, Context.NONE); + } + + /** +@@ -2106,8 +2110,9 @@ public final class JobRouterAdministrationClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteExceptionPolicyWithResponseAsync(String exceptionPolicyId, + RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteExceptionPolicy(this.getEndpoint(), +- this.getServiceVersion().getVersion(), exceptionPolicyId, requestOptions, context)); ++ this.getServiceVersion().getVersion(), exceptionPolicyId, accept, requestOptions, context)); + } + + /** +@@ -2123,8 +2128,9 @@ public final class JobRouterAdministrationClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteExceptionPolicyWithResponse(String exceptionPolicyId, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return service.deleteExceptionPolicySync(this.getEndpoint(), this.getServiceVersion().getVersion(), +- exceptionPolicyId, requestOptions, Context.NONE); ++ exceptionPolicyId, accept, requestOptions, Context.NONE); + } + + /** +@@ -2548,8 +2554,9 @@ public final class JobRouterAdministrationClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteQueueWithResponseAsync(String queueId, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteQueue(this.getEndpoint(), +- this.getServiceVersion().getVersion(), queueId, requestOptions, context)); ++ this.getServiceVersion().getVersion(), queueId, accept, requestOptions, context)); + } + + /** +@@ -2565,7 +2572,8 @@ public final class JobRouterAdministrationClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteQueueWithResponse(String queueId, RequestOptions requestOptions) { +- return service.deleteQueueSync(this.getEndpoint(), this.getServiceVersion().getVersion(), queueId, ++ final String accept = "application/json"; ++ return service.deleteQueueSync(this.getEndpoint(), this.getServiceVersion().getVersion(), queueId, accept, + requestOptions, Context.NONE); + } + +diff --git a/sdk/communication/azure-communication-jobrouter/src/main/java/com/azure/communication/jobrouter/implementation/JobRouterClientImpl.java b/sdk/communication/azure-communication-jobrouter/src/main/java/com/azure/communication/jobrouter/implementation/JobRouterClientImpl.java +index 1ab1634dd63..e0d6dc7f6e7 100644 +--- a/sdk/communication/azure-communication-jobrouter/src/main/java/com/azure/communication/jobrouter/implementation/JobRouterClientImpl.java ++++ b/sdk/communication/azure-communication-jobrouter/src/main/java/com/azure/communication/jobrouter/implementation/JobRouterClientImpl.java +@@ -210,7 +210,7 @@ public final class JobRouterClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteJob(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("jobId") String jobId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Delete("/routing/jobs/{jobId}") + @ExpectedResponses({ 204 }) +@@ -220,7 +220,7 @@ public final class JobRouterClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteJobSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("jobId") String jobId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Post("/routing/jobs/{jobId}:reclassify") + @ExpectedResponses({ 200 }) +@@ -484,7 +484,7 @@ public final class JobRouterClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteWorker(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("workerId") String workerId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Delete("/routing/workers/{workerId}") + @ExpectedResponses({ 204 }) +@@ -494,7 +494,7 @@ public final class JobRouterClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteWorkerSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("workerId") String workerId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Get("/routing/workers") + @ExpectedResponses({ 200 }) +@@ -1012,8 +1012,9 @@ public final class JobRouterClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteJobWithResponseAsync(String jobId, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteJob(this.getEndpoint(), +- this.getServiceVersion().getVersion(), jobId, requestOptions, context)); ++ this.getServiceVersion().getVersion(), jobId, accept, requestOptions, context)); + } + + /** +@@ -1029,8 +1030,9 @@ public final class JobRouterClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteJobWithResponse(String jobId, RequestOptions requestOptions) { +- return service.deleteJobSync(this.getEndpoint(), this.getServiceVersion().getVersion(), jobId, requestOptions, +- Context.NONE); ++ final String accept = "application/json"; ++ return service.deleteJobSync(this.getEndpoint(), this.getServiceVersion().getVersion(), jobId, accept, ++ requestOptions, Context.NONE); + } + + /** +@@ -2671,8 +2673,9 @@ public final class JobRouterClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteWorkerWithResponseAsync(String workerId, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteWorker(this.getEndpoint(), +- this.getServiceVersion().getVersion(), workerId, requestOptions, context)); ++ this.getServiceVersion().getVersion(), workerId, accept, requestOptions, context)); + } + + /** +@@ -2688,7 +2691,8 @@ public final class JobRouterClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteWorkerWithResponse(String workerId, RequestOptions requestOptions) { +- return service.deleteWorkerSync(this.getEndpoint(), this.getServiceVersion().getVersion(), workerId, ++ final String accept = "application/json"; ++ return service.deleteWorkerSync(this.getEndpoint(), this.getServiceVersion().getVersion(), workerId, accept, + requestOptions, Context.NONE); + } + +-- +2.50.1.windows.1 + diff --git a/eng/pipelines/scripts/patches/0001-revert-impl-in-contentsafety.patch b/eng/pipelines/scripts/patches/0001-revert-impl-in-contentsafety.patch new file mode 100644 index 000000000000..9bace5b40da0 --- /dev/null +++ b/eng/pipelines/scripts/patches/0001-revert-impl-in-contentsafety.patch @@ -0,0 +1,99 @@ +From 1185f9ded7e644d6b64c955c2faa9422765eea24 Mon Sep 17 00:00:00 2001 +From: Weidong Xu +Date: Thu, 7 Aug 2025 14:28:55 +0800 +Subject: [PATCH] revert impl in contentsafety + +--- + .../implementation/BlocklistClientImpl.java | 24 +++++++++++-------- + 1 file changed, 14 insertions(+), 10 deletions(-) + +diff --git a/sdk/contentsafety/azure-ai-contentsafety/src/main/java/com/azure/ai/contentsafety/implementation/BlocklistClientImpl.java b/sdk/contentsafety/azure-ai-contentsafety/src/main/java/com/azure/ai/contentsafety/implementation/BlocklistClientImpl.java +index e7e96b52ccf..727d27c5547 100644 +--- a/sdk/contentsafety/azure-ai-contentsafety/src/main/java/com/azure/ai/contentsafety/implementation/BlocklistClientImpl.java ++++ b/sdk/contentsafety/azure-ai-contentsafety/src/main/java/com/azure/ai/contentsafety/implementation/BlocklistClientImpl.java +@@ -216,7 +216,7 @@ public final class BlocklistClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteTextBlocklist(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("blocklistName") String name, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Delete("/text/blocklists/{blocklistName}") + @ExpectedResponses({ 204 }) +@@ -226,7 +226,7 @@ public final class BlocklistClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteTextBlocklistSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("blocklistName") String name, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Get("/text/blocklists/{blocklistName}") + @ExpectedResponses({ 200 }) +@@ -318,8 +318,8 @@ public final class BlocklistClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> removeBlocklistItems(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("blocklistName") String name, +- @HeaderParam("Content-Type") String contentType, @BodyParam("application/json") BinaryData options, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Content-Type") String contentType, @HeaderParam("Accept") String accept, ++ @BodyParam("application/json") BinaryData options, RequestOptions requestOptions, Context context); + + @Post("/text/blocklists/{blocklistName}:removeBlocklistItems") + @ExpectedResponses({ 204 }) +@@ -329,8 +329,8 @@ public final class BlocklistClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response removeBlocklistItemsSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("blocklistName") String name, +- @HeaderParam("Content-Type") String contentType, @BodyParam("application/json") BinaryData options, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Content-Type") String contentType, @HeaderParam("Accept") String accept, ++ @BodyParam("application/json") BinaryData options, RequestOptions requestOptions, Context context); + + @Get("{nextLink}") + @ExpectedResponses({ 200 }) +@@ -587,8 +587,9 @@ public final class BlocklistClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteTextBlocklistWithResponseAsync(String name, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteTextBlocklist(this.getEndpoint(), +- this.getServiceVersion().getVersion(), name, requestOptions, context)); ++ this.getServiceVersion().getVersion(), name, accept, requestOptions, context)); + } + + /** +@@ -606,7 +607,8 @@ public final class BlocklistClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteTextBlocklistWithResponse(String name, RequestOptions requestOptions) { +- return service.deleteTextBlocklistSync(this.getEndpoint(), this.getServiceVersion().getVersion(), name, ++ final String accept = "application/json"; ++ return service.deleteTextBlocklistSync(this.getEndpoint(), this.getServiceVersion().getVersion(), name, accept, + requestOptions, Context.NONE); + } + +@@ -1126,8 +1128,9 @@ public final class BlocklistClientImpl { + public Mono> removeBlocklistItemsWithResponseAsync(String name, BinaryData options, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.removeBlocklistItems(this.getEndpoint(), +- this.getServiceVersion().getVersion(), name, contentType, options, requestOptions, context)); ++ this.getServiceVersion().getVersion(), name, contentType, accept, options, requestOptions, context)); + } + + /** +@@ -1159,8 +1162,9 @@ public final class BlocklistClientImpl { + public Response removeBlocklistItemsWithResponse(String name, BinaryData options, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return service.removeBlocklistItemsSync(this.getEndpoint(), this.getServiceVersion().getVersion(), name, +- contentType, options, requestOptions, Context.NONE); ++ contentType, accept, options, requestOptions, Context.NONE); + } + + /** +-- +2.50.1.windows.1 + diff --git a/eng/pipelines/scripts/patches/0001-revert-impl-in-documentintelligence.patch b/eng/pipelines/scripts/patches/0001-revert-impl-in-documentintelligence.patch new file mode 100644 index 000000000000..a43942ea8dee --- /dev/null +++ b/eng/pipelines/scripts/patches/0001-revert-impl-in-documentintelligence.patch @@ -0,0 +1,534 @@ +From 3efa4c0cb7a78e09ff941b751092948faa74e70a Mon Sep 17 00:00:00 2001 +From: Weidong Xu +Date: Thu, 7 Aug 2025 12:28:19 +0800 +Subject: [PATCH] revert impl in documentintelligence + +--- + ...tIntelligenceAdministrationClientImpl.java | 90 ++++++++++++------- + .../DocumentIntelligenceClientImpl.java | 68 ++++++++------ + 2 files changed, 98 insertions(+), 60 deletions(-) + +diff --git a/sdk/documentintelligence/azure-ai-documentintelligence/src/main/java/com/azure/ai/documentintelligence/implementation/DocumentIntelligenceAdministrationClientImpl.java b/sdk/documentintelligence/azure-ai-documentintelligence/src/main/java/com/azure/ai/documentintelligence/implementation/DocumentIntelligenceAdministrationClientImpl.java +index edb1f567ca1..54d0a403774 100644 +--- a/sdk/documentintelligence/azure-ai-documentintelligence/src/main/java/com/azure/ai/documentintelligence/implementation/DocumentIntelligenceAdministrationClientImpl.java ++++ b/sdk/documentintelligence/azure-ai-documentintelligence/src/main/java/com/azure/ai/documentintelligence/implementation/DocumentIntelligenceAdministrationClientImpl.java +@@ -178,7 +178,8 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> buildDocumentModel(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @HeaderParam("Content-Type") String contentType, +- @BodyParam("application/json") BinaryData buildRequest, RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, @BodyParam("application/json") BinaryData buildRequest, ++ RequestOptions requestOptions, Context context); + + @Post("/documentModels:build") + @ExpectedResponses({ 202 }) +@@ -188,7 +189,8 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response buildDocumentModelSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @HeaderParam("Content-Type") String contentType, +- @BodyParam("application/json") BinaryData buildRequest, RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, @BodyParam("application/json") BinaryData buildRequest, ++ RequestOptions requestOptions, Context context); + + @Post("/documentModels:compose") + @ExpectedResponses({ 202 }) +@@ -198,7 +200,8 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> composeModel(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @HeaderParam("Content-Type") String contentType, +- @BodyParam("application/json") BinaryData composeRequest, RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, @BodyParam("application/json") BinaryData composeRequest, ++ RequestOptions requestOptions, Context context); + + @Post("/documentModels:compose") + @ExpectedResponses({ 202 }) +@@ -208,7 +211,8 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response composeModelSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @HeaderParam("Content-Type") String contentType, +- @BodyParam("application/json") BinaryData composeRequest, RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, @BodyParam("application/json") BinaryData composeRequest, ++ RequestOptions requestOptions, Context context); + + @Post("/documentModels:authorizeCopy") + @ExpectedResponses({ 200 }) +@@ -240,8 +244,8 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> copyModelTo(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("modelId") String modelId, +- @HeaderParam("Content-Type") String contentType, @BodyParam("application/json") BinaryData copyToRequest, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Content-Type") String contentType, @HeaderParam("Accept") String accept, ++ @BodyParam("application/json") BinaryData copyToRequest, RequestOptions requestOptions, Context context); + + @Post("/documentModels/{modelId}:copyTo") + @ExpectedResponses({ 202 }) +@@ -251,8 +255,8 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response copyModelToSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("modelId") String modelId, +- @HeaderParam("Content-Type") String contentType, @BodyParam("application/json") BinaryData copyToRequest, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Content-Type") String contentType, @HeaderParam("Accept") String accept, ++ @BodyParam("application/json") BinaryData copyToRequest, RequestOptions requestOptions, Context context); + + @Get("/documentModels/{modelId}") + @ExpectedResponses({ 200 }) +@@ -302,7 +306,7 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteModel(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("modelId") String modelId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Delete("/documentModels/{modelId}") + @ExpectedResponses({ 204 }) +@@ -312,7 +316,7 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteModelSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("modelId") String modelId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Get("/info") + @ExpectedResponses({ 200 }) +@@ -382,7 +386,8 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> buildClassifier(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @HeaderParam("Content-Type") String contentType, +- @BodyParam("application/json") BinaryData buildRequest, RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, @BodyParam("application/json") BinaryData buildRequest, ++ RequestOptions requestOptions, Context context); + + @Post("/documentClassifiers:build") + @ExpectedResponses({ 202 }) +@@ -392,7 +397,8 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response buildClassifierSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @HeaderParam("Content-Type") String contentType, +- @BodyParam("application/json") BinaryData buildRequest, RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, @BodyParam("application/json") BinaryData buildRequest, ++ RequestOptions requestOptions, Context context); + + @Post("/documentClassifiers:authorizeCopy") + @ExpectedResponses({ 200 }) +@@ -424,8 +430,8 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> copyClassifierTo(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("classifierId") String classifierId, +- @HeaderParam("Content-Type") String contentType, @BodyParam("application/json") BinaryData copyToRequest, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Content-Type") String contentType, @HeaderParam("Accept") String accept, ++ @BodyParam("application/json") BinaryData copyToRequest, RequestOptions requestOptions, Context context); + + @Post("/documentClassifiers/{classifierId}:copyTo") + @ExpectedResponses({ 202 }) +@@ -435,8 +441,8 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response copyClassifierToSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("classifierId") String classifierId, +- @HeaderParam("Content-Type") String contentType, @BodyParam("application/json") BinaryData copyToRequest, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Content-Type") String contentType, @HeaderParam("Accept") String accept, ++ @BodyParam("application/json") BinaryData copyToRequest, RequestOptions requestOptions, Context context); + + @Get("/documentClassifiers/{classifierId}") + @ExpectedResponses({ 200 }) +@@ -486,7 +492,7 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteClassifier(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("classifierId") String classifierId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Delete("/documentClassifiers/{classifierId}") + @ExpectedResponses({ 204 }) +@@ -496,7 +502,7 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteClassifierSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("classifierId") String classifierId, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Get("{nextLink}") + @ExpectedResponses({ 200 }) +@@ -598,8 +604,9 @@ public final class DocumentIntelligenceAdministrationClientImpl { + private Mono> buildDocumentModelWithResponseAsync(BinaryData buildRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.buildDocumentModel(this.getEndpoint(), +- this.getServiceVersion().getVersion(), contentType, buildRequest, requestOptions, context)); ++ this.getServiceVersion().getVersion(), contentType, accept, buildRequest, requestOptions, context)); + } + + /** +@@ -640,8 +647,9 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + private Response buildDocumentModelWithResponse(BinaryData buildRequest, RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return service.buildDocumentModelSync(this.getEndpoint(), this.getServiceVersion().getVersion(), contentType, +- buildRequest, requestOptions, Context.NONE); ++ accept, buildRequest, requestOptions, Context.NONE); + } + + /** +@@ -909,8 +917,9 @@ public final class DocumentIntelligenceAdministrationClientImpl { + private Mono> composeModelWithResponseAsync(BinaryData composeRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.composeModel(this.getEndpoint(), +- this.getServiceVersion().getVersion(), contentType, composeRequest, requestOptions, context)); ++ this.getServiceVersion().getVersion(), contentType, accept, composeRequest, requestOptions, context)); + } + + /** +@@ -971,7 +980,8 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + private Response composeModelWithResponse(BinaryData composeRequest, RequestOptions requestOptions) { + final String contentType = "application/json"; +- return service.composeModelSync(this.getEndpoint(), this.getServiceVersion().getVersion(), contentType, ++ final String accept = "application/json"; ++ return service.composeModelSync(this.getEndpoint(), this.getServiceVersion().getVersion(), contentType, accept, + composeRequest, requestOptions, Context.NONE); + } + +@@ -1391,8 +1401,10 @@ public final class DocumentIntelligenceAdministrationClientImpl { + private Mono> copyModelToWithResponseAsync(String modelId, BinaryData copyToRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; +- return FluxUtil.withContext(context -> service.copyModelTo(this.getEndpoint(), +- this.getServiceVersion().getVersion(), modelId, contentType, copyToRequest, requestOptions, context)); ++ final String accept = "application/json"; ++ return FluxUtil ++ .withContext(context -> service.copyModelTo(this.getEndpoint(), this.getServiceVersion().getVersion(), ++ modelId, contentType, accept, copyToRequest, requestOptions, context)); + } + + /** +@@ -1425,8 +1437,9 @@ public final class DocumentIntelligenceAdministrationClientImpl { + private Response copyModelToWithResponse(String modelId, BinaryData copyToRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return service.copyModelToSync(this.getEndpoint(), this.getServiceVersion().getVersion(), modelId, contentType, +- copyToRequest, requestOptions, Context.NONE); ++ accept, copyToRequest, requestOptions, Context.NONE); + } + + /** +@@ -2116,8 +2129,9 @@ public final class DocumentIntelligenceAdministrationClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteModelWithResponseAsync(String modelId, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteModel(this.getEndpoint(), +- this.getServiceVersion().getVersion(), modelId, requestOptions, context)); ++ this.getServiceVersion().getVersion(), modelId, accept, requestOptions, context)); + } + + /** +@@ -2133,7 +2147,8 @@ public final class DocumentIntelligenceAdministrationClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteModelWithResponse(String modelId, RequestOptions requestOptions) { +- return service.deleteModelSync(this.getEndpoint(), this.getServiceVersion().getVersion(), modelId, ++ final String accept = "application/json"; ++ return service.deleteModelSync(this.getEndpoint(), this.getServiceVersion().getVersion(), modelId, accept, + requestOptions, Context.NONE); + } + +@@ -2544,8 +2559,9 @@ public final class DocumentIntelligenceAdministrationClientImpl { + private Mono> buildClassifierWithResponseAsync(BinaryData buildRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.buildClassifier(this.getEndpoint(), +- this.getServiceVersion().getVersion(), contentType, buildRequest, requestOptions, context)); ++ this.getServiceVersion().getVersion(), contentType, accept, buildRequest, requestOptions, context)); + } + + /** +@@ -2587,8 +2603,9 @@ public final class DocumentIntelligenceAdministrationClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + private Response buildClassifierWithResponse(BinaryData buildRequest, RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return service.buildClassifierSync(this.getEndpoint(), this.getServiceVersion().getVersion(), contentType, +- buildRequest, requestOptions, Context.NONE); ++ accept, buildRequest, requestOptions, Context.NONE); + } + + /** +@@ -2931,8 +2948,10 @@ public final class DocumentIntelligenceAdministrationClientImpl { + private Mono> copyClassifierToWithResponseAsync(String classifierId, BinaryData copyToRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; +- return FluxUtil.withContext(context -> service.copyClassifierTo(this.getEndpoint(), +- this.getServiceVersion().getVersion(), classifierId, contentType, copyToRequest, requestOptions, context)); ++ final String accept = "application/json"; ++ return FluxUtil ++ .withContext(context -> service.copyClassifierTo(this.getEndpoint(), this.getServiceVersion().getVersion(), ++ classifierId, contentType, accept, copyToRequest, requestOptions, context)); + } + + /** +@@ -2965,8 +2984,9 @@ public final class DocumentIntelligenceAdministrationClientImpl { + private Response copyClassifierToWithResponse(String classifierId, BinaryData copyToRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return service.copyClassifierToSync(this.getEndpoint(), this.getServiceVersion().getVersion(), classifierId, +- contentType, copyToRequest, requestOptions, Context.NONE); ++ contentType, accept, copyToRequest, requestOptions, Context.NONE); + } + + /** +@@ -3479,8 +3499,9 @@ public final class DocumentIntelligenceAdministrationClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteClassifierWithResponseAsync(String classifierId, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteClassifier(this.getEndpoint(), +- this.getServiceVersion().getVersion(), classifierId, requestOptions, context)); ++ this.getServiceVersion().getVersion(), classifierId, accept, requestOptions, context)); + } + + /** +@@ -3496,8 +3517,9 @@ public final class DocumentIntelligenceAdministrationClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteClassifierWithResponse(String classifierId, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return service.deleteClassifierSync(this.getEndpoint(), this.getServiceVersion().getVersion(), classifierId, +- requestOptions, Context.NONE); ++ accept, requestOptions, Context.NONE); + } + + /** +diff --git a/sdk/documentintelligence/azure-ai-documentintelligence/src/main/java/com/azure/ai/documentintelligence/implementation/DocumentIntelligenceClientImpl.java b/sdk/documentintelligence/azure-ai-documentintelligence/src/main/java/com/azure/ai/documentintelligence/implementation/DocumentIntelligenceClientImpl.java +index 35d46b5d152..f1ad7faf330 100644 +--- a/sdk/documentintelligence/azure-ai-documentintelligence/src/main/java/com/azure/ai/documentintelligence/implementation/DocumentIntelligenceClientImpl.java ++++ b/sdk/documentintelligence/azure-ai-documentintelligence/src/main/java/com/azure/ai/documentintelligence/implementation/DocumentIntelligenceClientImpl.java +@@ -174,8 +174,8 @@ public final class DocumentIntelligenceClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> analyzeDocument(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("modelId") String modelId, +- @HeaderParam("content-type") String contentType, @BodyParam("application/json") BinaryData analyzeRequest, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("content-type") String contentType, @HeaderParam("Accept") String accept, ++ @BodyParam("application/json") BinaryData analyzeRequest, RequestOptions requestOptions, Context context); + + @Post("/documentModels/{modelId}:analyze") + @ExpectedResponses({ 202 }) +@@ -185,8 +185,8 @@ public final class DocumentIntelligenceClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response analyzeDocumentSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("modelId") String modelId, +- @HeaderParam("content-type") String contentType, @BodyParam("application/json") BinaryData analyzeRequest, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("content-type") String contentType, @HeaderParam("Accept") String accept, ++ @BodyParam("application/json") BinaryData analyzeRequest, RequestOptions requestOptions, Context context); + + @Get("/documentModels/{modelId}/analyzeResults/{resultId}/pdf") + @ExpectedResponses({ 200 }) +@@ -240,7 +240,8 @@ public final class DocumentIntelligenceClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteAnalyzeResult(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("modelId") String modelId, +- @PathParam("resultId") String resultId, RequestOptions requestOptions, Context context); ++ @PathParam("resultId") String resultId, @HeaderParam("Accept") String accept, RequestOptions requestOptions, ++ Context context); + + @Delete("/documentModels/{modelId}/analyzeResults/{resultId}") + @ExpectedResponses({ 204 }) +@@ -250,7 +251,8 @@ public final class DocumentIntelligenceClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteAnalyzeResultSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("modelId") String modelId, +- @PathParam("resultId") String resultId, RequestOptions requestOptions, Context context); ++ @PathParam("resultId") String resultId, @HeaderParam("Accept") String accept, RequestOptions requestOptions, ++ Context context); + + @Post("/documentModels/{modelId}:analyzeBatch") + @ExpectedResponses({ 202 }) +@@ -260,7 +262,7 @@ public final class DocumentIntelligenceClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> analyzeBatchDocuments(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("modelId") String modelId, +- @HeaderParam("content-type") String contentType, ++ @HeaderParam("content-type") String contentType, @HeaderParam("Accept") String accept, + @BodyParam("application/json") BinaryData analyzeBatchRequest, RequestOptions requestOptions, + Context context); + +@@ -272,7 +274,7 @@ public final class DocumentIntelligenceClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response analyzeBatchDocumentsSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("modelId") String modelId, +- @HeaderParam("content-type") String contentType, ++ @HeaderParam("content-type") String contentType, @HeaderParam("Accept") String accept, + @BodyParam("application/json") BinaryData analyzeBatchRequest, RequestOptions requestOptions, + Context context); + +@@ -304,7 +306,8 @@ public final class DocumentIntelligenceClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteAnalyzeBatchResult(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("modelId") String modelId, +- @PathParam("resultId") String resultId, RequestOptions requestOptions, Context context); ++ @PathParam("resultId") String resultId, @HeaderParam("Accept") String accept, RequestOptions requestOptions, ++ Context context); + + @Delete("/documentModels/{modelId}/analyzeBatchResults/{resultId}") + @ExpectedResponses({ 204 }) +@@ -314,7 +317,8 @@ public final class DocumentIntelligenceClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteAnalyzeBatchResultSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("modelId") String modelId, +- @PathParam("resultId") String resultId, RequestOptions requestOptions, Context context); ++ @PathParam("resultId") String resultId, @HeaderParam("Accept") String accept, RequestOptions requestOptions, ++ Context context); + + @Get("/documentModels/{modelId}/analyzeBatchResults/{resultId}") + @ExpectedResponses({ 200 }) +@@ -346,8 +350,8 @@ public final class DocumentIntelligenceClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> classifyDocument(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("classifierId") String classifierId, +- @HeaderParam("content-type") String contentType, @BodyParam("application/json") BinaryData classifyRequest, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("content-type") String contentType, @HeaderParam("Accept") String accept, ++ @BodyParam("application/json") BinaryData classifyRequest, RequestOptions requestOptions, Context context); + + @Post("/documentClassifiers/{classifierId}:analyze") + @ExpectedResponses({ 202 }) +@@ -357,8 +361,8 @@ public final class DocumentIntelligenceClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response classifyDocumentSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("classifierId") String classifierId, +- @HeaderParam("content-type") String contentType, @BodyParam("application/json") BinaryData classifyRequest, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("content-type") String contentType, @HeaderParam("Accept") String accept, ++ @BodyParam("application/json") BinaryData classifyRequest, RequestOptions requestOptions, Context context); + + @Get("{nextLink}") + @ExpectedResponses({ 200 }) +@@ -427,8 +431,10 @@ public final class DocumentIntelligenceClientImpl { + private Mono> analyzeDocumentWithResponseAsync(String modelId, BinaryData analyzeRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; +- return FluxUtil.withContext(context -> service.analyzeDocument(this.getEndpoint(), +- this.getServiceVersion().getVersion(), modelId, contentType, analyzeRequest, requestOptions, context)); ++ final String accept = "application/json"; ++ return FluxUtil ++ .withContext(context -> service.analyzeDocument(this.getEndpoint(), this.getServiceVersion().getVersion(), ++ modelId, contentType, accept, analyzeRequest, requestOptions, context)); + } + + /** +@@ -477,8 +483,9 @@ public final class DocumentIntelligenceClientImpl { + private Response analyzeDocumentWithResponse(String modelId, BinaryData analyzeRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return service.analyzeDocumentSync(this.getEndpoint(), this.getServiceVersion().getVersion(), modelId, +- contentType, analyzeRequest, requestOptions, Context.NONE); ++ contentType, accept, analyzeRequest, requestOptions, Context.NONE); + } + + /** +@@ -842,8 +849,9 @@ public final class DocumentIntelligenceClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteAnalyzeResultWithResponseAsync(String modelId, String resultId, + RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteAnalyzeResult(this.getEndpoint(), +- this.getServiceVersion().getVersion(), modelId, resultId, requestOptions, context)); ++ this.getServiceVersion().getVersion(), modelId, resultId, accept, requestOptions, context)); + } + + /** +@@ -861,8 +869,9 @@ public final class DocumentIntelligenceClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteAnalyzeResultWithResponse(String modelId, String resultId, + RequestOptions requestOptions) { ++ final String accept = "application/json"; + return service.deleteAnalyzeResultSync(this.getEndpoint(), this.getServiceVersion().getVersion(), modelId, +- resultId, requestOptions, Context.NONE); ++ resultId, accept, requestOptions, Context.NONE); + } + + /** +@@ -920,8 +929,10 @@ public final class DocumentIntelligenceClientImpl { + private Mono> analyzeBatchDocumentsWithResponseAsync(String modelId, BinaryData analyzeBatchRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; +- return FluxUtil.withContext(context -> service.analyzeBatchDocuments(this.getEndpoint(), +- this.getServiceVersion().getVersion(), modelId, contentType, analyzeBatchRequest, requestOptions, context)); ++ final String accept = "application/json"; ++ return FluxUtil.withContext( ++ context -> service.analyzeBatchDocuments(this.getEndpoint(), this.getServiceVersion().getVersion(), modelId, ++ contentType, accept, analyzeBatchRequest, requestOptions, context)); + } + + /** +@@ -979,8 +990,9 @@ public final class DocumentIntelligenceClientImpl { + private Response analyzeBatchDocumentsWithResponse(String modelId, BinaryData analyzeBatchRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return service.analyzeBatchDocumentsSync(this.getEndpoint(), this.getServiceVersion().getVersion(), modelId, +- contentType, analyzeBatchRequest, requestOptions, Context.NONE); ++ contentType, accept, analyzeBatchRequest, requestOptions, Context.NONE); + } + + /** +@@ -1507,8 +1519,9 @@ public final class DocumentIntelligenceClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteAnalyzeBatchResultWithResponseAsync(String modelId, String resultId, + RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteAnalyzeBatchResult(this.getEndpoint(), +- this.getServiceVersion().getVersion(), modelId, resultId, requestOptions, context)); ++ this.getServiceVersion().getVersion(), modelId, resultId, accept, requestOptions, context)); + } + + /** +@@ -1526,8 +1539,9 @@ public final class DocumentIntelligenceClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteAnalyzeBatchResultWithResponse(String modelId, String resultId, + RequestOptions requestOptions) { ++ final String accept = "application/json"; + return service.deleteAnalyzeBatchResultSync(this.getEndpoint(), this.getServiceVersion().getVersion(), modelId, +- resultId, requestOptions, Context.NONE); ++ resultId, accept, requestOptions, Context.NONE); + } + + /** +@@ -1686,9 +1700,10 @@ public final class DocumentIntelligenceClientImpl { + private Mono> classifyDocumentWithResponseAsync(String classifierId, BinaryData classifyRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return FluxUtil + .withContext(context -> service.classifyDocument(this.getEndpoint(), this.getServiceVersion().getVersion(), +- classifierId, contentType, classifyRequest, requestOptions, context)); ++ classifierId, contentType, accept, classifyRequest, requestOptions, context)); + } + + /** +@@ -1728,8 +1743,9 @@ public final class DocumentIntelligenceClientImpl { + private Response classifyDocumentWithResponse(String classifierId, BinaryData classifyRequest, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return service.classifyDocumentSync(this.getEndpoint(), this.getServiceVersion().getVersion(), classifierId, +- contentType, classifyRequest, requestOptions, Context.NONE); ++ contentType, accept, classifyRequest, requestOptions, Context.NONE); + } + + /** +-- +2.50.1.windows.1 + diff --git a/eng/pipelines/scripts/patches/0001-revert-impl-in-easm.patch b/eng/pipelines/scripts/patches/0001-revert-impl-in-easm.patch new file mode 100644 index 000000000000..0ff7844e6d57 --- /dev/null +++ b/eng/pipelines/scripts/patches/0001-revert-impl-in-easm.patch @@ -0,0 +1,135 @@ +From ee9efb32a58711f5bcb4667e4ae9bea70be2e138 Mon Sep 17 00:00:00 2001 +From: Weidong Xu +Date: Thu, 7 Aug 2025 13:47:27 +0800 +Subject: [PATCH] revert impl in easm + +--- + .../easm/implementation/EasmClientImpl.java | 30 +++++++++++-------- + 1 file changed, 18 insertions(+), 12 deletions(-) + +diff --git a/sdk/easm/azure-analytics-defender-easm/src/main/java/com/azure/analytics/defender/easm/implementation/EasmClientImpl.java b/sdk/easm/azure-analytics-defender-easm/src/main/java/com/azure/analytics/defender/easm/implementation/EasmClientImpl.java +index 74965f145fd..cd574da24ef 100644 +--- a/sdk/easm/azure-analytics-defender-easm/src/main/java/com/azure/analytics/defender/easm/implementation/EasmClientImpl.java ++++ b/sdk/easm/azure-analytics-defender-easm/src/main/java/com/azure/analytics/defender/easm/implementation/EasmClientImpl.java +@@ -315,7 +315,7 @@ public final class EasmClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteDataConnection(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("dataConnectionName") String dataConnectionName, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Delete("/dataConnections/{dataConnectionName}") + @ExpectedResponses({ 204 }) +@@ -325,7 +325,7 @@ public final class EasmClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteDataConnectionSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("dataConnectionName") String dataConnectionName, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Get("/discoGroups") + @ExpectedResponses({ 200 }) +@@ -419,7 +419,7 @@ public final class EasmClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> runDiscoGroup(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("groupName") String groupName, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Post("/discoGroups/{groupName}:run") + @ExpectedResponses({ 204 }) +@@ -429,7 +429,7 @@ public final class EasmClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response runDiscoGroupSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("groupName") String groupName, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Get("/discoGroups/{groupName}/runs") + @ExpectedResponses({ 200 }) +@@ -625,7 +625,7 @@ public final class EasmClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> deleteSavedFilter(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("filterName") String filterName, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Delete("/savedFilters/{filterName}") + @ExpectedResponses({ 204 }) +@@ -635,7 +635,7 @@ public final class EasmClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response deleteSavedFilterSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("filterName") String filterName, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context); + + @Get("/tasks") + @ExpectedResponses({ 200 }) +@@ -1886,8 +1886,9 @@ public final class EasmClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteDataConnectionWithResponseAsync(String dataConnectionName, + RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteDataConnection(this.getEndpoint(), +- this.getServiceVersion().getVersion(), dataConnectionName, requestOptions, context)); ++ this.getServiceVersion().getVersion(), dataConnectionName, accept, requestOptions, context)); + } + + /** +@@ -1903,8 +1904,9 @@ public final class EasmClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteDataConnectionWithResponse(String dataConnectionName, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return service.deleteDataConnectionSync(this.getEndpoint(), this.getServiceVersion().getVersion(), +- dataConnectionName, requestOptions, Context.NONE); ++ dataConnectionName, accept, requestOptions, Context.NONE); + } + + /** +@@ -2696,8 +2698,9 @@ public final class EasmClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> runDiscoGroupWithResponseAsync(String groupName, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.runDiscoGroup(this.getEndpoint(), +- this.getServiceVersion().getVersion(), groupName, requestOptions, context)); ++ this.getServiceVersion().getVersion(), groupName, accept, requestOptions, context)); + } + + /** +@@ -2713,7 +2716,8 @@ public final class EasmClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response runDiscoGroupWithResponse(String groupName, RequestOptions requestOptions) { +- return service.runDiscoGroupSync(this.getEndpoint(), this.getServiceVersion().getVersion(), groupName, ++ final String accept = "application/json"; ++ return service.runDiscoGroupSync(this.getEndpoint(), this.getServiceVersion().getVersion(), groupName, accept, + requestOptions, Context.NONE); + } + +@@ -4053,8 +4057,9 @@ public final class EasmClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteSavedFilterWithResponseAsync(String filterName, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.deleteSavedFilter(this.getEndpoint(), +- this.getServiceVersion().getVersion(), filterName, requestOptions, context)); ++ this.getServiceVersion().getVersion(), filterName, accept, requestOptions, context)); + } + + /** +@@ -4070,8 +4075,9 @@ public final class EasmClientImpl { + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteSavedFilterWithResponse(String filterName, RequestOptions requestOptions) { ++ final String accept = "application/json"; + return service.deleteSavedFilterSync(this.getEndpoint(), this.getServiceVersion().getVersion(), filterName, +- requestOptions, Context.NONE); ++ accept, requestOptions, Context.NONE); + } + + /** +-- +2.50.1.windows.1 + diff --git a/eng/pipelines/scripts/patches/0001-revert-impl-in-monitor.patch b/eng/pipelines/scripts/patches/0001-revert-impl-in-monitor.patch new file mode 100644 index 000000000000..f54f516b672e --- /dev/null +++ b/eng/pipelines/scripts/patches/0001-revert-impl-in-monitor.patch @@ -0,0 +1,58 @@ +From d767417fc77ec1a5754ea6c973c258acab2cc9a6 Mon Sep 17 00:00:00 2001 +From: Weidong Xu +Date: Thu, 7 Aug 2025 12:44:57 +0800 +Subject: [PATCH] revert impl in monitor + +--- + .../implementation/LogsIngestionClientImpl.java | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/sdk/monitor/azure-monitor-ingestion/src/main/java/com/azure/monitor/ingestion/implementation/LogsIngestionClientImpl.java b/sdk/monitor/azure-monitor-ingestion/src/main/java/com/azure/monitor/ingestion/implementation/LogsIngestionClientImpl.java +index 1d513291f2b..7e7a7ed1484 100644 +--- a/sdk/monitor/azure-monitor-ingestion/src/main/java/com/azure/monitor/ingestion/implementation/LogsIngestionClientImpl.java ++++ b/sdk/monitor/azure-monitor-ingestion/src/main/java/com/azure/monitor/ingestion/implementation/LogsIngestionClientImpl.java +@@ -161,7 +161,8 @@ public final class LogsIngestionClientImpl { + Mono> upload(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @PathParam("ruleId") String ruleId, + @PathParam("stream") String streamName, @HeaderParam("Content-Type") String contentType, +- @BodyParam("application/json") BinaryData body, RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, @BodyParam("application/json") BinaryData body, ++ RequestOptions requestOptions, Context context); + + @Post("/dataCollectionRules/{ruleId}/streams/{stream}") + @ExpectedResponses({ 204 }) +@@ -171,8 +172,8 @@ public final class LogsIngestionClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response uploadSync(@HostParam("endpoint") String endpoint, @QueryParam("api-version") String apiVersion, + @PathParam("ruleId") String ruleId, @PathParam("stream") String streamName, +- @HeaderParam("Content-Type") String contentType, @BodyParam("application/json") BinaryData body, +- RequestOptions requestOptions, Context context); ++ @HeaderParam("Content-Type") String contentType, @HeaderParam("Accept") String accept, ++ @BodyParam("application/json") BinaryData body, RequestOptions requestOptions, Context context); + } + + /** +@@ -211,8 +212,9 @@ public final class LogsIngestionClientImpl { + public Mono> uploadWithResponseAsync(String ruleId, String streamName, BinaryData body, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.upload(this.getEndpoint(), this.getServiceVersion().getVersion(), +- ruleId, streamName, contentType, body, requestOptions, context)); ++ ruleId, streamName, contentType, accept, body, requestOptions, context)); + } + + /** +@@ -251,7 +253,8 @@ public final class LogsIngestionClientImpl { + public Response uploadWithResponse(String ruleId, String streamName, BinaryData body, + RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return service.uploadSync(this.getEndpoint(), this.getServiceVersion().getVersion(), ruleId, streamName, +- contentType, body, requestOptions, Context.NONE); ++ contentType, accept, body, requestOptions, Context.NONE); + } + } +-- +2.50.1.windows.1 + diff --git a/eng/pipelines/scripts/patches/0001-revert-impl-in-translation.patch b/eng/pipelines/scripts/patches/0001-revert-impl-in-translation.patch new file mode 100644 index 000000000000..1a561ced9a94 --- /dev/null +++ b/eng/pipelines/scripts/patches/0001-revert-impl-in-translation.patch @@ -0,0 +1,59 @@ +From 67cd3515137238ae3c69008d0a65ec74af3338e9 Mon Sep 17 00:00:00 2001 +From: Weidong Xu +Date: Thu, 7 Aug 2025 12:13:55 +0800 +Subject: [PATCH] revert impl in translation + +--- + .../DocumentTranslationClientImpl.java | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/sdk/translation/azure-ai-translation-document/src/main/java/com/azure/ai/translation/document/implementation/DocumentTranslationClientImpl.java b/sdk/translation/azure-ai-translation-document/src/main/java/com/azure/ai/translation/document/implementation/DocumentTranslationClientImpl.java +index 9fb5a9e6fc8..cefc7bff313 100644 +--- a/sdk/translation/azure-ai-translation-document/src/main/java/com/azure/ai/translation/document/implementation/DocumentTranslationClientImpl.java ++++ b/sdk/translation/azure-ai-translation-document/src/main/java/com/azure/ai/translation/document/implementation/DocumentTranslationClientImpl.java +@@ -179,7 +179,8 @@ public final class DocumentTranslationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> translation(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @HeaderParam("Content-Type") String contentType, +- @BodyParam("application/json") BinaryData body, RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, @BodyParam("application/json") BinaryData body, ++ RequestOptions requestOptions, Context context); + + @Post("/document/batches") + @ExpectedResponses({ 202 }) +@@ -189,7 +190,8 @@ public final class DocumentTranslationClientImpl { + @UnexpectedResponseExceptionType(HttpResponseException.class) + Response translationSync(@HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, @HeaderParam("Content-Type") String contentType, +- @BodyParam("application/json") BinaryData body, RequestOptions requestOptions, Context context); ++ @HeaderParam("Accept") String accept, @BodyParam("application/json") BinaryData body, ++ RequestOptions requestOptions, Context context); + + @Get("/document/batches") + @ExpectedResponses({ 200 }) +@@ -426,8 +428,9 @@ public final class DocumentTranslationClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + private Mono> translationWithResponseAsync(BinaryData body, RequestOptions requestOptions) { + final String contentType = "application/json"; ++ final String accept = "application/json"; + return FluxUtil.withContext(context -> service.translation(this.getEndpoint(), +- this.getServiceVersion().getVersion(), contentType, body, requestOptions, context)); ++ this.getServiceVersion().getVersion(), contentType, accept, body, requestOptions, context)); + } + + /** +@@ -502,8 +505,9 @@ public final class DocumentTranslationClientImpl { + @ServiceMethod(returns = ReturnType.SINGLE) + private Response translationWithResponse(BinaryData body, RequestOptions requestOptions) { + final String contentType = "application/json"; +- return service.translationSync(this.getEndpoint(), this.getServiceVersion().getVersion(), contentType, body, +- requestOptions, Context.NONE); ++ final String accept = "application/json"; ++ return service.translationSync(this.getEndpoint(), this.getServiceVersion().getVersion(), contentType, accept, ++ body, requestOptions, Context.NONE); + } + + /** +-- +2.50.1.windows.1 + diff --git a/eng/pipelines/scripts/sync_sdk.py b/eng/pipelines/scripts/sync_sdk.py new file mode 100644 index 000000000000..0f67ff979f18 --- /dev/null +++ b/eng/pipelines/scripts/sync_sdk.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python3 + +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import os +import sys +import logging +import argparse +import subprocess +import glob +import shutil +import json +from typing import List + +sdk_root: str +# script is in "eng/pipelines/scripts/" +script_root: str = os.path.dirname(os.path.realpath(__file__)) + +skip_artifacts: List[str] = [ + "azure-ai-anomalydetector", # deprecated + # expect failure on below + # "azure-developer-devcenter", # 2 breaks introduced into stable api-version + # "azure-ai-vision-face", # SDK in development +] + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument( + "--sdk-root", + type=str, + required=True, + help="azure-sdk-for-java repository root.", + ) + parser.add_argument( + "--package-json-path", + type=str, + required=True, + help="path to package.json of typespec-java.", + ) + parser.add_argument( + "--dev-package", + type=str, + required=False, + default="false", + help="use build from the branch, instead of published typespec-java.", + ) + return parser.parse_args() + + +def update_emitter(package_json_path: str, use_dev_package: bool): + if use_dev_package: + # we cannot use "tsp-client generate-config-files" in dev mode, as this command also updates the lock file + logging.info("Update emitter-package.json") + subprocess.check_call( + [ + "pwsh", + "./eng/common/scripts/typespec/New-EmitterPackageJson.ps1", + "-PackageJsonPath", + package_json_path, + "-OutputDirectory", + "eng", + ], + cwd=sdk_root, + ) + + # replace version with path to dev package + dev_package_path = None + typespec_extension_path = os.path.dirname(package_json_path) + for file in os.listdir(typespec_extension_path): + if file.endswith(".tgz"): + dev_package_path = os.path.abspath(os.path.join(typespec_extension_path, file)) + logging.info(f'Found dev package at "{dev_package_path}"') + break + if dev_package_path: + emitter_package_path = os.path.join(sdk_root, "eng", "emitter-package.json") + with open(emitter_package_path, "r") as json_file: + package_json = json.load(json_file) + package_json["dependencies"]["@azure-tools/typespec-java"] = dev_package_path + with open(emitter_package_path, "w") as json_file: + logging.info(f'Update emitter-package.json to use typespec-java from "{dev_package_path}"') + json.dump(package_json, json_file, indent=2) + else: + logging.error("Failed to locate the dev package.") + + # only enable it on dev branch + # update_latest_dev() + + logging.info("Update emitter-package-lock.json") + generate_lock_file() + else: + logging.info("Update emitter-package.json and emitter-package-lock.json") + subprocess.check_call( + ["tsp-client", "generate-config-files", "--package-json", package_json_path], cwd=sdk_root + ) + + +def update_latest_dev(): + subprocess.check_call( + ["npx", "-y", "@azure-tools/typespec-bump-deps", "eng/emitter-package.json", "--add-npm-overrides"], + cwd=sdk_root, + ) + + +def generate_lock_file(): + subprocess.check_call(["tsp-client", "generate-lock-file"], cwd=sdk_root) + + +def get_generated_folder_from_artifact(module_path: str, artifact: str, type: str) -> str: + path = os.path.join(module_path, "src", type, "java", "com") + for seg in artifact.split("-"): + path = os.path.join(path, seg) + path = os.path.join(path, "generated") + return path + + +def update_sdks(): + failed_modules = [] + for tsp_location_file in glob.glob(os.path.join(sdk_root, "sdk/*/*/tsp-location.yaml")): + module_path = os.path.dirname(tsp_location_file) + artifact = os.path.basename(module_path) + + arm_module = "-resourcemanager-" in artifact + + if artifact in skip_artifacts: + continue + + # # update commit ID for ARM module + # commit_id = "3c15c2f8c50fb3130b34887d29442da75f07fefb" + # if commit_id and arm_module: + # with open(tsp_location_file, "r", encoding="utf-8") as f_in: + # lines = f_in.readlines() + # lines_out = [] + # for line in lines: + # if line.startswith("commit:"): + # line = f"commit: {commit_id}\n" + # lines_out.append(line) + # with open(tsp_location_file, "w", encoding="utf-8") as f_out: + # f_out.writelines(lines_out) + + # logging.info("Updated tsp-location file content:\n%s", "".join(lines_out)) + + if os.path.dirname(module_path).endswith("-v2"): + # skip modules on azure-core-v2 + logging.info(f"Skip azure-core-v2 module on path {module_path}") + continue + + generated_samples_path = os.path.join( + module_path, get_generated_folder_from_artifact(module_path, artifact, "samples") + ) + generated_test_path = os.path.join( + module_path, get_generated_folder_from_artifact(module_path, artifact, "test") + ) + generated_samples_exists = os.path.isdir(generated_samples_path) + generated_test_exists = os.path.isdir(generated_test_path) + + if arm_module: + logging.info("Delete generated source code of resourcemanager module %s", artifact) + shutil.rmtree(os.path.join(module_path, "src", "main", "resources"), ignore_errors=True) + delete_generated_source_code(os.path.join(module_path, "src", "main", "java")) + + logging.info(f"Generate for module {artifact}") + try: + subprocess.check_call(["tsp-client", "update"], cwd=module_path) + except subprocess.CalledProcessError: + # one retry + # sometimes customization have intermittent failure + logging.warning(f"Retry generate for module {artifact}") + try: + subprocess.check_call(["tsp-client", "update", "--debug"], cwd=module_path) + except subprocess.CalledProcessError: + logging.error(f"Failed to generate for module {artifact}") + failed_modules.append(artifact) + + if not arm_module: + # run mvn package, as this is what's done in "TypeSpec-Compare-CurrentToCodegeneration.ps1" script + try: + subprocess.check_call( + ["mvn", "--no-transfer-progress", "codesnippet:update-codesnippet"], cwd=module_path + ) + except subprocess.CalledProcessError: + logging.error(f"Failed to update code snippet for module {artifact}") + failed_modules.append(artifact) + + if arm_module: + # revert mock test code + cmd = ["git", "checkout", "src/test"] + subprocess.check_call(cmd, cwd=module_path) + + # For ARM module, we want to keep the generated samples code. + # For data-plane, if the generated samples/test code is not there before generation, we will delete the generated code after generation, to avoid unnecessary code check-in. + if not generated_samples_exists and not arm_module: + shutil.rmtree(generated_samples_path, ignore_errors=True) + if not generated_test_exists: + shutil.rmtree(generated_test_path, ignore_errors=True) + + # revert change on pom.xml, readme.md, changelog.md, etc. + cmd = ["git", "checkout", "**/pom.xml"] + subprocess.check_call(cmd, cwd=sdk_root) + cmd = ["git", "checkout", "**/*.md"] + subprocess.check_call(cmd, cwd=sdk_root) + + # temporary, revert change on metadata.json + cmd = ["git", "checkout", "**/*_metadata.json"] + subprocess.check_call(cmd, cwd=sdk_root) + + cmd = ["git", "add", "."] + subprocess.check_call(cmd, cwd=sdk_root) + + if failed_modules: + logging.error(f"Failed modules {failed_modules}") + + +def apply_patches() -> None: + failed_patches = [] + for patch_file in glob.glob(os.path.join(script_root, "patches/*.patch")): + try: + subprocess.check_call(["git", "apply", patch_file, "--ignore-whitespace"], cwd=sdk_root) + except subprocess.CalledProcessError: + logging.error(f"Failed to apply patch {patch_file}") + failed_patches.append(patch_file) + + if failed_patches: + logging.error(f"Failed patches {failed_patches}") + + +def delete_generated_source_code(path: str) -> None: + autorest_generated_header = "Code generated by Microsoft (R) AutoRest Code Generator" + typespec_generated_header = "Code generated by Microsoft (R) TypeSpec Code Generator" + if os.path.exists(path): + for file in os.listdir(path): + cur_path = os.path.join(path, file) + if os.path.isdir(cur_path): + # Recurse into subdirectory + delete_generated_source_code(cur_path) + else: + try: + # Read file content and check for header + with open(cur_path, "r", encoding="utf-8") as f: + content = f.read() + if autorest_generated_header in content or typespec_generated_header in content: + os.remove(cur_path) # Delete the file + except Exception as e: + # Skip files that can't be read (binary files, permission issues) + print(f"Warning: Could not process file {cur_path}: {e}") + continue + + +def main(): + global sdk_root + + args = vars(parse_args()) + sdk_root = args["sdk_root"] + + update_emitter(args["package_json_path"], args["dev_package"].lower() == "true") + + update_sdks() + + apply_patches() + + +if __name__ == "__main__": + logging.basicConfig( + stream=sys.stdout, + level=logging.INFO, + format="%(asctime)s %(levelname)s %(message)s", + datefmt="%Y-%m-%d %X", + ) + main() From dd2e7c0b0a22d6e2fd8ceb0c1e44387755e0de8b Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Wed, 8 Jul 2026 17:22:03 +0800 Subject: [PATCH 02/18] Simplify TypeSpecJavaPRId parameter displayName Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/post-publish-sdk.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/eng/pipelines/post-publish-sdk.yaml b/eng/pipelines/post-publish-sdk.yaml index e600d9041676..d2a6a47ee229 100644 --- a/eng/pipelines/post-publish-sdk.yaml +++ b/eng/pipelines/post-publish-sdk.yaml @@ -11,10 +11,7 @@ pr: none parameters: - name: TypeSpecJavaPRId - displayName: >- - Pull Request in the Azure/typespec-azure repo to build the typespec-java emitter from. - Accepts a PR number (e.g. 43312) or a full ref path (e.g. refs/pull/43312/merge). - Leave empty to use the published @azure-tools/typespec-java npm package. + displayName: 'typespec-azure PR to build emitter from (number or ref; empty = published package)' type: string default: '' From 813a81fb0b227e99236d0d3bec2510f58717a121 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Wed, 8 Jul 2026 17:25:41 +0800 Subject: [PATCH 03/18] Update TypeSpecJavaPRId parameter displayName Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/post-publish-sdk.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/pipelines/post-publish-sdk.yaml b/eng/pipelines/post-publish-sdk.yaml index d2a6a47ee229..93361a678737 100644 --- a/eng/pipelines/post-publish-sdk.yaml +++ b/eng/pipelines/post-publish-sdk.yaml @@ -11,7 +11,7 @@ pr: none parameters: - name: TypeSpecJavaPRId - displayName: 'typespec-azure PR to build emitter from (number or ref; empty = published package)' + displayName: 'Pull Request ID in typespec-azure(e.g. 43321, or refs/pull/43321/merge)' type: string default: '' From 90259c8877ae28eb3f1625639e6536b0d7dc2dbd Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Wed, 8 Jul 2026 17:32:17 +0800 Subject: [PATCH 04/18] Use DevPackage boolean toggle with sentinel PRId default Replace the single PR-id parameter with a DevPackage boolean (default false) plus a PRId string that defaults to the sentinel 'none' so the run panel never requires input for the default (published-package) path. PRId is validated at runtime only when DevPackage is true. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/post-publish-sdk.yaml | 43 +++++++++++++++++++---------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/eng/pipelines/post-publish-sdk.yaml b/eng/pipelines/post-publish-sdk.yaml index 93361a678737..1a88965cf35c 100644 --- a/eng/pipelines/post-publish-sdk.yaml +++ b/eng/pipelines/post-publish-sdk.yaml @@ -2,18 +2,25 @@ # then opens an automated draft PR with the results. # # The typespec-java emitter lives in Azure/typespec-azure (packages/typespec-java). -# By default this pipeline generates using the published @azure-tools/typespec-java npm -# package. To validate an unreleased emitter change, provide the TypeSpecJavaPRId parameter -# pointing at a Pull Request in the Azure/typespec-azure repository; the emitter dev package -# is then built from that PR and used for generation. +# By default (DevPackage=false) this pipeline generates using the published +# @azure-tools/typespec-java npm package. To validate an unreleased emitter change, set +# DevPackage=true and provide PRId pointing at a Pull Request in the Azure/typespec-azure +# repository; the emitter dev package is then built from that PR and used for generation. trigger: none pr: none parameters: -- name: TypeSpecJavaPRId - displayName: 'Pull Request ID in typespec-azure(e.g. 43321, or refs/pull/43321/merge)' +# NOTE: Azure DevOps cannot conditionally show/hide runtime parameters, so both are always +# shown. DevPackage defaults to false (a boolean toggle needs no value entered); PRId is only +# used when DevPackage=true, in which case it must be provided. +- name: DevPackage + displayName: 'Build emitter from a typespec-azure PR (instead of the published npm package)' + type: boolean + default: false +- name: PRId + displayName: 'Pull Request ID in typespec-azure, only used when DevPackage=true (e.g. 43321, or refs/pull/43321/merge)' type: string - default: '' + default: 'none' jobs: - job: Generate_SDK @@ -29,8 +36,8 @@ jobs: - name: TypeSpecAzureDirectory value: $(Agent.BuildDirectory)/typespec-azure - name: PullRequestTitleSuffix - ${{ if ne(parameters.TypeSpecJavaPRId, '') }}: - value: " DEV (typespec-azure PR ${{ parameters.TypeSpecJavaPRId }})" + ${{ if eq(parameters.DevPackage, true) }}: + value: " DEV (typespec-azure PR ${{ parameters.PRId }})" ${{ else }}: value: "" @@ -58,8 +65,9 @@ jobs: parameters: registryUrl: https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ - # Clone the public Azure/typespec-azure repo (no service connection required). When a PR is - # specified via TypeSpecJavaPRId, fetch and check out that PR ref (number or full ref path). + # Clone the public Azure/typespec-azure repo (no service connection required). When building + # a dev package (DevPackage=true), fetch and check out the specified PR ref. PRId must be set + # in that case (a bare number is expanded to refs/pull//merge; a full ref is used as-is). - task: PowerShell@2 displayName: 'Clone typespec-azure' inputs: @@ -67,8 +75,13 @@ jobs: targetType: 'inline' script: | git clone https://github.com/Azure/typespec-azure.git "$(TypeSpecAzureDirectory)" - $prId = '${{ parameters.TypeSpecJavaPRId }}' - if ($prId) { + $devPackage = [System.Convert]::ToBoolean('${{ parameters.DevPackage }}') + $prId = '${{ parameters.PRId }}' + if ($devPackage) { + if (-not $prId -or $prId -eq 'none') { + Write-Error "PRId is required when DevPackage is true." + exit 1 + } if ($prId -match '^\d+$') { $ref = "refs/pull/$prId/merge" } else { @@ -87,7 +100,7 @@ jobs: # the .tgz next to the package.json for sync_sdk.py to pick up. - task: PowerShell@2 retryCountOnTaskFailure: 1 - condition: and(succeeded(), ne('${{ parameters.TypeSpecJavaPRId }}', '')) + condition: and(succeeded(), eq('${{ parameters.DevPackage }}', 'true')) displayName: 'Build typespec-java dev package' inputs: pwsh: true @@ -114,7 +127,7 @@ jobs: workingDirectory: $(TypeSpecAzureDirectory) - script: | - python3 ./eng/pipelines/scripts/sync_sdk.py --sdk-root=$(Build.SourcesDirectory) --package-json-path=$(TypeSpecAzureDirectory)/packages/typespec-java/package.json --dev-package=${{ ne(parameters.TypeSpecJavaPRId, '') }} + python3 ./eng/pipelines/scripts/sync_sdk.py --sdk-root=$(Build.SourcesDirectory) --package-json-path=$(TypeSpecAzureDirectory)/packages/typespec-java/package.json --dev-package=${{ parameters.DevPackage }} displayName: 'Generate SDK' workingDirectory: $(Build.SourcesDirectory) From 08711baa27c06aa6d4e0790d359e44b987e45121 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Wed, 8 Jul 2026 17:36:35 +0800 Subject: [PATCH 05/18] Build emitter from main when DevPackage=true and no PRId given DevPackage=false uses the published npm package. DevPackage=true builds the emitter from source: from the typespec-azure PR in PRId, or from main when PRId is left at the 'none' sentinel. Drops the previous PRId-required error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/post-publish-sdk.yaml | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/eng/pipelines/post-publish-sdk.yaml b/eng/pipelines/post-publish-sdk.yaml index 1a88965cf35c..89dd945f46c5 100644 --- a/eng/pipelines/post-publish-sdk.yaml +++ b/eng/pipelines/post-publish-sdk.yaml @@ -3,22 +3,22 @@ # # The typespec-java emitter lives in Azure/typespec-azure (packages/typespec-java). # By default (DevPackage=false) this pipeline generates using the published -# @azure-tools/typespec-java npm package. To validate an unreleased emitter change, set -# DevPackage=true and provide PRId pointing at a Pull Request in the Azure/typespec-azure -# repository; the emitter dev package is then built from that PR and used for generation. +# @azure-tools/typespec-java npm package. Set DevPackage=true to instead build the emitter +# dev package from source: from the typespec-azure PR given by PRId, or from the default +# branch (main) when PRId is left at 'none'. trigger: none pr: none parameters: # NOTE: Azure DevOps cannot conditionally show/hide runtime parameters, so both are always -# shown. DevPackage defaults to false (a boolean toggle needs no value entered); PRId is only -# used when DevPackage=true, in which case it must be provided. +# shown. DevPackage defaults to false (a boolean toggle needs no value entered). PRId is only +# used when DevPackage=true; leave it at 'none' to build the emitter from main. - name: DevPackage - displayName: 'Build emitter from a typespec-azure PR (instead of the published npm package)' + displayName: 'Build emitter from typespec-azure source (instead of the published npm package)' type: boolean default: false - name: PRId - displayName: 'Pull Request ID in typespec-azure, only used when DevPackage=true (e.g. 43321, or refs/pull/43321/merge)' + displayName: 'typespec-azure Pull Request to build the emitter from, used only when DevPackage=true (e.g. 43321 or refs/pull/43321/merge; leave as none to build from main)' type: string default: 'none' @@ -37,7 +37,10 @@ jobs: value: $(Agent.BuildDirectory)/typespec-azure - name: PullRequestTitleSuffix ${{ if eq(parameters.DevPackage, true) }}: - value: " DEV (typespec-azure PR ${{ parameters.PRId }})" + ${{ if ne(parameters.PRId, 'none') }}: + value: " DEV (typespec-azure PR ${{ parameters.PRId }})" + ${{ else }}: + value: " DEV (typespec-azure main)" ${{ else }}: value: "" @@ -66,8 +69,9 @@ jobs: registryUrl: https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ # Clone the public Azure/typespec-azure repo (no service connection required). When building - # a dev package (DevPackage=true), fetch and check out the specified PR ref. PRId must be set - # in that case (a bare number is expanded to refs/pull//merge; a full ref is used as-is). + # a dev package (DevPackage=true) with a PRId specified, fetch and check out that PR ref + # (a bare number is expanded to refs/pull//merge; a full ref is used as-is). When PRId is + # left at 'none', the emitter is built from the default branch (main). - task: PowerShell@2 displayName: 'Clone typespec-azure' inputs: @@ -77,11 +81,7 @@ jobs: git clone https://github.com/Azure/typespec-azure.git "$(TypeSpecAzureDirectory)" $devPackage = [System.Convert]::ToBoolean('${{ parameters.DevPackage }}') $prId = '${{ parameters.PRId }}' - if ($devPackage) { - if (-not $prId -or $prId -eq 'none') { - Write-Error "PRId is required when DevPackage is true." - exit 1 - } + if ($devPackage -and $prId -and $prId -ne 'none') { if ($prId -match '^\d+$') { $ref = "refs/pull/$prId/merge" } else { From 9438cc5ed462d09a514ee836fe9f97e80ee69239 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Wed, 8 Jul 2026 17:40:02 +0800 Subject: [PATCH 06/18] Shorten parameter displayNames Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/post-publish-sdk.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/post-publish-sdk.yaml b/eng/pipelines/post-publish-sdk.yaml index 89dd945f46c5..10fb2e543dcb 100644 --- a/eng/pipelines/post-publish-sdk.yaml +++ b/eng/pipelines/post-publish-sdk.yaml @@ -14,11 +14,11 @@ parameters: # shown. DevPackage defaults to false (a boolean toggle needs no value entered). PRId is only # used when DevPackage=true; leave it at 'none' to build the emitter from main. - name: DevPackage - displayName: 'Build emitter from typespec-azure source (instead of the published npm package)' + displayName: 'DevPackage' type: boolean default: false - name: PRId - displayName: 'typespec-azure Pull Request to build the emitter from, used only when DevPackage=true (e.g. 43321 or refs/pull/43321/merge; leave as none to build from main)' + displayName: 'typespec-azure PR ID (e.g. 43321; used only when DevPackage=true, none = main)' type: string default: 'none' From 7896b122ee4df5d5bea9bba59b79270ec6b77e9f Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Wed, 8 Jul 2026 17:48:21 +0800 Subject: [PATCH 07/18] Init typespec-azure submodules for dev build The dev build failed because the 'core' submodule (microsoft/typespec) was not checked out: tspd and the http-client-java generator required by Build-Generator.ps1 live there. Initialize submodules after settling on the ref, only when DevPackage=true. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/post-publish-sdk.yaml | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/eng/pipelines/post-publish-sdk.yaml b/eng/pipelines/post-publish-sdk.yaml index 10fb2e543dcb..41e87abec2b2 100644 --- a/eng/pipelines/post-publish-sdk.yaml +++ b/eng/pipelines/post-publish-sdk.yaml @@ -71,7 +71,10 @@ jobs: # Clone the public Azure/typespec-azure repo (no service connection required). When building # a dev package (DevPackage=true) with a PRId specified, fetch and check out that PR ref # (a bare number is expanded to refs/pull//merge; a full ref is used as-is). When PRId is - # left at 'none', the emitter is built from the default branch (main). + # left at 'none', the emitter is built from the default branch (main). Submodules are then + # initialized because typespec-azure vendors the TypeSpec compiler and the http-client-java + # generator via the 'core' submodule, which build:generator (Build-Generator.ps1) and tspd + # both require. - task: PowerShell@2 displayName: 'Clone typespec-azure' inputs: @@ -81,15 +84,20 @@ jobs: git clone https://github.com/Azure/typespec-azure.git "$(TypeSpecAzureDirectory)" $devPackage = [System.Convert]::ToBoolean('${{ parameters.DevPackage }}') $prId = '${{ parameters.PRId }}' - if ($devPackage -and $prId -and $prId -ne 'none') { - if ($prId -match '^\d+$') { - $ref = "refs/pull/$prId/merge" - } else { - $ref = $prId + if ($devPackage) { + if ($prId -and $prId -ne 'none') { + if ($prId -match '^\d+$') { + $ref = "refs/pull/$prId/merge" + } else { + $ref = $prId + } + Write-Host "Fetching typespec-azure ref $ref" + git -C "$(TypeSpecAzureDirectory)" fetch origin $ref + git -C "$(TypeSpecAzureDirectory)" checkout FETCH_HEAD } - Write-Host "Fetching typespec-azure ref $ref" - git -C "$(TypeSpecAzureDirectory)" fetch origin $ref - git -C "$(TypeSpecAzureDirectory)" checkout FETCH_HEAD + # The 'core' submodule (microsoft/typespec) provides tspd and the http-client-java + # generator, both required by the dev build; init it only when actually building. + git -C "$(TypeSpecAzureDirectory)" submodule update --init --recursive } # Build the emitter dev package (.tgz) from the typespec-azure PR. From ad234fb67149ae92b0dde7ec5a4ead673d9bcf26 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Thu, 9 Jul 2026 10:58:42 +0800 Subject: [PATCH 08/18] Resolve catalog:/workspace: deps for dev emitter package tsp-client generate-lock-file failed with 'Unsupported URL Type catalog:' because New-EmitterPackageJson.ps1 was fed the typespec-azure monorepo source package.json, whose deps use the catalog:/workspace: protocols. Extract the packed tarball's package.json instead (pnpm pack resolves those protocols to concrete versions) and use it as the basis for emitter-package.json. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/scripts/sync_sdk.py | 50 ++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/eng/pipelines/scripts/sync_sdk.py b/eng/pipelines/scripts/sync_sdk.py index 0f67ff979f18..aabe296d42ee 100644 --- a/eng/pipelines/scripts/sync_sdk.py +++ b/eng/pipelines/scripts/sync_sdk.py @@ -11,6 +11,7 @@ import glob import shutil import json +import tarfile from typing import List sdk_root: str @@ -52,13 +53,36 @@ def parse_args() -> argparse.Namespace: def update_emitter(package_json_path: str, use_dev_package: bool): if use_dev_package: # we cannot use "tsp-client generate-config-files" in dev mode, as this command also updates the lock file + + # Locate the dev package tarball produced by "pnpm pack". + dev_package_path = None + typespec_extension_path = os.path.dirname(package_json_path) + for file in os.listdir(typespec_extension_path): + if file.endswith(".tgz"): + dev_package_path = os.path.abspath(os.path.join(typespec_extension_path, file)) + logging.info(f'Found dev package at "{dev_package_path}"') + break + if not dev_package_path: + logging.error("Failed to locate the dev package.") + return + + # The monorepo source package.json (typespec-azure) declares dependencies with the + # "catalog:" and "workspace:" protocols, which npm cannot resolve. The packed tarball + # contains a package.json with those protocols resolved to concrete versions, so use it + # as the basis for emitter-package.json instead of the source package.json. + resolved_package_json_path = os.path.join(typespec_extension_path, "package.resolved.json") + with tarfile.open(dev_package_path, "r:gz") as tar: + with tar.extractfile("package/package.json") as member: + with open(resolved_package_json_path, "wb") as f: + f.write(member.read()) + logging.info("Update emitter-package.json") subprocess.check_call( [ "pwsh", "./eng/common/scripts/typespec/New-EmitterPackageJson.ps1", "-PackageJsonPath", - package_json_path, + resolved_package_json_path, "-OutputDirectory", "eng", ], @@ -66,23 +90,13 @@ def update_emitter(package_json_path: str, use_dev_package: bool): ) # replace version with path to dev package - dev_package_path = None - typespec_extension_path = os.path.dirname(package_json_path) - for file in os.listdir(typespec_extension_path): - if file.endswith(".tgz"): - dev_package_path = os.path.abspath(os.path.join(typespec_extension_path, file)) - logging.info(f'Found dev package at "{dev_package_path}"') - break - if dev_package_path: - emitter_package_path = os.path.join(sdk_root, "eng", "emitter-package.json") - with open(emitter_package_path, "r") as json_file: - package_json = json.load(json_file) - package_json["dependencies"]["@azure-tools/typespec-java"] = dev_package_path - with open(emitter_package_path, "w") as json_file: - logging.info(f'Update emitter-package.json to use typespec-java from "{dev_package_path}"') - json.dump(package_json, json_file, indent=2) - else: - logging.error("Failed to locate the dev package.") + emitter_package_path = os.path.join(sdk_root, "eng", "emitter-package.json") + with open(emitter_package_path, "r") as json_file: + package_json = json.load(json_file) + package_json["dependencies"]["@azure-tools/typespec-java"] = dev_package_path + with open(emitter_package_path, "w") as json_file: + logging.info(f'Update emitter-package.json to use typespec-java from "{dev_package_path}"') + json.dump(package_json, json_file, indent=2) # only enable it on dev branch # update_latest_dev() From e10c21ec4622b9ebdc1f9909207569f84fe68535 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Thu, 9 Jul 2026 14:27:34 +0800 Subject: [PATCH 09/18] Make version-pinned regen the default route (post-publish) The default (DevPackage=false) route now pins eng/emitter-package.json to the published emitter given by TypeSpecJavaVersion, then regenerates - matching the post-publish-sdk intent. sync_sdk.py gains --emitter-version: it npm-packs the published package, seeds emitter-package.json from the tarball's resolved package.json, and regenerates the lock. DevPackage=true still builds from source (PRId or main). The typespec-azure clone/build steps are gated to the dev route, and the default route fails fast if no version is given. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/post-publish-sdk.yaml | 80 ++++++++++++--------- eng/pipelines/scripts/sync_sdk.py | 104 +++++++++++++++++++--------- 2 files changed, 121 insertions(+), 63 deletions(-) diff --git a/eng/pipelines/post-publish-sdk.yaml b/eng/pipelines/post-publish-sdk.yaml index 41e87abec2b2..bba1d328513d 100644 --- a/eng/pipelines/post-publish-sdk.yaml +++ b/eng/pipelines/post-publish-sdk.yaml @@ -2,19 +2,24 @@ # then opens an automated draft PR with the results. # # The typespec-java emitter lives in Azure/typespec-azure (packages/typespec-java). -# By default (DevPackage=false) this pipeline generates using the published -# @azure-tools/typespec-java npm package. Set DevPackage=true to instead build the emitter -# dev package from source: from the typespec-azure PR given by PRId, or from the default -# branch (main) when PRId is left at 'none'. +# Default route (DevPackage=false): pins eng/emitter-package.json to the published +# @azure-tools/typespec-java version given by TypeSpecJavaVersion, then regenerates. This is +# the post-publish path and does not touch typespec-azure sources. +# Dev route (DevPackage=true): builds the emitter dev package from source instead - from the +# typespec-azure PR given by PRId, or from the default branch (main) when PRId is 'none'. trigger: none pr: none parameters: -# NOTE: Azure DevOps cannot conditionally show/hide runtime parameters, so both are always -# shown. DevPackage defaults to false (a boolean toggle needs no value entered). PRId is only -# used when DevPackage=true; leave it at 'none' to build the emitter from main. +# NOTE: Azure DevOps cannot conditionally show/hide runtime parameters, so all are always +# shown. For the default (post-publish) route, set TypeSpecJavaVersion and leave DevPackage +# unchecked. DevPackage/PRId are only for building the emitter from source. +- name: TypeSpecJavaVersion + displayName: 'Published @azure-tools/typespec-java version to regenerate with (e.g. 0.45.4). Required unless DevPackage=true' + type: string + default: '' - name: DevPackage - displayName: 'DevPackage' + displayName: 'DevPackage (build emitter from typespec-azure source instead of a published version)' type: boolean default: false - name: PRId @@ -32,9 +37,13 @@ jobs: - template: /eng/pipelines/templates/variables/image.yml - name: NodeVersion value: '24.x' - # Local clone target for the (public) Azure/typespec-azure emitter repo. + # Local clone target for the (public) Azure/typespec-azure emitter repo (dev route only). - name: TypeSpecAzureDirectory value: $(Agent.BuildDirectory)/typespec-azure + # For the default route this is the published version. For the dev route it is overwritten at + # runtime from the built emitter's package.json (see 'Get Package Version'). + - name: PackageVersion + value: ${{ parameters.TypeSpecJavaVersion }} - name: PullRequestTitleSuffix ${{ if eq(parameters.DevPackage, true) }}: ${{ if ne(parameters.PRId, 'none') }}: @@ -68,37 +77,43 @@ jobs: parameters: registryUrl: https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ - # Clone the public Azure/typespec-azure repo (no service connection required). When building - # a dev package (DevPackage=true) with a PRId specified, fetch and check out that PR ref - # (a bare number is expanded to refs/pull//merge; a full ref is used as-is). When PRId is - # left at 'none', the emitter is built from the default branch (main). Submodules are then - # initialized because typespec-azure vendors the TypeSpec compiler and the http-client-java - # generator via the 'core' submodule, which build:generator (Build-Generator.ps1) and tspd - # both require. + # Fail fast: the default (post-publish) route requires an explicit published version. + - task: PowerShell@2 + displayName: 'Validate parameters' + condition: and(succeeded(), ne('${{ parameters.DevPackage }}', 'true'), eq('${{ parameters.TypeSpecJavaVersion }}', '')) + inputs: + pwsh: true + targetType: 'inline' + script: | + Write-Error "TypeSpecJavaVersion is required when DevPackage is false." + exit 1 + + # Clone the public Azure/typespec-azure repo (dev route only; no service connection required). + # With a PRId specified, fetch and check out that PR ref (a bare number is expanded to + # refs/pull//merge; a full ref is used as-is). When PRId is 'none', the emitter is built + # from the default branch (main). Submodules are then initialized because typespec-azure + # vendors the TypeSpec compiler and the http-client-java generator via the 'core' submodule, + # which build:generator (Build-Generator.ps1) and tspd both require. - task: PowerShell@2 displayName: 'Clone typespec-azure' + condition: and(succeeded(), eq('${{ parameters.DevPackage }}', 'true')) inputs: pwsh: true targetType: 'inline' script: | git clone https://github.com/Azure/typespec-azure.git "$(TypeSpecAzureDirectory)" - $devPackage = [System.Convert]::ToBoolean('${{ parameters.DevPackage }}') $prId = '${{ parameters.PRId }}' - if ($devPackage) { - if ($prId -and $prId -ne 'none') { - if ($prId -match '^\d+$') { - $ref = "refs/pull/$prId/merge" - } else { - $ref = $prId - } - Write-Host "Fetching typespec-azure ref $ref" - git -C "$(TypeSpecAzureDirectory)" fetch origin $ref - git -C "$(TypeSpecAzureDirectory)" checkout FETCH_HEAD + if ($prId -and $prId -ne 'none') { + if ($prId -match '^\d+$') { + $ref = "refs/pull/$prId/merge" + } else { + $ref = $prId } - # The 'core' submodule (microsoft/typespec) provides tspd and the http-client-java - # generator, both required by the dev build; init it only when actually building. - git -C "$(TypeSpecAzureDirectory)" submodule update --init --recursive + Write-Host "Fetching typespec-azure ref $ref" + git -C "$(TypeSpecAzureDirectory)" fetch origin $ref + git -C "$(TypeSpecAzureDirectory)" checkout FETCH_HEAD } + git -C "$(TypeSpecAzureDirectory)" submodule update --init --recursive # Build the emitter dev package (.tgz) from the typespec-azure PR. # typespec-java depends on other workspace packages (workspace:^). First build only those @@ -124,8 +139,11 @@ jobs: npm install -g @azure-tools/typespec-client-generator-cli displayName: 'Install tsp-client' + # Dev route only: the built emitter's version drives the PR title. For the default route + # PackageVersion is already the published TypeSpecJavaVersion. - task: PowerShell@2 displayName: 'Get Package Version' + condition: and(succeeded(), eq('${{ parameters.DevPackage }}', 'true')) inputs: pwsh: true targetType: 'inline' @@ -135,7 +153,7 @@ jobs: workingDirectory: $(TypeSpecAzureDirectory) - script: | - python3 ./eng/pipelines/scripts/sync_sdk.py --sdk-root=$(Build.SourcesDirectory) --package-json-path=$(TypeSpecAzureDirectory)/packages/typespec-java/package.json --dev-package=${{ parameters.DevPackage }} + python3 ./eng/pipelines/scripts/sync_sdk.py --sdk-root=$(Build.SourcesDirectory) --dev-package=${{ parameters.DevPackage }} --emitter-version='${{ parameters.TypeSpecJavaVersion }}' --package-json-path=$(TypeSpecAzureDirectory)/packages/typespec-java/package.json displayName: 'Generate SDK' workingDirectory: $(Build.SourcesDirectory) diff --git a/eng/pipelines/scripts/sync_sdk.py b/eng/pipelines/scripts/sync_sdk.py index aabe296d42ee..d34b497ae805 100644 --- a/eng/pipelines/scripts/sync_sdk.py +++ b/eng/pipelines/scripts/sync_sdk.py @@ -12,6 +12,7 @@ import shutil import json import tarfile +import tempfile from typing import List sdk_root: str @@ -37,22 +38,59 @@ def parse_args() -> argparse.Namespace: parser.add_argument( "--package-json-path", type=str, - required=True, - help="path to package.json of typespec-java.", + required=False, + default="", + help="path to package.json of typespec-java. Required when --dev-package is true.", ) parser.add_argument( "--dev-package", type=str, required=False, default="false", - help="use build from the branch, instead of published typespec-java.", + help="build the emitter from source, instead of using a published typespec-java.", + ) + parser.add_argument( + "--emitter-version", + type=str, + required=False, + default="", + help="published @azure-tools/typespec-java version to regenerate with. " + "Required (default route) unless --dev-package is true.", ) return parser.parse_args() -def update_emitter(package_json_path: str, use_dev_package: bool): +EMITTER_PACKAGE_NAME = "@azure-tools/typespec-java" + + +def extract_package_json_from_tgz(tgz_path: str, dest_path: str) -> None: + # npm/pnpm pack tarballs place all files under a top-level "package/" directory. The + # package.json inside has the "catalog:"/"workspace:" protocols resolved to concrete + # versions, which is what emitter-package.json needs (npm cannot resolve those protocols). + with tarfile.open(tgz_path, "r:gz") as tar: + with tar.extractfile("package/package.json") as member: + with open(dest_path, "wb") as f: + f.write(member.read()) + + +def generate_emitter_package_json(resolved_package_json_path: str) -> None: + subprocess.check_call( + [ + "pwsh", + "./eng/common/scripts/typespec/New-EmitterPackageJson.ps1", + "-PackageJsonPath", + resolved_package_json_path, + "-OutputDirectory", + "eng", + ], + cwd=sdk_root, + ) + + +def update_emitter(package_json_path: str, use_dev_package: bool, emitter_version: str): if use_dev_package: - # we cannot use "tsp-client generate-config-files" in dev mode, as this command also updates the lock file + # Dev route: build the emitter from source. We cannot use "tsp-client + # generate-config-files" here, as it would resolve against a published version. # Locate the dev package tarball produced by "pnpm pack". dev_package_path = None @@ -66,48 +104,46 @@ def update_emitter(package_json_path: str, use_dev_package: bool): logging.error("Failed to locate the dev package.") return - # The monorepo source package.json (typespec-azure) declares dependencies with the - # "catalog:" and "workspace:" protocols, which npm cannot resolve. The packed tarball - # contains a package.json with those protocols resolved to concrete versions, so use it - # as the basis for emitter-package.json instead of the source package.json. resolved_package_json_path = os.path.join(typespec_extension_path, "package.resolved.json") - with tarfile.open(dev_package_path, "r:gz") as tar: - with tar.extractfile("package/package.json") as member: - with open(resolved_package_json_path, "wb") as f: - f.write(member.read()) + extract_package_json_from_tgz(dev_package_path, resolved_package_json_path) logging.info("Update emitter-package.json") - subprocess.check_call( - [ - "pwsh", - "./eng/common/scripts/typespec/New-EmitterPackageJson.ps1", - "-PackageJsonPath", - resolved_package_json_path, - "-OutputDirectory", - "eng", - ], - cwd=sdk_root, - ) + generate_emitter_package_json(resolved_package_json_path) # replace version with path to dev package emitter_package_path = os.path.join(sdk_root, "eng", "emitter-package.json") with open(emitter_package_path, "r") as json_file: package_json = json.load(json_file) - package_json["dependencies"]["@azure-tools/typespec-java"] = dev_package_path + package_json["dependencies"][EMITTER_PACKAGE_NAME] = dev_package_path with open(emitter_package_path, "w") as json_file: logging.info(f'Update emitter-package.json to use typespec-java from "{dev_package_path}"') json.dump(package_json, json_file, indent=2) - # only enable it on dev branch - # update_latest_dev() + logging.info("Update emitter-package-lock.json") + generate_lock_file() + elif emitter_version: + # Default route (post-publish): pin emitter-package.json to a published version. + logging.info(f"Pin emitter-package.json to published typespec-java {emitter_version}") + with tempfile.TemporaryDirectory() as tmp_dir: + # Download the published tarball so its package.json (with resolved dependency + # versions) can seed emitter-package.json, consistent with the dev route. + subprocess.check_call( + ["npm", "pack", f"{EMITTER_PACKAGE_NAME}@{emitter_version}", "--pack-destination", tmp_dir], + cwd=sdk_root, + ) + tgz_files = glob.glob(os.path.join(tmp_dir, "*.tgz")) + if not tgz_files: + raise RuntimeError(f"Failed to download typespec-java {emitter_version} from npm.") + resolved_package_json_path = os.path.join(tmp_dir, "package.resolved.json") + extract_package_json_from_tgz(tgz_files[0], resolved_package_json_path) + + logging.info("Update emitter-package.json") + generate_emitter_package_json(resolved_package_json_path) logging.info("Update emitter-package-lock.json") generate_lock_file() else: - logging.info("Update emitter-package.json and emitter-package-lock.json") - subprocess.check_call( - ["tsp-client", "generate-config-files", "--package-json", package_json_path], cwd=sdk_root - ) + raise ValueError("Either --emitter-version (default route) or --dev-package must be provided.") def update_latest_dev(): @@ -267,7 +303,11 @@ def main(): args = vars(parse_args()) sdk_root = args["sdk_root"] - update_emitter(args["package_json_path"], args["dev_package"].lower() == "true") + update_emitter( + args["package_json_path"], + args["dev_package"].lower() == "true", + args["emitter_version"], + ) update_sdks() From 377b2d8a7c097022aa08c8ea54cb14ca0dac4412 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Thu, 9 Jul 2026 14:32:31 +0800 Subject: [PATCH 10/18] Drop DevPackage; infer route from TypeSpecJavaVersion The presence of a published version fully determines the route, so DevPackage is redundant. TypeSpecJavaVersion set = published route; empty = build emitter from source (PRId, or main). sync_sdk.py drops --dev-package and infers the route from --emitter-version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/post-publish-sdk.yaml | 50 ++++++++-------------- eng/pipelines/scripts/sync_sdk.py | 65 +++++++++++++---------------- 2 files changed, 46 insertions(+), 69 deletions(-) diff --git a/eng/pipelines/post-publish-sdk.yaml b/eng/pipelines/post-publish-sdk.yaml index bba1d328513d..ff370b006dd8 100644 --- a/eng/pipelines/post-publish-sdk.yaml +++ b/eng/pipelines/post-publish-sdk.yaml @@ -2,28 +2,23 @@ # then opens an automated draft PR with the results. # # The typespec-java emitter lives in Azure/typespec-azure (packages/typespec-java). -# Default route (DevPackage=false): pins eng/emitter-package.json to the published -# @azure-tools/typespec-java version given by TypeSpecJavaVersion, then regenerates. This is -# the post-publish path and does not touch typespec-azure sources. -# Dev route (DevPackage=true): builds the emitter dev package from source instead - from the -# typespec-azure PR given by PRId, or from the default branch (main) when PRId is 'none'. +# Published route (TypeSpecJavaVersion set): pins eng/emitter-package.json to that published +# @azure-tools/typespec-java version, then regenerates. This is the post-publish path and does +# not touch typespec-azure sources. +# Dev route (TypeSpecJavaVersion empty): builds the emitter dev package from source instead - +# from the typespec-azure PR given by PRId, or from the default branch (main) when PRId is 'none'. trigger: none pr: none parameters: -# NOTE: Azure DevOps cannot conditionally show/hide runtime parameters, so all are always -# shown. For the default (post-publish) route, set TypeSpecJavaVersion and leave DevPackage -# unchecked. DevPackage/PRId are only for building the emitter from source. +# Set TypeSpecJavaVersion for the default (post-publish) route. Leave it empty to build the +# emitter from source, in which case PRId selects the typespec-azure PR (or main). - name: TypeSpecJavaVersion - displayName: 'Published @azure-tools/typespec-java version to regenerate with (e.g. 0.45.4). Required unless DevPackage=true' + displayName: 'Published @azure-tools/typespec-java version to regenerate with (e.g. 0.45.4). Leave empty to build the emitter from source' type: string default: '' -- name: DevPackage - displayName: 'DevPackage (build emitter from typespec-azure source instead of a published version)' - type: boolean - default: false - name: PRId - displayName: 'typespec-azure PR ID (e.g. 43321; used only when DevPackage=true, none = main)' + displayName: 'typespec-azure PR ID (e.g. 43321; used only when TypeSpecJavaVersion is empty, none = main)' type: string default: 'none' @@ -40,12 +35,12 @@ jobs: # Local clone target for the (public) Azure/typespec-azure emitter repo (dev route only). - name: TypeSpecAzureDirectory value: $(Agent.BuildDirectory)/typespec-azure - # For the default route this is the published version. For the dev route it is overwritten at + # For the published route this is the given version. For the dev route it is overwritten at # runtime from the built emitter's package.json (see 'Get Package Version'). - name: PackageVersion value: ${{ parameters.TypeSpecJavaVersion }} - name: PullRequestTitleSuffix - ${{ if eq(parameters.DevPackage, true) }}: + ${{ if eq(parameters.TypeSpecJavaVersion, '') }}: ${{ if ne(parameters.PRId, 'none') }}: value: " DEV (typespec-azure PR ${{ parameters.PRId }})" ${{ else }}: @@ -77,17 +72,6 @@ jobs: parameters: registryUrl: https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ - # Fail fast: the default (post-publish) route requires an explicit published version. - - task: PowerShell@2 - displayName: 'Validate parameters' - condition: and(succeeded(), ne('${{ parameters.DevPackage }}', 'true'), eq('${{ parameters.TypeSpecJavaVersion }}', '')) - inputs: - pwsh: true - targetType: 'inline' - script: | - Write-Error "TypeSpecJavaVersion is required when DevPackage is false." - exit 1 - # Clone the public Azure/typespec-azure repo (dev route only; no service connection required). # With a PRId specified, fetch and check out that PR ref (a bare number is expanded to # refs/pull//merge; a full ref is used as-is). When PRId is 'none', the emitter is built @@ -96,7 +80,7 @@ jobs: # which build:generator (Build-Generator.ps1) and tspd both require. - task: PowerShell@2 displayName: 'Clone typespec-azure' - condition: and(succeeded(), eq('${{ parameters.DevPackage }}', 'true')) + condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', '')) inputs: pwsh: true targetType: 'inline' @@ -123,7 +107,7 @@ jobs: # the .tgz next to the package.json for sync_sdk.py to pick up. - task: PowerShell@2 retryCountOnTaskFailure: 1 - condition: and(succeeded(), eq('${{ parameters.DevPackage }}', 'true')) + condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', '')) displayName: 'Build typespec-java dev package' inputs: pwsh: true @@ -139,11 +123,11 @@ jobs: npm install -g @azure-tools/typespec-client-generator-cli displayName: 'Install tsp-client' - # Dev route only: the built emitter's version drives the PR title. For the default route - # PackageVersion is already the published TypeSpecJavaVersion. + # Dev route only: the built emitter's version drives the PR title. For the published route + # PackageVersion is already the given TypeSpecJavaVersion. - task: PowerShell@2 displayName: 'Get Package Version' - condition: and(succeeded(), eq('${{ parameters.DevPackage }}', 'true')) + condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', '')) inputs: pwsh: true targetType: 'inline' @@ -153,7 +137,7 @@ jobs: workingDirectory: $(TypeSpecAzureDirectory) - script: | - python3 ./eng/pipelines/scripts/sync_sdk.py --sdk-root=$(Build.SourcesDirectory) --dev-package=${{ parameters.DevPackage }} --emitter-version='${{ parameters.TypeSpecJavaVersion }}' --package-json-path=$(TypeSpecAzureDirectory)/packages/typespec-java/package.json + python3 ./eng/pipelines/scripts/sync_sdk.py --sdk-root=$(Build.SourcesDirectory) --emitter-version='${{ parameters.TypeSpecJavaVersion }}' --package-json-path=$(TypeSpecAzureDirectory)/packages/typespec-java/package.json displayName: 'Generate SDK' workingDirectory: $(Build.SourcesDirectory) diff --git a/eng/pipelines/scripts/sync_sdk.py b/eng/pipelines/scripts/sync_sdk.py index d34b497ae805..14d1eafe6684 100644 --- a/eng/pipelines/scripts/sync_sdk.py +++ b/eng/pipelines/scripts/sync_sdk.py @@ -40,22 +40,16 @@ def parse_args() -> argparse.Namespace: type=str, required=False, default="", - help="path to package.json of typespec-java. Required when --dev-package is true.", - ) - parser.add_argument( - "--dev-package", - type=str, - required=False, - default="false", - help="build the emitter from source, instead of using a published typespec-java.", + help="path to package.json of typespec-java. Required when --emitter-version is empty " + "(the dev build route).", ) parser.add_argument( "--emitter-version", type=str, required=False, default="", - help="published @azure-tools/typespec-java version to regenerate with. " - "Required (default route) unless --dev-package is true.", + help="published @azure-tools/typespec-java version to regenerate with. When empty, the " + "emitter is built from source (dev route) instead.", ) return parser.parse_args() @@ -87,10 +81,33 @@ def generate_emitter_package_json(resolved_package_json_path: str) -> None: ) -def update_emitter(package_json_path: str, use_dev_package: bool, emitter_version: str): - if use_dev_package: +def update_emitter(package_json_path: str, emitter_version: str): + if emitter_version: + # Published route (post-publish): pin emitter-package.json to a published version. + logging.info(f"Pin emitter-package.json to published typespec-java {emitter_version}") + with tempfile.TemporaryDirectory() as tmp_dir: + # Download the published tarball so its package.json (with resolved dependency + # versions) can seed emitter-package.json, consistent with the dev route. + subprocess.check_call( + ["npm", "pack", f"{EMITTER_PACKAGE_NAME}@{emitter_version}", "--pack-destination", tmp_dir], + cwd=sdk_root, + ) + tgz_files = glob.glob(os.path.join(tmp_dir, "*.tgz")) + if not tgz_files: + raise RuntimeError(f"Failed to download typespec-java {emitter_version} from npm.") + resolved_package_json_path = os.path.join(tmp_dir, "package.resolved.json") + extract_package_json_from_tgz(tgz_files[0], resolved_package_json_path) + + logging.info("Update emitter-package.json") + generate_emitter_package_json(resolved_package_json_path) + + logging.info("Update emitter-package-lock.json") + generate_lock_file() + else: # Dev route: build the emitter from source. We cannot use "tsp-client # generate-config-files" here, as it would resolve against a published version. + if not package_json_path: + raise ValueError("--package-json-path is required when --emitter-version is empty.") # Locate the dev package tarball produced by "pnpm pack". dev_package_path = None @@ -121,29 +138,6 @@ def update_emitter(package_json_path: str, use_dev_package: bool, emitter_versio logging.info("Update emitter-package-lock.json") generate_lock_file() - elif emitter_version: - # Default route (post-publish): pin emitter-package.json to a published version. - logging.info(f"Pin emitter-package.json to published typespec-java {emitter_version}") - with tempfile.TemporaryDirectory() as tmp_dir: - # Download the published tarball so its package.json (with resolved dependency - # versions) can seed emitter-package.json, consistent with the dev route. - subprocess.check_call( - ["npm", "pack", f"{EMITTER_PACKAGE_NAME}@{emitter_version}", "--pack-destination", tmp_dir], - cwd=sdk_root, - ) - tgz_files = glob.glob(os.path.join(tmp_dir, "*.tgz")) - if not tgz_files: - raise RuntimeError(f"Failed to download typespec-java {emitter_version} from npm.") - resolved_package_json_path = os.path.join(tmp_dir, "package.resolved.json") - extract_package_json_from_tgz(tgz_files[0], resolved_package_json_path) - - logging.info("Update emitter-package.json") - generate_emitter_package_json(resolved_package_json_path) - - logging.info("Update emitter-package-lock.json") - generate_lock_file() - else: - raise ValueError("Either --emitter-version (default route) or --dev-package must be provided.") def update_latest_dev(): @@ -305,7 +299,6 @@ def main(): update_emitter( args["package_json_path"], - args["dev_package"].lower() == "true", args["emitter_version"], ) From 7ea6d69259585cd717319275893b217eabdb0451 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Thu, 9 Jul 2026 14:37:07 +0800 Subject: [PATCH 11/18] Default TypeSpecJavaVersion to 'none' sentinel Azure DevOps string parameters cannot be left truly empty in the run UI, so default TypeSpecJavaVersion to the 'none' sentinel (matching PRId). Pipeline conditions compare against 'none'; sync_sdk.py normalizes 'none' to empty and treats it as the dev (build-from-source) route. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/post-publish-sdk.yaml | 25 ++++++++++++++----------- eng/pipelines/scripts/sync_sdk.py | 5 +++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/eng/pipelines/post-publish-sdk.yaml b/eng/pipelines/post-publish-sdk.yaml index ff370b006dd8..62735b005eb8 100644 --- a/eng/pipelines/post-publish-sdk.yaml +++ b/eng/pipelines/post-publish-sdk.yaml @@ -5,20 +5,20 @@ # Published route (TypeSpecJavaVersion set): pins eng/emitter-package.json to that published # @azure-tools/typespec-java version, then regenerates. This is the post-publish path and does # not touch typespec-azure sources. -# Dev route (TypeSpecJavaVersion empty): builds the emitter dev package from source instead - -# from the typespec-azure PR given by PRId, or from the default branch (main) when PRId is 'none'. +# Dev route (TypeSpecJavaVersion is 'none'): builds the emitter dev package from source instead +# - from the typespec-azure PR given by PRId, or from the default branch (main) when PRId is 'none'. trigger: none pr: none parameters: -# Set TypeSpecJavaVersion for the default (post-publish) route. Leave it empty to build the +# Set TypeSpecJavaVersion for the default (post-publish) route. Leave it as 'none' to build the # emitter from source, in which case PRId selects the typespec-azure PR (or main). - name: TypeSpecJavaVersion - displayName: 'Published @azure-tools/typespec-java version to regenerate with (e.g. 0.45.4). Leave empty to build the emitter from source' + displayName: 'Published @azure-tools/typespec-java version to regenerate with (e.g. 0.45.4). Leave as none to build the emitter from source' type: string - default: '' + default: 'none' - name: PRId - displayName: 'typespec-azure PR ID (e.g. 43321; used only when TypeSpecJavaVersion is empty, none = main)' + displayName: 'typespec-azure PR ID (e.g. 43321; used only when TypeSpecJavaVersion is none, none = main)' type: string default: 'none' @@ -38,9 +38,12 @@ jobs: # For the published route this is the given version. For the dev route it is overwritten at # runtime from the built emitter's package.json (see 'Get Package Version'). - name: PackageVersion - value: ${{ parameters.TypeSpecJavaVersion }} + ${{ if ne(parameters.TypeSpecJavaVersion, 'none') }}: + value: ${{ parameters.TypeSpecJavaVersion }} + ${{ else }}: + value: '' - name: PullRequestTitleSuffix - ${{ if eq(parameters.TypeSpecJavaVersion, '') }}: + ${{ if eq(parameters.TypeSpecJavaVersion, 'none') }}: ${{ if ne(parameters.PRId, 'none') }}: value: " DEV (typespec-azure PR ${{ parameters.PRId }})" ${{ else }}: @@ -80,7 +83,7 @@ jobs: # which build:generator (Build-Generator.ps1) and tspd both require. - task: PowerShell@2 displayName: 'Clone typespec-azure' - condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', '')) + condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', 'none')) inputs: pwsh: true targetType: 'inline' @@ -107,7 +110,7 @@ jobs: # the .tgz next to the package.json for sync_sdk.py to pick up. - task: PowerShell@2 retryCountOnTaskFailure: 1 - condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', '')) + condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', 'none')) displayName: 'Build typespec-java dev package' inputs: pwsh: true @@ -127,7 +130,7 @@ jobs: # PackageVersion is already the given TypeSpecJavaVersion. - task: PowerShell@2 displayName: 'Get Package Version' - condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', '')) + condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', 'none')) inputs: pwsh: true targetType: 'inline' diff --git a/eng/pipelines/scripts/sync_sdk.py b/eng/pipelines/scripts/sync_sdk.py index 14d1eafe6684..2c7185d4b4e6 100644 --- a/eng/pipelines/scripts/sync_sdk.py +++ b/eng/pipelines/scripts/sync_sdk.py @@ -82,6 +82,11 @@ def generate_emitter_package_json(resolved_package_json_path: str) -> None: def update_emitter(package_json_path: str, emitter_version: str): + # 'none' is the pipeline sentinel for "not specified" (Azure DevOps string parameters + # cannot be left truly empty in the run UI), so normalize it to empty here. + if emitter_version.lower() == "none": + emitter_version = "" + if emitter_version: # Published route (post-publish): pin emitter-package.json to a published version. logging.info(f"Pin emitter-package.json to published typespec-java {emitter_version}") From fc918d88b68ef7e995b8b9ce84d86cab9aad2794 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Thu, 9 Jul 2026 15:10:04 +0800 Subject: [PATCH 12/18] Refine parameter displayNames (Emitter Version) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/post-publish-sdk.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/post-publish-sdk.yaml b/eng/pipelines/post-publish-sdk.yaml index 62735b005eb8..edddff3b79c8 100644 --- a/eng/pipelines/post-publish-sdk.yaml +++ b/eng/pipelines/post-publish-sdk.yaml @@ -14,11 +14,11 @@ parameters: # Set TypeSpecJavaVersion for the default (post-publish) route. Leave it as 'none' to build the # emitter from source, in which case PRId selects the typespec-azure PR (or main). - name: TypeSpecJavaVersion - displayName: 'Published @azure-tools/typespec-java version to regenerate with (e.g. 0.45.4). Leave as none to build the emitter from source' + displayName: 'Emitter Version — published @azure-tools/typespec-java to regenerate with (e.g. 0.45.4; none = build from source)' type: string default: 'none' - name: PRId - displayName: 'typespec-azure PR ID (e.g. 43321; used only when TypeSpecJavaVersion is none, none = main)' + displayName: 'typespec-azure PR ID — used only when Emitter Version is none (e.g. 43321; none = main)' type: string default: 'none' From 224711833f8ed2d766adcfbb974eb600afbf0a99 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Thu, 9 Jul 2026 15:20:12 +0800 Subject: [PATCH 13/18] Rename to post-publish-emitter.yaml and sync_emitter.py Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../{post-publish-sdk.yaml => post-publish-emitter.yaml} | 4 ++-- eng/pipelines/scripts/{sync_sdk.py => sync_emitter.py} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename eng/pipelines/{post-publish-sdk.yaml => post-publish-emitter.yaml} (95%) rename eng/pipelines/scripts/{sync_sdk.py => sync_emitter.py} (100%) diff --git a/eng/pipelines/post-publish-sdk.yaml b/eng/pipelines/post-publish-emitter.yaml similarity index 95% rename from eng/pipelines/post-publish-sdk.yaml rename to eng/pipelines/post-publish-emitter.yaml index edddff3b79c8..42ca0ea96fb4 100644 --- a/eng/pipelines/post-publish-sdk.yaml +++ b/eng/pipelines/post-publish-emitter.yaml @@ -107,7 +107,7 @@ jobs: # upstream dependencies with turbo (the `^...` filter selects dependencies but excludes # typespec-java itself). Then Build-TypeSpec.ps1 builds and packs typespec-java (its # build:generator step runs Build-Generator.ps1, which needs JDK 11+ and Maven), producing - # the .tgz next to the package.json for sync_sdk.py to pick up. + # the .tgz next to the package.json for sync_emitter.py to pick up. - task: PowerShell@2 retryCountOnTaskFailure: 1 condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', 'none')) @@ -140,7 +140,7 @@ jobs: workingDirectory: $(TypeSpecAzureDirectory) - script: | - python3 ./eng/pipelines/scripts/sync_sdk.py --sdk-root=$(Build.SourcesDirectory) --emitter-version='${{ parameters.TypeSpecJavaVersion }}' --package-json-path=$(TypeSpecAzureDirectory)/packages/typespec-java/package.json + python3 ./eng/pipelines/scripts/sync_emitter.py --sdk-root=$(Build.SourcesDirectory) --emitter-version='${{ parameters.TypeSpecJavaVersion }}' --package-json-path=$(TypeSpecAzureDirectory)/packages/typespec-java/package.json displayName: 'Generate SDK' workingDirectory: $(Build.SourcesDirectory) diff --git a/eng/pipelines/scripts/sync_sdk.py b/eng/pipelines/scripts/sync_emitter.py similarity index 100% rename from eng/pipelines/scripts/sync_sdk.py rename to eng/pipelines/scripts/sync_emitter.py From fc517a55c610f8603b62186492c353b4d13bb1c3 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Thu, 9 Jul 2026 15:23:38 +0800 Subject: [PATCH 14/18] Rename pipeline parameter TypeSpecJavaVersion to EmitterVersion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/post-publish-emitter.yaml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/eng/pipelines/post-publish-emitter.yaml b/eng/pipelines/post-publish-emitter.yaml index 42ca0ea96fb4..291964afaf03 100644 --- a/eng/pipelines/post-publish-emitter.yaml +++ b/eng/pipelines/post-publish-emitter.yaml @@ -2,18 +2,18 @@ # then opens an automated draft PR with the results. # # The typespec-java emitter lives in Azure/typespec-azure (packages/typespec-java). -# Published route (TypeSpecJavaVersion set): pins eng/emitter-package.json to that published +# Published route (EmitterVersion set): pins eng/emitter-package.json to that published # @azure-tools/typespec-java version, then regenerates. This is the post-publish path and does # not touch typespec-azure sources. -# Dev route (TypeSpecJavaVersion is 'none'): builds the emitter dev package from source instead +# Dev route (EmitterVersion is 'none'): builds the emitter dev package from source instead # - from the typespec-azure PR given by PRId, or from the default branch (main) when PRId is 'none'. trigger: none pr: none parameters: -# Set TypeSpecJavaVersion for the default (post-publish) route. Leave it as 'none' to build the +# Set EmitterVersion for the default (post-publish) route. Leave it as 'none' to build the # emitter from source, in which case PRId selects the typespec-azure PR (or main). -- name: TypeSpecJavaVersion +- name: EmitterVersion displayName: 'Emitter Version — published @azure-tools/typespec-java to regenerate with (e.g. 0.45.4; none = build from source)' type: string default: 'none' @@ -38,12 +38,12 @@ jobs: # For the published route this is the given version. For the dev route it is overwritten at # runtime from the built emitter's package.json (see 'Get Package Version'). - name: PackageVersion - ${{ if ne(parameters.TypeSpecJavaVersion, 'none') }}: - value: ${{ parameters.TypeSpecJavaVersion }} + ${{ if ne(parameters.EmitterVersion, 'none') }}: + value: ${{ parameters.EmitterVersion }} ${{ else }}: value: '' - name: PullRequestTitleSuffix - ${{ if eq(parameters.TypeSpecJavaVersion, 'none') }}: + ${{ if eq(parameters.EmitterVersion, 'none') }}: ${{ if ne(parameters.PRId, 'none') }}: value: " DEV (typespec-azure PR ${{ parameters.PRId }})" ${{ else }}: @@ -83,7 +83,7 @@ jobs: # which build:generator (Build-Generator.ps1) and tspd both require. - task: PowerShell@2 displayName: 'Clone typespec-azure' - condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', 'none')) + condition: and(succeeded(), eq('${{ parameters.EmitterVersion }}', 'none')) inputs: pwsh: true targetType: 'inline' @@ -110,7 +110,7 @@ jobs: # the .tgz next to the package.json for sync_emitter.py to pick up. - task: PowerShell@2 retryCountOnTaskFailure: 1 - condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', 'none')) + condition: and(succeeded(), eq('${{ parameters.EmitterVersion }}', 'none')) displayName: 'Build typespec-java dev package' inputs: pwsh: true @@ -127,10 +127,10 @@ jobs: displayName: 'Install tsp-client' # Dev route only: the built emitter's version drives the PR title. For the published route - # PackageVersion is already the given TypeSpecJavaVersion. + # PackageVersion is already the given EmitterVersion. - task: PowerShell@2 displayName: 'Get Package Version' - condition: and(succeeded(), eq('${{ parameters.TypeSpecJavaVersion }}', 'none')) + condition: and(succeeded(), eq('${{ parameters.EmitterVersion }}', 'none')) inputs: pwsh: true targetType: 'inline' @@ -140,7 +140,7 @@ jobs: workingDirectory: $(TypeSpecAzureDirectory) - script: | - python3 ./eng/pipelines/scripts/sync_emitter.py --sdk-root=$(Build.SourcesDirectory) --emitter-version='${{ parameters.TypeSpecJavaVersion }}' --package-json-path=$(TypeSpecAzureDirectory)/packages/typespec-java/package.json + python3 ./eng/pipelines/scripts/sync_emitter.py --sdk-root=$(Build.SourcesDirectory) --emitter-version='${{ parameters.EmitterVersion }}' --package-json-path=$(TypeSpecAzureDirectory)/packages/typespec-java/package.json displayName: 'Generate SDK' workingDirectory: $(Build.SourcesDirectory) From 5de30fca086074ae471092641fba8fd1da1d3064 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Thu, 9 Jul 2026 15:28:23 +0800 Subject: [PATCH 15/18] Use tsp-client generate-config-files for the published route Once the published tarball is downloaded via npm pack, its package.json has resolved dependency versions, so tsp-client generate-config-files can consume it directly to produce both emitter-package.json and its lock file - replacing the separate New-EmitterPackageJson.ps1 + generate-lock-file calls. The dev route still uses those two steps to inject the local .tgz path between them. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/scripts/sync_emitter.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/eng/pipelines/scripts/sync_emitter.py b/eng/pipelines/scripts/sync_emitter.py index 2c7185d4b4e6..a9ac4a610bff 100644 --- a/eng/pipelines/scripts/sync_emitter.py +++ b/eng/pipelines/scripts/sync_emitter.py @@ -91,8 +91,10 @@ def update_emitter(package_json_path: str, emitter_version: str): # Published route (post-publish): pin emitter-package.json to a published version. logging.info(f"Pin emitter-package.json to published typespec-java {emitter_version}") with tempfile.TemporaryDirectory() as tmp_dir: - # Download the published tarball so its package.json (with resolved dependency - # versions) can seed emitter-package.json, consistent with the dev route. + # Download the published tarball. Its package.json has the dependency versions + # resolved (unlike the typespec-azure monorepo source, which uses the "catalog:"/ + # "workspace:" protocols that npm cannot resolve), so tsp-client generate-config-files + # can consume it directly to produce emitter-package.json and its lock file. subprocess.check_call( ["npm", "pack", f"{EMITTER_PACKAGE_NAME}@{emitter_version}", "--pack-destination", tmp_dir], cwd=sdk_root, @@ -103,11 +105,11 @@ def update_emitter(package_json_path: str, emitter_version: str): resolved_package_json_path = os.path.join(tmp_dir, "package.resolved.json") extract_package_json_from_tgz(tgz_files[0], resolved_package_json_path) - logging.info("Update emitter-package.json") - generate_emitter_package_json(resolved_package_json_path) - - logging.info("Update emitter-package-lock.json") - generate_lock_file() + logging.info("Update emitter-package.json and emitter-package-lock.json") + subprocess.check_call( + ["tsp-client", "generate-config-files", "--package-json", resolved_package_json_path], + cwd=sdk_root, + ) else: # Dev route: build the emitter from source. We cannot use "tsp-client # generate-config-files" here, as it would resolve against a published version. From fc1b8815fa4741252f79d17227119c27420b4a86 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Thu, 9 Jul 2026 16:25:04 +0800 Subject: [PATCH 16/18] Pin dev-route emitter deps to exact versions pnpm pack resolves workspace:^ dependencies to caret ranges (e.g. ^0.69.1) in the packed package.json. New-EmitterPackageJson.ps1 pins peer deps from those devDependencies values, so carets leaked into emitter-package.json (^0.69.0 instead of the exact 0.69.2 that autorest.java produced). Strip the leading caret/tilde from the extracted package.json so peer deps are pinned to exact versions. Simple ranges like '>=x --- eng/pipelines/scripts/sync_emitter.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/eng/pipelines/scripts/sync_emitter.py b/eng/pipelines/scripts/sync_emitter.py index a9ac4a610bff..2d1a47a36097 100644 --- a/eng/pipelines/scripts/sync_emitter.py +++ b/eng/pipelines/scripts/sync_emitter.py @@ -13,6 +13,7 @@ import json import tarfile import tempfile +import re from typing import List sdk_root: str @@ -67,6 +68,26 @@ def extract_package_json_from_tgz(tgz_path: str, dest_path: str) -> None: f.write(member.read()) +def pin_exact_versions(package_json_path: str) -> None: + # pnpm pack resolves "workspace:^" dependencies to caret ranges (e.g. "^0.69.1") in the + # packed package.json. New-EmitterPackageJson.ps1 pins each peer dependency to the value in + # devDependencies, so those caret ranges would leak into emitter-package.json. Strip the + # leading range operator so peer dependencies are pinned to exact versions (matching how the + # emitter was published/built). + with open(package_json_path, "r", encoding="utf-8") as f: + package_json = json.load(f) + for section in ("dependencies", "devDependencies", "peerDependencies"): + deps = package_json.get(section) + if not deps: + continue + for name, version in deps.items(): + if isinstance(version, str): + # Turn a simple caret/tilde range (e.g. "^0.69.1", "~1.2.3") into an exact version. + deps[name] = re.sub(r"^[\^~]\s*(?=\d)", "", version) + with open(package_json_path, "w", encoding="utf-8") as f: + json.dump(package_json, f, indent=2) + + def generate_emitter_package_json(resolved_package_json_path: str) -> None: subprocess.check_call( [ @@ -130,6 +151,7 @@ def update_emitter(package_json_path: str, emitter_version: str): resolved_package_json_path = os.path.join(typespec_extension_path, "package.resolved.json") extract_package_json_from_tgz(dev_package_path, resolved_package_json_path) + pin_exact_versions(resolved_package_json_path) logging.info("Update emitter-package.json") generate_emitter_package_json(resolved_package_json_path) From e0e3229207b41856d05a0d128af1daf63125d901 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Thu, 9 Jul 2026 16:26:22 +0800 Subject: [PATCH 17/18] Revert "Pin dev-route emitter deps to exact versions" This reverts commit fc1b8815fa4741252f79d17227119c27420b4a86. --- eng/pipelines/scripts/sync_emitter.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/eng/pipelines/scripts/sync_emitter.py b/eng/pipelines/scripts/sync_emitter.py index 2d1a47a36097..a9ac4a610bff 100644 --- a/eng/pipelines/scripts/sync_emitter.py +++ b/eng/pipelines/scripts/sync_emitter.py @@ -13,7 +13,6 @@ import json import tarfile import tempfile -import re from typing import List sdk_root: str @@ -68,26 +67,6 @@ def extract_package_json_from_tgz(tgz_path: str, dest_path: str) -> None: f.write(member.read()) -def pin_exact_versions(package_json_path: str) -> None: - # pnpm pack resolves "workspace:^" dependencies to caret ranges (e.g. "^0.69.1") in the - # packed package.json. New-EmitterPackageJson.ps1 pins each peer dependency to the value in - # devDependencies, so those caret ranges would leak into emitter-package.json. Strip the - # leading range operator so peer dependencies are pinned to exact versions (matching how the - # emitter was published/built). - with open(package_json_path, "r", encoding="utf-8") as f: - package_json = json.load(f) - for section in ("dependencies", "devDependencies", "peerDependencies"): - deps = package_json.get(section) - if not deps: - continue - for name, version in deps.items(): - if isinstance(version, str): - # Turn a simple caret/tilde range (e.g. "^0.69.1", "~1.2.3") into an exact version. - deps[name] = re.sub(r"^[\^~]\s*(?=\d)", "", version) - with open(package_json_path, "w", encoding="utf-8") as f: - json.dump(package_json, f, indent=2) - - def generate_emitter_package_json(resolved_package_json_path: str) -> None: subprocess.check_call( [ @@ -151,7 +130,6 @@ def update_emitter(package_json_path: str, emitter_version: str): resolved_package_json_path = os.path.join(typespec_extension_path, "package.resolved.json") extract_package_json_from_tgz(dev_package_path, resolved_package_json_path) - pin_exact_versions(resolved_package_json_path) logging.info("Update emitter-package.json") generate_emitter_package_json(resolved_package_json_path) From d8442086a3be2469120faa85557570f50ac66972 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Fri, 10 Jul 2026 10:11:35 +0800 Subject: [PATCH 18/18] Only update emitter version in emitter-package.json, not peer deps Regenerating emitter-package.json from the emitter tarball pinned the peer/dev dependency versions from that tarball, which could be stale (e.g. an older typespec-client-generator-core than the one being released). Instead, only overwrite the @azure-tools/typespec-java dependency (published version or dev .tgz path) in the existing emitter-package.json and regenerate the lock file. Other dependency versions are managed elsewhere and updated before the pipeline runs. Removes the now-unused tarball extraction / config-file generation helpers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/scripts/sync_emitter.py | 88 +++++++-------------------- 1 file changed, 23 insertions(+), 65 deletions(-) diff --git a/eng/pipelines/scripts/sync_emitter.py b/eng/pipelines/scripts/sync_emitter.py index a9ac4a610bff..ba3289d25d37 100644 --- a/eng/pipelines/scripts/sync_emitter.py +++ b/eng/pipelines/scripts/sync_emitter.py @@ -11,8 +11,6 @@ import glob import shutil import json -import tarfile -import tempfile from typing import List sdk_root: str @@ -57,28 +55,19 @@ def parse_args() -> argparse.Namespace: EMITTER_PACKAGE_NAME = "@azure-tools/typespec-java" -def extract_package_json_from_tgz(tgz_path: str, dest_path: str) -> None: - # npm/pnpm pack tarballs place all files under a top-level "package/" directory. The - # package.json inside has the "catalog:"/"workspace:" protocols resolved to concrete - # versions, which is what emitter-package.json needs (npm cannot resolve those protocols). - with tarfile.open(tgz_path, "r:gz") as tar: - with tar.extractfile("package/package.json") as member: - with open(dest_path, "wb") as f: - f.write(member.read()) - - -def generate_emitter_package_json(resolved_package_json_path: str) -> None: - subprocess.check_call( - [ - "pwsh", - "./eng/common/scripts/typespec/New-EmitterPackageJson.ps1", - "-PackageJsonPath", - resolved_package_json_path, - "-OutputDirectory", - "eng", - ], - cwd=sdk_root, - ) +def set_emitter_dependency(emitter_ref: str) -> None: + # Only update the typespec-java dependency in the existing emitter-package.json. The other + # (peer/dev) dependency versions are managed elsewhere and updated before this pipeline runs, + # so we intentionally do not regenerate them from the emitter tarball (which may pin stale + # versions, e.g. an older typespec-client-generator-core). emitter_ref is either a published + # version string or a local path to the dev package tarball. + emitter_package_path = os.path.join(sdk_root, "eng", "emitter-package.json") + with open(emitter_package_path, "r") as json_file: + package_json = json.load(json_file) + package_json["dependencies"][EMITTER_PACKAGE_NAME] = emitter_ref + with open(emitter_package_path, "w") as json_file: + logging.info(f"Update emitter-package.json to use typespec-java {emitter_ref}") + json.dump(package_json, json_file, indent=2) def update_emitter(package_json_path: str, emitter_version: str): @@ -88,35 +77,17 @@ def update_emitter(package_json_path: str, emitter_version: str): emitter_version = "" if emitter_version: - # Published route (post-publish): pin emitter-package.json to a published version. - logging.info(f"Pin emitter-package.json to published typespec-java {emitter_version}") - with tempfile.TemporaryDirectory() as tmp_dir: - # Download the published tarball. Its package.json has the dependency versions - # resolved (unlike the typespec-azure monorepo source, which uses the "catalog:"/ - # "workspace:" protocols that npm cannot resolve), so tsp-client generate-config-files - # can consume it directly to produce emitter-package.json and its lock file. - subprocess.check_call( - ["npm", "pack", f"{EMITTER_PACKAGE_NAME}@{emitter_version}", "--pack-destination", tmp_dir], - cwd=sdk_root, - ) - tgz_files = glob.glob(os.path.join(tmp_dir, "*.tgz")) - if not tgz_files: - raise RuntimeError(f"Failed to download typespec-java {emitter_version} from npm.") - resolved_package_json_path = os.path.join(tmp_dir, "package.resolved.json") - extract_package_json_from_tgz(tgz_files[0], resolved_package_json_path) - - logging.info("Update emitter-package.json and emitter-package-lock.json") - subprocess.check_call( - ["tsp-client", "generate-config-files", "--package-json", resolved_package_json_path], - cwd=sdk_root, - ) + # Published route (post-publish): point emitter-package.json at a published version + # (released or unreleased/dev), then regenerate the lock file. + set_emitter_dependency(emitter_version) else: - # Dev route: build the emitter from source. We cannot use "tsp-client - # generate-config-files" here, as it would resolve against a published version. + # Dev route: build the emitter from source, then point emitter-package.json at the local + # dev package tarball. if not package_json_path: raise ValueError("--package-json-path is required when --emitter-version is empty.") - # Locate the dev package tarball produced by "pnpm pack". + # Locate the dev package tarball built by the "Build typespec-java dev package" step of + # the post-publish-emitter pipeline (pnpm pack), next to the emitter's package.json. dev_package_path = None typespec_extension_path = os.path.dirname(package_json_path) for file in os.listdir(typespec_extension_path): @@ -128,23 +99,10 @@ def update_emitter(package_json_path: str, emitter_version: str): logging.error("Failed to locate the dev package.") return - resolved_package_json_path = os.path.join(typespec_extension_path, "package.resolved.json") - extract_package_json_from_tgz(dev_package_path, resolved_package_json_path) - - logging.info("Update emitter-package.json") - generate_emitter_package_json(resolved_package_json_path) - - # replace version with path to dev package - emitter_package_path = os.path.join(sdk_root, "eng", "emitter-package.json") - with open(emitter_package_path, "r") as json_file: - package_json = json.load(json_file) - package_json["dependencies"][EMITTER_PACKAGE_NAME] = dev_package_path - with open(emitter_package_path, "w") as json_file: - logging.info(f'Update emitter-package.json to use typespec-java from "{dev_package_path}"') - json.dump(package_json, json_file, indent=2) + set_emitter_dependency(dev_package_path) - logging.info("Update emitter-package-lock.json") - generate_lock_file() + logging.info("Update emitter-package-lock.json") + generate_lock_file() def update_latest_dev():