Skip to content

[Swagger Linter Migration] LatestVersionOfCommonTypesMustBeUsed - #5271

Open
Yuchao Yan (msyyc) wants to merge 13 commits into
Azure:mainfrom
msyyc:promote-latest-version-common-types-to-arm
Open

[Swagger Linter Migration] LatestVersionOfCommonTypesMustBeUsed#5271
Yuchao Yan (msyyc) wants to merge 13 commits into
Azure:mainfrom
msyyc:promote-latest-version-common-types-to-arm

Conversation

@msyyc

@msyyc Yuchao Yan (msyyc) commented Aug 20, 2026

Copy link
Copy Markdown
Member

Original Swagger linter

Original checks:

  • Runs on every Swagger $ref value via given: "$..['$ref']" in the ARM ruleset.
  • Ignores null refs and refs that do not match /common-types/resource-management/v\d+/\w+.json#.
  • Extracts the referenced common-types version and file name from the $ref path.
  • Uses isLatestCommonTypesVersionForFile and LATEST_VERSION_BY_COMMON_TYPES_FILENAME to decide whether that file is referenced through the latest known common-types version.
  • Emits one warning at the $ref path when the referenced version differs from the latest map entry.

How the Swagger linter works

The Swagger validator is a Spectral function over emitted OpenAPI 2.0 documents. It does not understand TypeSpec symbols, ARM service namespaces, @armCommonTypesVersion, or version projections. Instead, it scans each emitted $ref string, filters to ARM common-types references under /common-types/resource-management/v*/, extracts {version, file}, and compares the extracted version with a hard-coded filename-to-latest-version map.

That string-level traversal means Swagger diagnostic locations are emitted $ref paths and raw diagnostic cardinality is tied to every repeated emitted occurrence. It also means the validator can be stale when the hard-coded map is stale. The lintdiff evidence records known stale-map false positives for valid v6 networksecurityperimeter.json and managedidentitywithdelegation.json references that the validator still maps to v5; the promoted rule intentionally does not copy those stale data defects.

Source TypeSpec lintdiff rule

Source lintdiff rule id: LatestVersionOfCommonTypesMustBeUsed

Local rule name: latest-version-of-common-types-must-be-used

Official TypeSpec rule name: use-latest-version-of-common-types (renamed during promotion to follow TypeSpec linter naming conventions)

Source branch: feature/lintdiff-latest-version-common-types

Source commit inspected: be310817b95a8c2fba64b7906e23624ec38efab0

Source worktree: C:\dev\worktrees\lintdiff-latest-version-common-types-source

Source rule: https://github.com/Azure/typespec-azure/blob/feature/lintdiff-latest-version-common-types/packages/typespec-lintdiff/src/rules/latest-version-of-common-types-must-be-used.ts

The source worktree was clean, with no uncommitted rule changes. The user-marked done lintdiff source rule was not modified during promotion.

Destination analysis

Selected package: @azure-tools/typespec-azure-resource-manager.

@azure-tools/typespec-azure-core was considered but rejected because this rule is ARM-specific and depends on ARM-only concepts and helpers. The validator metadata marks applicability as ARM with source arm; the fixture rule docs say it applies to Resource Manager; the implementation checks ARM provider namespaces, reads effective @armCommonTypesVersion, resolves ARM common type references, and imports ARM helpers such as getArmCommonTypesVersion, getArmCommonTypesVersions, isArmCommonType, and getArmCommonTypeOpenAPIRef. Moving this to core would require rewriting away ARM package dependencies and would violate the dependency direction that core must not depend on resource-manager.

The existing official ARM arm-common-types-version rule is related but not equivalent: it requires specifying a common-types version, while this promoted rule requires selecting the latest common-types version and catches latest-version APIs that still emit legacy common-type symbols.

The rule is explicitly listed in @azure-tools/typespec-azure-rulesets/resource-manager but disabled by default for staged rollout. Existing Azure service specs and repo samples still select older ARM common-types versions, so enabling it immediately would convert those existing warnings into CI failures. Teams can opt in directly, and the default ruleset can be flipped after specs are updated or intentionally suppressed.

How the promoted TypeSpec linter works

The promoted rule runs once at program root and visits TypeSpec services registered as ARM provider namespaces. It discovers the latest available ARM common-types version from Azure.ResourceManager.CommonTypes.Versions, compares the effective @armCommonTypesVersion on the service namespace or each version enum member, and reports older selections on the namespace or enum-member target.

