Skip to content

feat(typespec-autorest): emit service.yaml manifest from @versioned#4846

Queued
timotheeguerin wants to merge 3 commits into
mainfrom
serviceyaml
Queued

feat(typespec-autorest): emit service.yaml manifest from @versioned#4846
timotheeguerin wants to merge 3 commits into
mainfrom
serviceyaml

Conversation

@timotheeguerin

@timotheeguerin timotheeguerin commented Jul 7, 2026

Copy link
Copy Markdown
Member

Part of #4825 — implements #4827 (and the schema half of #4826).

Adds service.yaml emission to @azure-tools/typespec-autorest, deriving the ordered API version list from the @versioned enum.

What

  • New service-yaml emitter option ("auto" | "always" | "never", default "auto"):
    • auto: emit/update service.yaml only if the file already exists at the project root (opt-in during rollout; will be forced later).
    • always: always emit.
    • never: disable emission.
  • Output: service.yaml is written at the project root, next to tspconfig.yaml:
    versions:
      - version: 2023-11-01
        source: typespec
        swagger-files:
          - resource-manager/Contoso/stable/2023-11-01/openapi.json
      - version: 2024-06-01-preview
        source: typespec
        swagger-files:
          - resource-manager/Contoso/preview/2024-06-01-preview/openapi.json
    • versions are ordered oldest→newest from the @versioned enum.
    • source differentiates TypeSpec versions from legacy swagger versions (autorest always emits typespec).
    • swagger-files are paths to the emitted openapi file(s), relative to service.yaml.
  • Schema defined in TypeSpec: schema/service-yaml.tsp (@jsonSchema), with the JSON Schema generated via @typespec/json-schema (new regen-service-yaml-schema build step, shipped through package.json files).
  • Multiple services in one project → a warning diagnostic; only the first service is included.

Scope

This PR intentionally covers only autorest emission + the schema. Deferred to sibling issues: the readme.md → service.yaml conversion tool and repo-wide migration (rest of #4826), TSV sync-check (#4828), contributor docs (#4829), and readme.md removal (#4830).

Real-world rollout validation

Validated end-to-end against azure-rest-api-specs by rolling this emitter out to every TypeSpec project that emits @azure-tools/typespec-autorest: Azure/azure-rest-api-specs#44506 (draft, depends on this PR).

  • Seeded an empty service.yaml at each project root and recompiled with this PR's build (via pkg.pr.new); auto mode populated 558 services.
  • Cross-checked every generated service.yaml against the existing readme.md input-file mappings across all 579 readmes: 1360/1365 (99.6%) of swagger references match exactly; the manifests are a faithful subset (TypeSpec versions only, legacy source: swagger versions excluded as expected).
  • The rollout surfaced that multi-service aggregate/wrapper projects (e.g. ResourceGraph, Microsoft.Insights, RadiologyInsights) don't map to a single project-root output — real per-service manifests come from their sub-projects instead. Reinforces the "only first service" multi-service caveat below.

Open questions

  • Option name/shape (service-yaml enum vs a boolean).
  • Whether unversioned services should emit a service.yaml.
  • Whether to embed a $schema / yaml-language-server reference in the emitted file.

Validation

  • Full package build (schema regen + tsc + library lint) ✅
  • oxlint clean ✅
  • 501/501 package tests pass, including 6 new test/service-yaml.test.ts cases ✅

Add a `service-yaml` emitter option that generates a service.yaml manifest
at the project root declaring the service's API versions (derived from the
@versioned enum). The option controls emission: "auto" (default) writes only
if the file already exists, "always" always writes, "never" disables it.

The schema is defined in TypeSpec (schema/service-yaml.tsp) and the JSON
Schema is generated via @typespec/json-schema.
@microsoft-github-policy-service microsoft-github-policy-service Bot added emitter:autorest Issues for @azure-tools/typespec-autorest emitter meta:website TypeSpec.io updates labels Jul 7, 2026
@azure-sdk-automation

azure-sdk-automation Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • @azure-tools/typespec-autorest
Show changes

@azure-tools/typespec-autorest - feature ✏️

Add service-yaml emitter option to generate a service.yaml manifest at the project root declaring the service's API versions (derived from the @versioned enum). The option controls emission: "auto" (default) writes the file only when it already exists, "always" always writes it, and "never" disables it. When an existing service.yaml is present it is updated in place, preserving comments and unrelated keys.,> ,> yaml,> versions:,> - version: 2023-11-01,> source: typespec,> swagger-files:,> - resource-manager/Contoso/stable/2023-11-01/openapi.json,>

@pkg-pr-new

pkg-pr-new Bot commented Jul 7, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@azure-tools/typespec-autorest@4846

commit: a02e305

@azure-sdk-automation

Copy link
Copy Markdown
Contributor

You can try these changes here

🛝 Playground 🌐 Website

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

⚡ Benchmark Results

✅ No performance regressions detected.

Full details – comparing e73b90a vs baseline rolling-baseline-f49cd9d-f3a0f6d (rolling baseline (20 main runs))
Metric Baseline Current Change
total 🔴 1.03s 🔴 765.1ms -25.4% 🟢
loader 🟡 310.9ms 🟡 226.1ms -27.3% 🟢
resolver 🟢 40.5ms 🟢 32.9ms -18.8% 🟢
checker 🟡 369.3ms 🟡 276.5ms -25.1% 🟢
validation 🟢 85.6ms 🟢 67.3ms -21.4% 🟢
 ↳ validation/@azure-tools/typespec-azure-core 🟡 12.8ms 🟢 9.4ms -26.6% 🟢
 ↳ validation/@typespec/http 🟡 15.7ms 🟡 11.5ms -26.9% 🟢
 ↳ validation/@typespec/rest 🟢 1.6ms 🟢 1.4ms -17.4%
 ↳ validation/@typespec/versioning 🔴 51.5ms 🔴 41.8ms -18.8% 🟢
 ↳ validation/compiler 🟢 3.9ms 🟢 3.2ms -17.7%
linter 🟡 218.5ms 🟢 161.6ms -26.0% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/auth-required 🟢 0.1ms 🟢 0.1ms -26.2%
 ↳ linter/@azure-tools/typespec-azure-core/bad-record-type 🟢 0.6ms 🟢 0.5ms -20.2%
 ↳ linter/@azure-tools/typespec-azure-core/byos 🟢 8.5ms 🟢 6.6ms -22.4% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/casing-style 🟢 1.5ms 🟢 1.2ms -21.4%
 ↳ linter/@azure-tools/typespec-azure-core/composition-over-inheritance 🟢 0.2ms 🟢 0.1ms -21.4%
 ↳ linter/@azure-tools/typespec-azure-core/documentation-required 🟢 2.0ms 🟢 1.7ms -12.7%
 ↳ linter/@azure-tools/typespec-azure-core/friendly-name 🟢 1.4ms 🟢 1.1ms -25.0%
 ↳ linter/@azure-tools/typespec-azure-core/key-visibility-required 🟢 0.4ms 🟢 0.3ms -24.1%
 ↳ linter/@azure-tools/typespec-azure-core/known-encoding 🟢 0.5ms 🟢 0.4ms -28.2%
 ↳ linter/@azure-tools/typespec-azure-core/long-running-polling-operation-required 🟢 0.8ms 🟢 0.6ms -22.5%
 ↳ linter/@azure-tools/typespec-azure-core/no-case-mismatch 🟢 0.7ms 🟢 0.6ms -20.1%
 ↳ linter/@azure-tools/typespec-azure-core/no-closed-literal-union 🟢 1.0ms 🟢 0.8ms -20.2%
 ↳ linter/@azure-tools/typespec-azure-core/no-enum 🟢 0.2ms 🟢 0.2ms -22.0%
 ↳ linter/@azure-tools/typespec-azure-core/no-error-status-codes 🟢 0.3ms 🟢 0.2ms -21.9%
 ↳ linter/@azure-tools/typespec-azure-core/no-explicit-routes-resource-ops 🟢 0.2ms 🟢 0.1ms -22.4%
 ↳ linter/@azure-tools/typespec-azure-core/no-format 🟢 0.8ms 🟢 0.6ms -23.0%
 ↳ linter/@azure-tools/typespec-azure-core/no-generic-numeric 🟢 0.8ms 🟢 0.7ms -19.2%
 ↳ linter/@azure-tools/typespec-azure-core/no-header-explode 🔴 27.2ms 🟡 19.2ms -29.4% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/no-legacy-usage 🟢 2.1ms 🟢 1.6ms -24.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-multiple-discriminator 🟢 0.2ms 🟢 0.2ms -29.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-nullable 🟢 0.4ms 🟢 0.3ms -22.7%
 ↳ linter/@azure-tools/typespec-azure-core/no-offsetdatetime 🟢 2.0ms 🟢 1.5ms -25.7%
 ↳ linter/@azure-tools/typespec-azure-core/no-openapi 🟢 2.3ms 🟢 1.8ms -21.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-private-usage 🟢 3.2ms 🟢 2.5ms -21.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-query-explode 🔴 28.3ms 🟡 19.9ms -29.8% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/no-response-body 🔴 33.9ms 🔴 25.4ms -25.0% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/no-rest-library-interfaces 🟢 0.1ms 🟢 0.1ms -20.4%
 ↳ linter/@azure-tools/typespec-azure-core/no-route-parameter-name-mismatch 🟢 7.8ms 🟢 5.5ms -29.2% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/no-rpc-path-params 🟢 0.4ms 🟢 0.3ms -23.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-string-discriminator 🟢 0.1ms 🟢 0.1ms -27.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-unknown 🟢 0.4ms 🟢 0.3ms -25.9%
 ↳ linter/@azure-tools/typespec-azure-core/no-unnamed-union 🟢 0.8ms 🟢 0.6ms -21.5%
 ↳ linter/@azure-tools/typespec-azure-core/operation-missing-api-version 🟢 0.4ms 🟢 0.3ms -17.6%
 ↳ linter/@azure-tools/typespec-azure-core/request-body-problem 🟢 0.5ms 🟢 0.4ms -16.4%
 ↳ linter/@azure-tools/typespec-azure-core/require-versioned 🟢 0.1ms 🟢 0.0ms -26.1%
 ↳ linter/@azure-tools/typespec-azure-core/response-schema-problem 🔴 33.2ms 🔴 24.4ms -26.6% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/rpc-operation-request-body 🟢 0.7ms 🟢 0.5ms -28.5%
 ↳ linter/@azure-tools/typespec-azure-core/spread-discriminated-model 🟢 0.5ms 🟢 0.3ms -24.6%
 ↳ linter/@azure-tools/typespec-azure-core/use-standard-names 🟢 7.7ms 🟢 5.5ms -28.7% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/use-standard-operations 🟢 0.3ms 🟢 0.2ms -21.6%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-agent-base-type-child-resources 🔴 21.7ms 🟢 9.6ms -55.9% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-agent-base-type-lifecycle-operations 🟢 0.3ms 🟢 0.2ms -35.4%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-common-types-version 🟡 12.3ms 🟢 8.7ms -29.3% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-custom-resource-no-key 🟢 0.2ms 🟢 0.2ms -29.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-custom-resource-usage-discourage 🟢 0.1ms 🟢 1.1ms +702.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-delete-operation-response-codes 🟢 5.0ms 🟢 4.2ms -15.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-path-casing-conflicts 🟡 17.2ms 🟡 12.7ms -26.2% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-record 🟢 0.6ms 🟢 1.5ms +126.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes 🟢 1.4ms 🟢 1.4ms +0.6%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-put-operation-response-codes 🟢 0.2ms 🟢 0.2ms -24.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-action-no-segment 🟢 0.5ms 🟢 0.3ms -23.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-duplicate-property 🟢 0.4ms 🟢 0.3ms -26.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-interface-requires-decorator 🟢 0.1ms 🟢 0.1ms -23.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-action-verb 🟢 0.2ms 🟢 0.1ms -21.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-envelope-property 🟢 0.3ms 🟢 0.2ms -26.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-version-format 🟢 0.2ms 🟢 0.2ms -18.6%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-key-invalid-chars 🟢 0.5ms 🟢 0.3ms -30.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-name-pattern 🟢 0.1ms 🟢 0.1ms -20.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation 🟢 0.6ms 🟢 0.4ms -25.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation-response 🟢 9.1ms 🟢 6.9ms -24.1% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-patch 🟢 0.8ms 🟢 0.7ms -17.6%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-path-segment-invalid-chars 🟢 0.4ms 🟢 0.3ms -26.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-provisioning-state 🟢 0.4ms 🟢 0.3ms -25.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/beyond-nesting-levels 🟢 0.2ms 🟢 0.2ms -30.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/empty-updateable-properties 🟢 0.4ms 🟢 0.3ms -27.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/improper-subscription-list-operation 🟢 0.1ms 🟢 0.1ms -27.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/lro-location-header 🔴 20.0ms 🟡 13.7ms -31.4% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-operations-endpoint 🟢 0.1ms 🟢 0.1ms -22.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-x-ms-identifiers 🟢 1.0ms 🟢 0.9ms -7.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-empty-model 🟢 0.3ms 🟢 0.2ms -23.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-override-props 🟢 0.3ms 🟢 0.2ms -23.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-reserved-resource-property 🟢 0.2ms 🟢 0.2ms -28.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-resource-delete-operation 🟢 0.5ms 🟢 0.3ms -28.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-response-body 🔴 30.4ms 🔴 21.1ms -30.6% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/patch-envelope 🟢 0.4ms 🟢 0.3ms -28.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/resource-name 🟢 0.4ms 🟢 0.3ms -27.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/secret-prop 🟢 5.4ms 🟢 4.5ms -16.5%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/unsupported-type 🟢 0.7ms 🟢 0.5ms -24.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/version-progression 🟢 0.3ms 🟢 0.2ms -14.7%
 ↳ linter/@azure-tools/typespec-client-generator-core/property-name-conflict 🟢 2.3ms 🟢 1.7ms -26.1%
 ↳ linter/@azure-tools/typespec-client-generator-core/require-client-suffix 🟢 1.7ms 🟢 1.3ms -21.7%
emit 🔴 6.13s 🔴 4.68s -23.7% 🟢
 ↳ emit/@Azure-Tools 🟢 0.0ms 🟢 0.0ms +0.0%
 ↳ emit/@azure-tools/typespec-autorest 🟢 127.0ms 🟢 98.1ms -22.7% 🟢
 ↳ emit/@azure-tools/typespec-python 🔴 2.28s 🔴 1.74s -23.9% 🟢
 ↳ emit/@typespec 🟢 0.0ms 🟢 0.0ms +0.0%
 ↳ emit/@typespec/http-client-js 🔴 578.5ms 🔴 444.3ms -23.2% 🟢
 ↳ emit/@typespec/openapi3 🟢 109.5ms 🟢 83.8ms -23.4% 🟢
 ↳ emit/@typespec/openapi3/compute 🟢 95.1ms 🟢 72.9ms -23.4% 🟢
 ↳ emit/@typespec/openapi3/write 🟢 14.0ms 🟢 10.6ms -24.2% 🟢

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

Update an existing service.yaml in place by editing the YAML document tree
rather than regenerating it, so file-level comments, unrelated keys, and
per-version comments on versions that still exist are preserved.
@markcowl

markcowl commented Jul 8, 2026

Copy link
Copy Markdown
Member

@timotheeguerin Here is an example of the readme.md needed by RPaaS: https://github.com/Azure/azure-rest-api-specs-pr/tree/RPSaaSMaster/specification/windowsesu/resource-manager

It looks like this is just boilerplate, so we should be able to get away with just leaving it in place without updates.

RPSaaSMaster also has mostly v1 specs, so these would need to be converted to v2 and have the boilerplate readme.md under resource-manager

There may be some need for this in live validation as well, will see if I cna find an instance.

Comment thread packages/typespec-autorest/test/service-yaml.test.ts
…service.yaml

Adds a test for ARM legacy feature-file splitting where a single version emits multiple swagger files, all aggregated under that version's swagger-files list. Generalizes the emitServiceYaml test helper to accept a tester and emitter options.
@timotheeguerin timotheeguerin added this pull request to the merge queue Jul 13, 2026
Any commits made after this event will not be merged.
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Jul 13, 2026
@timotheeguerin timotheeguerin added this pull request to the merge queue Jul 13, 2026
Any commits made after this event will not be merged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

emitter:autorest Issues for @azure-tools/typespec-autorest emitter meta:website TypeSpec.io updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants