Skip to content

Decouple emitter spector-coverage e2e from publish critical path#4824

Open
timotheeguerin wants to merge 1 commit into
mainfrom
tg/optimize-publish-spector-coverage
Open

Decouple emitter spector-coverage e2e from publish critical path#4824
timotheeguerin wants to merge 1 commit into
mainfrom
tg/optimize-publish-spector-coverage

Conversation

@timotheeguerin

@timotheeguerin timotheeguerin commented Jul 6, 2026

Copy link
Copy Markdown
Member

fix #4814

Problem

The publish pipeline (eng/pipelines/publish.yml) runs a single monolithic build job that executes the Python/Java/TypeScript emitter end-to-end tests serially, before the npm packing steps, purely to upload spector coverage. Because packing happens after those e2e blocks and publish_npm depends on the whole build stage, npm publishing is gated behind ~28 min of serial e2e runs.

These same emitter e2e tests already run on every PR (ci-python.yml, ci-typescript.yml, ci-java.yml) and nightly (nightly-e2e.yml); their only unique role in the publish pipeline is producing/uploading the coverage report.

Change

Move the emitter e2e + spector-coverage work out of the publish critical path into a separate parallel spector_coverage stage, and avoid rebuilding by reusing the turbo build cache.

  • build-for-publish.yml: remove the Python/Java/TS e2e + coverage steps; publish .turbo/cache as a turbo-cache pipeline artifact.
  • spector-coverage.yml (new): 3 parallel jobs. Each restores turbo-cache, runs pnpm run build (turbo cache hits → restores dist/**, no recompilation), runs the emitter e2e, and uploads spector coverage. Java runs Generate.ps1 without -SkipBuild since the Maven jar isn't a turbo dist/** output.
  • publish.yml: add the spector_coverage stage (dependsOn: build, only to consume the cache). publish_npm still depends only on build, so it runs in parallel with coverage and is no longer gated.

Result

  • Critical path is now build → publish_npm; emitter e2e/coverage runs alongside and never blocks publishing.
  • e2e failures still fail the pipeline run for visibility (coverage upload steps remain continueOnError: true).
  • No redundant full rebuilds — spector jobs reuse the build stage's turbo cache.

Measured before/after

Apples-to-apples on real runs — before: 6522723 (main, all 3 emitters, publish ran); after: 6523837 (this branch).

Metric Before (6522723) After (6523837)
build stage (gates npm publish) 51.4 min 23.3 min
Emitter e2e location serial, inside build, before pack separate spector_coverage stage, parallel
Emitter e2e wall-clock ~28 min serial 14.9 min (3 jobs in parallel)
Per-job build step part of the 3.7 min full build 0.9 min turbo cache hit
publish_npm starts at ~51 min ~23 min
Total pipeline ~68.6 min ~39.7 min

Net: the publish critical path drops ~51 → ~23 min (−28 min / −55%); total wall-clock ~68 → ~40 min.

Why each spector job is still ~15 min (expected)

The turbo cache is working — the build step dropped from 3.7 min (full) to 0.9 min (cache hit). The remaining ~15 min is the intrinsic emitter e2e work, not the build:

Job (~15 min) Breakdown
Python regenerate 7.8m + run e2e 2.6m + ~4m fixed 1ES overhead
Java Generate (regen + Maven jar) 9.8m + ~4m overhead
TypeScript e2e 8.0m + ~4m overhead

The ~8–10 min per job is the emitter regenerating all spector scenarios and running the tests — the exact thing being measured for coverage — so it can't be cached away. These now overlap instead of stacking serially and are off the publish path.

Possible follow-ups (out of scope here)

  • Java: reuse the emitter.jar already built in the build stage (publish it as an artifact + Generate.ps1 -SkipBuild) to skip the Maven rebuild (~1–2 min); the bulk is scenario regeneration, not the jar.
  • The real lever for faster jobs is speeding the per-emitter regeneration/tests themselves (more internal parallelism / reduced scenario set).

Move the Python/Java/TypeScript emitter e2e + spector coverage upload out
of the monolithic build job (where they ran before packing and gated npm
publishing) into a separate parallel spector_coverage stage.

- build-for-publish.yml: remove the emitter e2e steps; publish .turbo/cache
  as a turbo-cache pipeline artifact.
- spector-coverage.yml (new): 3 parallel jobs that restore the turbo cache,
  run pnpm build as cache hits (no recompilation), run the emitter e2e and
  upload spector coverage. Java uses Generate.ps1 without -SkipBuild since
  the Maven jar isn't a turbo dist output.
- publish.yml: add spector_coverage stage (dependsOn build only to consume
  the cache). publish_npm still depends only on build, so it runs in
  parallel and is no longer gated by the emitter e2e tests.
@azure-sdk-automation

Copy link
Copy Markdown
Contributor

No changes needing a change description found.

@azure-sdk-automation

Copy link
Copy Markdown
Contributor

You can try these changes here

🛝 Playground 🌐 Website

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

⚡ Benchmark Results

⚠️ 24 metric(s) regressed above the +5% threshold:

Metric Baseline Current Change
total 🔴 984.6ms 🔴 1.08s +10.0% 🔴
loader 🟡 297.3ms 🟡 330.7ms +11.2% 🔴
resolver 🟢 39.3ms 🟢 42.1ms +7.2% 🔴
checker 🟡 354.6ms 🟡 390.2ms +10.0% 🔴
validation 🟢 83.8ms 🟢 88.9ms +6.1% 🔴
 ↳ validation/@azure-tools/typespec-azure-core 🟡 12.3ms 🟡 13.6ms +10.9% 🔴
 ↳ validation/@typespec/http 🟡 15.2ms 🟡 16.6ms +9.8% 🔴
linter 🟡 209.2ms 🟡 230.4ms +10.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-header-explode 🔴 25.7ms 🔴 28.7ms +11.4% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-query-explode 🔴 26.9ms 🔴 30.0ms +11.4% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-response-body 🔴 32.4ms 🔴 35.4ms +9.3% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/response-schema-problem 🔴 31.8ms 🔴 35.0ms +10.2% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-agent-base-type-child-resources 🔴 20.9ms 🔴 23.3ms +11.6% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-common-types-version 🟡 11.9ms 🟡 13.3ms +12.4% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-path-casing-conflicts 🟡 16.8ms 🟡 18.4ms +9.5% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/lro-location-header 🟡 19.0ms 🔴 21.3ms +12.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-response-body 🔴 28.9ms 🔴 32.0ms +10.4% 🔴
emit 🔴 5.89s 🔴 6.42s +9.1% 🔴
 ↳ emit/@azure-tools/typespec-autorest 🟢 122.1ms 🟢 131.9ms +8.0% 🔴
 ↳ emit/@azure-tools/typespec-python 🔴 2.19s 🔴 2.40s +9.3% 🔴
 ↳ emit/@typespec/http-client-js 🔴 557.0ms 🔴 605.5ms +8.7% 🔴
 ↳ emit/@typespec/openapi3 🟢 104.8ms 🟢 114.4ms +9.1% 🔴
 ↳ emit/@typespec/openapi3/compute 🟢 91.0ms 🟢 99.4ms +9.3% 🔴
 ↳ emit/@typespec/openapi3/write 🟢 13.6ms 🟢 14.7ms +8.0% 🔴
Full details – comparing d3831bf vs baseline rolling-baseline-8d517e0-d56eaff (rolling baseline (20 main runs))
Metric Baseline Current Change
total 🔴 984.6ms 🔴 1.08s +10.0% 🔴
loader 🟡 297.3ms 🟡 330.7ms +11.2% 🔴
resolver 🟢 39.3ms 🟢 42.1ms +7.2% 🔴
checker 🟡 354.6ms 🟡 390.2ms +10.0% 🔴
validation 🟢 83.8ms 🟢 88.9ms +6.1% 🔴
 ↳ validation/@azure-tools/typespec-azure-core 🟡 12.3ms 🟡 13.6ms +10.9% 🔴
 ↳ validation/@typespec/http 🟡 15.2ms 🟡 16.6ms +9.8% 🔴
 ↳ validation/@typespec/rest 🟢 1.6ms 🟢 1.7ms +9.4%
 ↳ validation/@typespec/versioning 🔴 50.8ms 🔴 52.8ms +4.0%
 ↳ validation/compiler 🟢 3.8ms 🟢 4.0ms +6.7%
linter 🟡 209.2ms 🟡 230.4ms +10.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/auth-required 🟢 0.1ms 🟢 0.1ms +13.2%
 ↳ linter/@azure-tools/typespec-azure-core/bad-record-type 🟢 0.6ms 🟢 0.6ms +6.0%
 ↳ linter/@azure-tools/typespec-azure-core/byos 🟢 8.1ms 🟢 9.0ms +10.8%
 ↳ linter/@azure-tools/typespec-azure-core/casing-style 🟢 1.4ms 🟢 1.5ms +9.1%
 ↳ linter/@azure-tools/typespec-azure-core/composition-over-inheritance 🟢 0.2ms 🟢 0.2ms +9.5%
 ↳ linter/@azure-tools/typespec-azure-core/documentation-required 🟢 1.9ms 🟢 2.1ms +10.3%
 ↳ linter/@azure-tools/typespec-azure-core/friendly-name 🟢 1.4ms 🟢 1.5ms +8.3%
 ↳ linter/@azure-tools/typespec-azure-core/key-visibility-required 🟢 0.4ms 🟢 0.4ms +16.9%
 ↳ linter/@azure-tools/typespec-azure-core/known-encoding 🟢 0.5ms 🟢 0.5ms +9.3%
 ↳ linter/@azure-tools/typespec-azure-core/long-running-polling-operation-required 🟢 0.8ms 🟢 0.9ms +13.2%
 ↳ linter/@azure-tools/typespec-azure-core/no-case-mismatch 🟢 0.7ms 🟢 0.7ms +9.0%
 ↳ linter/@azure-tools/typespec-azure-core/no-closed-literal-union 🟢 1.0ms 🟢 1.1ms +14.3%
 ↳ linter/@azure-tools/typespec-azure-core/no-enum 🟢 0.2ms 🟢 0.3ms +12.4%
 ↳ linter/@azure-tools/typespec-azure-core/no-error-status-codes 🟢 0.3ms 🟢 0.3ms +11.0%
 ↳ linter/@azure-tools/typespec-azure-core/no-explicit-routes-resource-ops 🟢 0.2ms 🟢 0.2ms +13.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-format 🟢 0.8ms 🟢 0.9ms +7.4%
 ↳ linter/@azure-tools/typespec-azure-core/no-generic-numeric 🟢 0.8ms 🟢 0.9ms +8.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-header-explode 🔴 25.7ms 🔴 28.7ms +11.4% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-legacy-usage 🟢 2.0ms 🟢 2.2ms +9.2%
 ↳ linter/@azure-tools/typespec-azure-core/no-multiple-discriminator 🟢 0.2ms 🟢 0.3ms +17.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-nullable 🟢 0.4ms 🟢 0.5ms +10.7%
 ↳ linter/@azure-tools/typespec-azure-core/no-offsetdatetime 🟢 1.9ms 🟢 2.1ms +8.0%
 ↳ linter/@azure-tools/typespec-azure-core/no-openapi 🟢 2.3ms 🟢 2.4ms +5.3%
 ↳ linter/@azure-tools/typespec-azure-core/no-private-usage 🟢 3.0ms 🟢 3.4ms +12.3%
 ↳ linter/@azure-tools/typespec-azure-core/no-query-explode 🔴 26.9ms 🔴 30.0ms +11.4% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-response-body 🔴 32.4ms 🔴 35.4ms +9.3% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-rest-library-interfaces 🟢 0.1ms 🟢 0.1ms +20.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-route-parameter-name-mismatch 🟢 7.4ms 🟢 8.3ms +12.1%
 ↳ linter/@azure-tools/typespec-azure-core/no-rpc-path-params 🟢 0.4ms 🟢 0.4ms +11.1%
 ↳ linter/@azure-tools/typespec-azure-core/no-string-discriminator 🟢 0.1ms 🟢 0.2ms +13.4%
 ↳ linter/@azure-tools/typespec-azure-core/no-unknown 🟢 0.3ms 🟢 0.4ms +10.2%
 ↳ linter/@azure-tools/typespec-azure-core/no-unnamed-union 🟢 0.8ms 🟢 0.8ms +9.6%
 ↳ linter/@azure-tools/typespec-azure-core/operation-missing-api-version 🟢 0.4ms 🟢 0.4ms +8.2%
 ↳ linter/@azure-tools/typespec-azure-core/request-body-problem 🟢 0.4ms 🟢 0.5ms +6.7%
 ↳ linter/@azure-tools/typespec-azure-core/require-versioned 🟢 0.1ms 🟢 0.1ms +9.7%
 ↳ linter/@azure-tools/typespec-azure-core/response-schema-problem 🔴 31.8ms 🔴 35.0ms +10.2% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/rpc-operation-request-body 🟢 0.7ms 🟢 0.8ms +10.2%
 ↳ linter/@azure-tools/typespec-azure-core/spread-discriminated-model 🟢 0.5ms 🟢 0.5ms +9.1%
 ↳ linter/@azure-tools/typespec-azure-core/use-standard-names 🟢 7.3ms 🟢 8.2ms +12.1%
 ↳ linter/@azure-tools/typespec-azure-core/use-standard-operations 🟢 0.3ms 🟢 0.3ms +11.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-agent-base-type-child-resources 🔴 20.9ms 🔴 23.3ms +11.6% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-agent-base-type-lifecycle-operations 🟢 0.3ms 🟢 0.3ms +2.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-common-types-version 🟡 11.9ms 🟡 13.3ms +12.4% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-custom-resource-no-key 🟢 0.2ms 🟢 0.3ms +17.4%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-custom-resource-usage-discourage 🟢 0.1ms 🟢 0.1ms +8.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-delete-operation-response-codes 🟢 5.4ms 🟢 4.6ms -15.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-path-casing-conflicts 🟡 16.8ms 🟡 18.4ms +9.5% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-record 🟢 0.6ms 🟢 0.7ms +9.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes 🟢 1.4ms 🟢 1.5ms +5.5%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-put-operation-response-codes 🟢 0.2ms 🟢 0.2ms +2.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-action-no-segment 🟢 0.4ms 🟢 0.5ms +14.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-duplicate-property 🟢 0.4ms 🟢 0.4ms +12.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-interface-requires-decorator 🟢 0.1ms 🟢 0.1ms +0.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-action-verb 🟢 0.2ms 🟢 0.2ms +15.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-envelope-property 🟢 0.3ms 🟢 0.3ms +16.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-version-format 🟢 0.2ms 🟢 0.2ms +10.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-key-invalid-chars 🟢 0.5ms 🟢 0.5ms +15.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-name-pattern 🟢 0.1ms 🟢 0.1ms +5.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation 🟢 0.5ms 🟢 0.6ms +17.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation-response 🟢 8.7ms 🟢 9.6ms +10.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-patch 🟢 0.8ms 🟢 0.9ms +11.6%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-path-segment-invalid-chars 🟢 0.4ms 🟢 0.4ms +9.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-provisioning-state 🟢 0.4ms 🟢 0.5ms +7.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/beyond-nesting-levels 🟢 0.2ms 🟢 0.3ms +14.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/empty-updateable-properties 🟢 0.4ms 🟢 0.4ms +8.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/improper-subscription-list-operation 🟢 0.1ms 🟢 0.1ms +13.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/lro-location-header 🟡 19.0ms 🔴 21.3ms +12.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-operations-endpoint 🟢 0.1ms 🟢 0.2ms +12.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-x-ms-identifiers 🟢 1.0ms 🟢 1.1ms +8.4%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-empty-model 🟢 0.3ms 🟢 0.3ms +14.5%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-override-props 🟢 0.3ms 🟢 0.3ms +12.5%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-resource-delete-operation 🟢 0.4ms 🟢 0.5ms +17.6%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-response-body 🔴 28.9ms 🔴 32.0ms +10.4% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/patch-envelope 🟢 0.4ms 🟢 0.5ms +18.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/resource-name 🟢 0.4ms 🟢 0.5ms +10.4%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/secret-prop 🟢 5.3ms 🟢 5.9ms +11.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/unsupported-type 🟢 0.7ms 🟢 0.7ms +12.5%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/version-progression 🟢 0.3ms 🟢 0.3ms +7.1%
 ↳ linter/@azure-tools/typespec-client-generator-core/property-name-conflict 🟢 2.2ms 🟢 2.4ms +8.8%
 ↳ linter/@azure-tools/typespec-client-generator-core/require-client-suffix 🟢 1.6ms 🟢 1.8ms +8.6%
emit 🔴 5.89s 🔴 6.42s +9.1% 🔴
 ↳ emit/@Azure-Tools 🟢 0.0ms 🟢 0.0ms +0.0%
 ↳ emit/@azure-tools/typespec-autorest 🟢 122.1ms 🟢 131.9ms +8.0% 🔴
 ↳ emit/@azure-tools/typespec-python 🔴 2.19s 🔴 2.40s +9.3% 🔴
 ↳ emit/@typespec 🟢 0.0ms 🟢 0.0ms +0.0%
 ↳ emit/@typespec/http-client-js 🔴 557.0ms 🔴 605.5ms +8.7% 🔴
 ↳ emit/@typespec/openapi3 🟢 104.8ms 🟢 114.4ms +9.1% 🔴
 ↳ emit/@typespec/openapi3/compute 🟢 91.0ms 🟢 99.4ms +9.3% 🔴
 ↳ emit/@typespec/openapi3/write 🟢 13.6ms 🟢 14.7ms +8.0% 🔴

Averaged across 3 specs (azure-arm-resource-manager, azure-core-dataplane, azure-full).
Threshold: changes > ±5% are highlighted.
🟢 Fast · 🟡 Moderate (stages >200ms, rules >10ms) · 🔴 Slow (stages >400ms, rules >20ms)

@timotheeguerin timotheeguerin marked this pull request as ready for review July 6, 2026 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Optimize publish pipeline] Split e2e test run + publish from main build

2 participants