When an API version already selects the latest common-types version, it projects versioned services with the same versioning mutators used by the emitter, resolves HTTP operations from the projected service, and walks operation parameters, request bodies, and response bodies. It uses HTTP metadata visibility (resolveRequestVisibility, Visibility.Read, and createMetadataInfo().isPayloadProperty) so properties excluded from the emitted payload are not false positives. For each reachable ARM common type, it asks getArmCommonTypeOpenAPIRef which common-types file would be emitted for that service/version and warns when that resolved reference is older than the latest version.

Diagnostics are deduplicated per target plus resolved reference identity, so repeated traversal of the same target does not spam while two distinct operations producing the same legacy reference each get actionable diagnostics. This intentionally differs from the Swagger validator's emitted-$ref occurrence count while preserving project-level behavior.

Fixture-to-native test mapping

Source fixtures are linked from the lintdiff source branch and converted into native vitest coverage in packages/typespec-azure-resource-manager/test/rules/use-latest-version-of-common-types.test.ts:

The native tests use direct TypeSpec snippets and expected diagnostics rather than copying lintdiff snapshots.

Migration evidence

Detailed migration evidence is in the source rule's migration report: https://github.com/Azure/typespec-azure/blob/feature/lintdiff-latest-version-common-types/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/migration.md

That report records the focused fixture tests, full-corpus comparison, one-sided project explanations, known validator stale-map behavior, compile failures, and remaining uncertainty. In short: after aligning selected API population and excluding known stale validator-map false positives, both sides fire in the same 384 successfully compiled projects, with raw diagnostic counts intentionally different because Swagger reports emitted $ref occurrences while TypeSpec reports semantic source usages and version selections.

Validation

  • pnpm --filter @azure-tools/typespec-azure-resource-manager exec vitest run test/rules/use-latest-version-of-common-types.test.ts
  • pnpm -r --filter "@azure-tools/typespec-azure-resource-manager..." build
  • pnpm --filter @azure-tools/typespec-azure-resource-manager build
  • pnpm --filter @azure-tools/typespec-azure-resource-manager lint
  • pnpm --filter @azure-tools/typespec-azure-resource-manager regen-docs
  • pnpm --filter @azure-tools/typespec-azure-rulesets build
  • pnpm --filter @azure-tools/typespec-azure-rulesets test
  • pnpm --filter @azure-tools/typespec-azure-playground-website exec vitest run test/validate-samples.test.ts
  • pnpm --filter @azure-tools/typespec-samples exec vitest run test/samples.test.ts -t "legacy/legacy-operations"
  • pnpm --filter @azure-tools/typespec-azure-resource-manager test
  • Focused code review of promotion diff: no blocking findings.
  • git diff --check

Validation blocker

pnpm validate:pr was attempted on the final branch but hung after printing only Branch is up to date for more than 13 minutes. The process was idle and was stopped. The narrower promotion validations above completed successfully.

Promotion sync policy

If review finds a semantic gap after promotion, this PR should be blocked until the user explicitly reopens lintdiff rule repair. The done lintdiff source rule was treated as immutable during promotion and was not edited here.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@msyyc Yuchao Yan (msyyc) added the int:azure-specs Run integration tests against azure-rest-api-specs label Aug 20, 2026
@microsoft-github-policy-service microsoft-github-policy-service Bot added lib:azure-resource-manager Issues for @azure-tools/typespec-azure-core library meta:website TypeSpec.io updates linter Issues related to linter rules labels Aug 20, 2026
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • @azure-tools/typespec-azure-resource-manager
  • @azure-tools/typespec-azure-rulesets
Show changes

@azure-tools/typespec-azure-resource-manager - feature ✏️

Add the use-latest-version-of-common-types ARM lint rule that warns when services select or emit older ARM common-types versions instead of the latest available common-types version.

@azure-tools/typespec-azure-rulesets - internal ✏️

Register the ARM use-latest-version-of-common-types lint rule as disabled in the resource manager ruleset.

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

📦 Package size report

✅ No notable package size changes compared to the base branch.

13 package(s) with no notable change
Package Packed (base → head) Δ Packed Unpacked (base → head) Δ Unpacked
@azure-tools/typespec-azure-resource-manager 182.71 KB → 182.80 KB +90 B (+0.0%) 1.10 MB → 1.10 MB +665 B (+0.1%)
@azure-tools/typespec-azure-rulesets 5.34 KB → 5.37 KB +24 B (+0.4%) 33.37 KB → 33.58 KB +220 B (+0.6%)
@azure-tools/typespec-java 13.51 MB → 13.51 MB -8 B (-0.0%) 15.04 MB → 15.04 MB
@azure-tools/azure-http-specs 146.76 KB → 146.76 KB 1.16 MB → 1.16 MB
@azure-tools/typespec-autorest 81.23 KB → 81.23 KB 396.61 KB → 396.61 KB
@azure-tools/typespec-autorest-canonical 7.42 KB → 7.42 KB 26.00 KB → 26.00 KB
@azure-tools/typespec-azure-core 130.84 KB → 130.84 KB 709.84 KB → 709.84 KB
@azure-tools/typespec-azure-portal-core 42.46 KB → 42.46 KB 193.15 KB → 193.15 KB
@azure-tools/typespec-client-generator-core 237.09 KB → 237.09 KB 1.27 MB → 1.27 MB
@azure-tools/typespec-go 261.39 KB → 261.39 KB 1.34 MB → 1.34 MB
@azure-tools/typespec-metadata 15.91 KB → 15.91 KB 62.26 KB → 62.26 KB
@azure-tools/typespec-python 42.22 KB → 42.22 KB 164.91 KB → 164.91 KB
@azure-tools/typespec-ts 540.59 KB → 540.59 KB 2.60 MB → 2.60 MB

Packed = gzipped .tgz published to npm. Unpacked = total extracted size. 🆕 added, 🗑️ removed. Packages from the core/ submodule are not included.
🔴 grew · 🟢 shrank — only changes of at least 512 B and 0.5% are marked.

@pkg-pr-new

pkg-pr-new Bot commented Aug 20, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@azure-tools/typespec-azure-resource-manager@5271
npm i https://pkg.pr.new/@azure-tools/typespec-azure-rulesets@5271

commit: aeb0eab

@azure-sdk-automation

Copy link
Copy Markdown
Contributor

You can try these changes here

🛝 Playground 🌐 Website

Keep the promoted ARM rule available while avoiding immediate failures in existing samples and azure-rest-api-specs projects that still select older common-types versions.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 8ed00e6d-dd0a-40f7-8871-ee32f0f371fb
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 8ed00e6d-dd0a-40f7-8871-ee32f0f371fb
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 8ed00e6d-dd0a-40f7-8871-ee32f0f371fb

@catalinaperalta catalinaperalta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, just some comments to improve clarity

continue;
}

const projected = unsafe_mutateSubgraphWithNamespace(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering if it's ok to use this function Mark Cowlishaw (@markcowl) or there's a better function to use?

@msyyc Yuchao Yan (msyyc) Aug 25, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context: we need this projection step because the rule walks HTTP operations and payload types for each effective API version. Versioning decorators like @added, @removed, and @typeChangedFrom don't rewrite the original service graph, so walking the unprojected namespace could report legacy common types for the wrong API version.

getVersioningMutators(...) gives us the per-version mutator, and unsafe_mutateSubgraphWithNamespace(...) applies it to produce the same kind of version-specific namespace used by the OpenAPI3 and Autorest emitters before generating per-version output. I don't see a stable replacement API in this repo right now, so keeping this matches the existing emitter pattern.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah we definitely do not want to start mutating everything here this is extremely costly

@msyyc Yuchao Yan (msyyc) Aug 31, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catalinaperalta / Timothee Guerin (@timotheeguerin) Addressed in f15a77b. I removed unsafe_mutateSubgraphWithNamespace and replaced the per-version whole-program projection with direct traversal using resolved version metadata, including dependency-specific versions, availability, and historical property/return types. The existing cases remain covered, and I added regressions for added/removed properties, child namespaces, dependency type changes, and @typeChangedFrom/@returnTypeChangedFrom.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9f3bdb44-9302-42a3-9b45-ea02a4e8f0a6
Replace per-version whole-program projection with dependency-aware version metadata traversal and cover availability and historical type changes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: bb0b0075-253e-4e28-a073-fc5fd64708b1
Update the title to reflect the latest version usage.
Yuchao Yan (msyyc) and others added 3 commits August 31, 2026 19:39
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Resolve linter registration and documentation conflicts by preserving both common-types and pageable rules.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

int:azure-specs Run integration tests against azure-rest-api-specs lib:azure-resource-manager Issues for @azure-tools/typespec-azure-core library linter Issues related to linter rules meta:website TypeSpec.io updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants