feat(typespec-autorest): emit service.yaml manifest from @versioned#4846
feat(typespec-autorest): emit service.yaml manifest from @versioned#4846timotheeguerin wants to merge 3 commits into
Conversation
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.
|
All changed packages have been documented.
Show changes
|
commit: |
|
You can try these changes here
|
⚡ Benchmark Results✅ No performance regressions detected. Full details – comparing
|
| 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.
|
@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 There may be some need for this in live validation as well, will see if I cna find an instance. |
…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.
Part of #4825 — implements #4827 (and the schema half of #4826).
Adds
service.yamlemission to@azure-tools/typespec-autorest, deriving the ordered API version list from the@versionedenum.What
service-yamlemitter option ("auto"|"always"|"never", default"auto"):auto: emit/updateservice.yamlonly if the file already exists at the project root (opt-in during rollout; will be forced later).always: always emit.never: disable emission.service.yamlis written at the project root, next totspconfig.yaml:versionsare ordered oldest→newest from the@versionedenum.sourcedifferentiates TypeSpec versions from legacyswaggerversions (autorest always emitstypespec).swagger-filesare paths to the emitted openapi file(s), relative toservice.yaml.schema/service-yaml.tsp(@jsonSchema), with the JSON Schema generated via@typespec/json-schema(newregen-service-yaml-schemabuild step, shipped throughpackage.jsonfiles).Scope
This PR intentionally covers only autorest emission + the schema. Deferred to sibling issues: the
readme.md → service.yamlconversion tool and repo-wide migration (rest of #4826), TSV sync-check (#4828), contributor docs (#4829), andreadme.mdremoval (#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).service.yamlat each project root and recompiled with this PR's build (viapkg.pr.new);automode populated 558 services.service.yamlagainst the existingreadme.mdinput-filemappings across all 579 readmes: 1360/1365 (99.6%) of swagger references match exactly; the manifests are a faithful subset (TypeSpec versions only, legacysource: swaggerversions excluded as expected).Open questions
service-yamlenum vs a boolean).service.yaml.$schema/ yaml-language-server reference in the emitted file.Validation
oxlintclean ✅test/service-yaml.test.tscases ✅