diff --git a/.chronus/changes/promote-latest-version-common-types-2026-08-20-14-00-00.md b/.chronus/changes/promote-latest-version-common-types-2026-08-20-14-00-00.md new file mode 100644 index 0000000000..d341f1fcda --- /dev/null +++ b/.chronus/changes/promote-latest-version-common-types-2026-08-20-14-00-00.md @@ -0,0 +1,11 @@ +--- +changeKind: feature +packages: + - "@azure-tools/typespec-azure-resource-manager" +--- + +Extend `arm-common-types-version` to warn when an ARM service or API version selects an older common-types version, while preserving the existing warning for missing explicit version configuration. + +Latest-version warnings are now active wherever this existing rule is enabled, including the resource-manager ruleset. Update `@armCommonTypesVersion` to the latest version in `Azure.ResourceManager.CommonTypes.Versions`, or suppress `@azure-tools/typespec-azure-resource-manager/arm-common-types-version` when an older version is required for compatibility. + +The rule checks effective version selections only. diff --git a/packages/samples/specs/resource-manager/legacy/legacy-operations/main.tsp b/packages/samples/specs/resource-manager/legacy/legacy-operations/main.tsp index d172397b7e..7a79ab6427 100644 --- a/packages/samples/specs/resource-manager/legacy/legacy-operations/main.tsp +++ b/packages/samples/specs/resource-manager/legacy/legacy-operations/main.tsp @@ -19,6 +19,7 @@ namespace Microsoft.ContosoProviderHub; /** Contoso API versions */ enum Versions { /** 2021-10-01-preview version */ + #suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "Preserve older common-types schemas for this grandfathered API sample." @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) @Azure.Core.previewVersion `2021-10-01-preview`, diff --git a/packages/samples/specs/resource-manager/legacy/managed-identity/main.tsp b/packages/samples/specs/resource-manager/legacy/managed-identity/main.tsp index 2ae50c5e15..d828f8fca1 100644 --- a/packages/samples/specs/resource-manager/legacy/managed-identity/main.tsp +++ b/packages/samples/specs/resource-manager/legacy/managed-identity/main.tsp @@ -19,6 +19,7 @@ namespace Microsoft.UpdateTests; /** UpdateTests API versions */ enum Versions { /** 2023-03-01-preview version */ + #suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "This sample intentionally mixes v3 common types with the legacy v4 managed identity." @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v3) @previewVersion `2023-03-01-preview`, diff --git a/packages/samples/specs/resource-manager/legacy/non-standard-properties/main.tsp b/packages/samples/specs/resource-manager/legacy/non-standard-properties/main.tsp index 10e15b9e42..adb4db0a00 100644 --- a/packages/samples/specs/resource-manager/legacy/non-standard-properties/main.tsp +++ b/packages/samples/specs/resource-manager/legacy/non-standard-properties/main.tsp @@ -16,6 +16,7 @@ namespace Microsoft.ContosoProviderHub; /** Contoso API versions */ enum Versions { /** 2021-10-01-preview version */ + #suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "Preserve older common-types schemas for this grandfathered API sample." @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) @Azure.Core.previewVersion `2021-10-01-preview`, diff --git a/packages/samples/specs/resource-manager/legacy/nsp/main.tsp b/packages/samples/specs/resource-manager/legacy/nsp/main.tsp index 6648c0a745..f74f262469 100644 --- a/packages/samples/specs/resource-manager/legacy/nsp/main.tsp +++ b/packages/samples/specs/resource-manager/legacy/nsp/main.tsp @@ -17,6 +17,7 @@ namespace Microsoft.ContosoProviderHub; /** Contoso API versions */ enum Versions { /** 2025-11-19-preview version */ + #suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "Preserve older common-types schemas for this grandfathered API sample." @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) @previewVersion `2025-11-19-preview`, diff --git a/packages/samples/specs/resource-manager/legacy/optional-body/main.tsp b/packages/samples/specs/resource-manager/legacy/optional-body/main.tsp index cecd27f2d7..4607efd76b 100644 --- a/packages/samples/specs/resource-manager/legacy/optional-body/main.tsp +++ b/packages/samples/specs/resource-manager/legacy/optional-body/main.tsp @@ -13,6 +13,7 @@ namespace Microsoft.OptionalBody; interface Operations extends Azure.ResourceManager.Operations {} enum Versions { + #suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "Preserve older common-types schemas for this grandfathered API sample." @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) @Azure.Core.previewVersion `2025-01-01-preview`, diff --git a/packages/samples/specs/resource-manager/legacy/polymorphic-resource/main.tsp b/packages/samples/specs/resource-manager/legacy/polymorphic-resource/main.tsp index efa4128fa9..a5a0cbe313 100644 --- a/packages/samples/specs/resource-manager/legacy/polymorphic-resource/main.tsp +++ b/packages/samples/specs/resource-manager/legacy/polymorphic-resource/main.tsp @@ -19,6 +19,7 @@ namespace Microsoft.ContosoProviderHub; /** Contoso API versions */ enum Versions { /** 2021-10-01-preview version */ + #suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "Preserve older common-types schemas for this grandfathered API sample." @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) @Azure.Core.previewVersion `2025-10-01-preview`, diff --git a/packages/samples/specs/resource-manager/legacy/private-endpoints/main.tsp b/packages/samples/specs/resource-manager/legacy/private-endpoints/main.tsp index c5ce7a69f1..a7fd411267 100644 --- a/packages/samples/specs/resource-manager/legacy/private-endpoints/main.tsp +++ b/packages/samples/specs/resource-manager/legacy/private-endpoints/main.tsp @@ -17,6 +17,7 @@ namespace Microsoft.ContosoProviderHub; /** Contoso API versions */ enum Versions { /** 2021-10-01-preview version */ + #suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "Preserve older common-types schemas for this grandfathered API sample." @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) @previewVersion `2021-10-01-preview`, diff --git a/packages/samples/specs/resource-manager/legacy/private-links/main.tsp b/packages/samples/specs/resource-manager/legacy/private-links/main.tsp index d03174417a..c2ab38b5be 100644 --- a/packages/samples/specs/resource-manager/legacy/private-links/main.tsp +++ b/packages/samples/specs/resource-manager/legacy/private-links/main.tsp @@ -17,6 +17,7 @@ namespace Microsoft.ContosoProviderHub; /** Contoso API versions */ enum Versions { /** 2021-10-01-preview version */ + #suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "Preserve older common-types schemas for this grandfathered API sample." @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) @previewVersion `2021-10-01-preview`, diff --git a/packages/samples/specs/resource-manager/legacy/rename-operations/main.tsp b/packages/samples/specs/resource-manager/legacy/rename-operations/main.tsp index 98c0d272ba..b38e71acc5 100644 --- a/packages/samples/specs/resource-manager/legacy/rename-operations/main.tsp +++ b/packages/samples/specs/resource-manager/legacy/rename-operations/main.tsp @@ -12,6 +12,7 @@ namespace Microsoft.RenamedOperations; interface Operations extends Azure.ResourceManager.Operations {} enum Versions { + #suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "Preserve older common-types schemas for this grandfathered API sample." @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) @Azure.Core.previewVersion `2025-01-01-preview`, diff --git a/packages/samples/specs/resource-manager/legacy/static-routes/main.tsp b/packages/samples/specs/resource-manager/legacy/static-routes/main.tsp index 1ee47938c4..cfdda2b849 100644 --- a/packages/samples/specs/resource-manager/legacy/static-routes/main.tsp +++ b/packages/samples/specs/resource-manager/legacy/static-routes/main.tsp @@ -20,6 +20,7 @@ namespace Microsoft.ContosoProviderHub; interface Operations extends Azure.ResourceManager.Operations {} enum Versions { + #suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "Preserve older common-types schemas for this grandfathered API sample." @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) @previewVersion `2025-06-01-preview`, diff --git a/packages/samples/specs/resource-manager/resource-common-properties/private-links/main.tsp b/packages/samples/specs/resource-manager/resource-common-properties/private-links/main.tsp index 0e42edc211..a1466683d3 100644 --- a/packages/samples/specs/resource-manager/resource-common-properties/private-links/main.tsp +++ b/packages/samples/specs/resource-manager/resource-common-properties/private-links/main.tsp @@ -16,10 +16,12 @@ namespace Microsoft.PrivateLinkTest; @doc("The PrivateLinkTest service versions.") enum Versions { + #suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "This sample demonstrates private-link schema compatibility across older common-types versions." @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v4) @doc("The 2023-08-01 version.") `2023-08-01`, + #suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "This sample demonstrates private-link schema compatibility across older common-types versions." @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) @doc("The 2023-11-01-preview version.") @Azure.Core.previewVersion diff --git a/packages/typespec-azure-resource-manager/README.md b/packages/typespec-azure-resource-manager/README.md index 90d0c1b482..2d9375e526 100644 --- a/packages/typespec-azure-resource-manager/README.md +++ b/packages/typespec-azure-resource-manager/README.md @@ -35,7 +35,7 @@ Available ruleSets: | [`@azure-tools/typespec-azure-resource-manager/arm-no-path-casing-conflicts`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-no-path-casing-conflicts) | Operation paths must be unique when compared case-insensitively. | | [`@azure-tools/typespec-azure-resource-manager/scope-path-segment-casing`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/scope-path-segment-casing) | ARM paths must use the canonical subscriptions and resourceGroups segment casing. | | [`@azure-tools/typespec-azure-resource-manager/no-override-props`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/no-override-props) | Disallow redefining properties already defined in a base type. | -| [`@azure-tools/typespec-azure-resource-manager/arm-common-types-version`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-common-types-version) | Specify the ARM common-types version using @armCommonTypesVersion. | +| [`@azure-tools/typespec-azure-resource-manager/arm-common-types-version`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-common-types-version) | Specify the latest ARM common-types version using @armCommonTypesVersion. | | [`@azure-tools/typespec-azure-resource-manager/arm-delete-operation-response-codes`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-delete-operation-response-codes) | Ensure delete operations have the appropriate status codes. | | [`@azure-tools/typespec-azure-resource-manager/arm-put-operation-response-codes`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-put-operation-response-codes) | Ensure put operations have the appropriate status codes. | | [`@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-post-operation-response-codes) | Ensure post operations have the appropriate status codes. | diff --git a/packages/typespec-azure-resource-manager/src/rules/arm-common-types-version.md b/packages/typespec-azure-resource-manager/src/rules/arm-common-types-version.md index 27515d61e2..3d3f19361e 100644 --- a/packages/typespec-azure-resource-manager/src/rules/arm-common-types-version.md +++ b/packages/typespec-azure-resource-manager/src/rules/arm-common-types-version.md @@ -1,10 +1,20 @@ -If the same common types version is used across all service versions, this decorator should be applied either on the service namespace or on each version enum member. If the common types version is updated in later versions, the decorator should appear on each version enum member. +ARM services should explicitly select the latest version in +`Azure.ResourceManager.CommonTypes.Versions` using `@armCommonTypesVersion`. +Apply the decorator to the service namespace when all API versions use the same +common-types version. A decorator on an API version enum member overrides the +namespace selection for that API version. + +The rule warns when explicit version configuration is missing or when an +effective selection is older than the latest available version. It checks +version selections, not individual legacy type usages. ## Impact - **Area:** API, SDK -Indicates common-types are not being used, which normally surfaces as other violations too. +Missing or outdated common-types selections can leave an API and its generated +SDKs using older shared schemas and parameters. Latest-version warnings are +enabled wherever this rule is enabled, including the resource-manager ruleset. ## ❌ Incorrect @@ -13,33 +23,47 @@ Indicates common-types are not being used, which normally surfaces as other viol namespace Microsoft.Contoso; ``` -## ✅ Correct - -Apply `@armCommonTypesVersion` on the namespace: +An explicit older selection also produces a warning: ```tsp @armProviderNamespace -@armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) +@armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v3) namespace Microsoft.Contoso; ``` -## ✅ Correct (per version) +## ✅ Correct -Apply `@armCommonTypesVersion` on each version enum member: +Select the latest version on the service namespace: ```tsp @armProviderNamespace +@armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v6) namespace Microsoft.Contoso; +``` -enum Versions { - @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v4) - v1, +Alternatively, apply `@armCommonTypesVersion` to every API version enum member +instead of the namespace. Every effective selection must use the latest version +to satisfy this rule. - @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) - v2, -} -``` +As of August 2026, `v6` was the latest ARM common-types version. +Newer versions may exist; check `Azure.ResourceManager.CommonTypes.Versions` +before updating a service. + +## LintDiff Equivalent + +This rule covers the version-selection guidance of +[`LatestVersionOfCommonTypesMustBeUsed`](https://github.com/Azure/azure-openapi-validator/blob/main/docs/latest-version-of-common-types-must-be-used.md). +Unlike the OpenAPI rule, it does not inspect emitted common-type references. ## Suppression -Suppress only if no common-types are ever used and that is valid; otherwise associate a common-types version with each version in the Versions enum. +Prefer updating `@armCommonTypesVersion` to the latest version. Suppress only +when an older version is required for compatibility and the API and SDK impact +has been accepted, or when no common-types are used and omitting the selection +is intentional. + +Place `#suppress "@azure-tools/typespec-azure-resource-manager/arm-common-types-version" "Compatibility justification"` +above the service namespace for missing configuration or an unversioned +selection, or above the affected API version enum member for a versioned +selection. Suppressing the rule also suppresses its missing-configuration check +within that scope. diff --git a/packages/typespec-azure-resource-manager/src/rules/arm-common-types-version.ts b/packages/typespec-azure-resource-manager/src/rules/arm-common-types-version.ts index 3abb4cdd68..b5b9b73765 100644 --- a/packages/typespec-azure-resource-manager/src/rules/arm-common-types-version.ts +++ b/packages/typespec-azure-resource-manager/src/rules/arm-common-types-version.ts @@ -1,32 +1,40 @@ -import { type Program, type SemanticNodeListener, createRule, fileRef } from "@typespec/compiler"; -import { getAllHttpServices } from "@typespec/http"; +import { + type Program, + type SemanticNodeListener, + createRule, + fileRef, + listServices, + paramMessage, +} from "@typespec/compiler"; import { getVersion } from "@typespec/versioning"; +import { getArmCommonTypesVersion, getArmCommonTypesVersions } from "../common-types.js"; import { getArmProviderNamespace } from "../namespace.js"; /** - * The @armCommonTypesVersion decorator should be used to set the ARM - * common-types version used by the service. + * The @armCommonTypesVersion decorator should select the latest ARM common-types version. */ export const armCommonTypesVersionRule = createRule({ name: "arm-common-types-version", docs: fileRef.fromPackageRoot("src/rules/arm-common-types-version.md"), severity: "warning", - description: "Specify the ARM common-types version using @armCommonTypesVersion.", + description: "Specify the latest ARM common-types version using @armCommonTypesVersion.", url: "https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-common-types-version", messages: { default: "Specify the ARM common-types version using the @armCommonTypesVersion decorator on the service namespace or on each version of the service version enum.", + outdated: paramMessage`Use the latest ARM common-types version '${"latestVersion"}' instead of '${"currentVersion"}'.`, }, create(context): SemanticNodeListener { return { root: (program: Program) => { - const [services, _] = getAllHttpServices(program); - for (const service of services) { - if (!getArmProviderNamespace(program, service.namespace)) { + // The common-types registry stores versions newest first. + const latestVersion = getArmCommonTypesVersions(program)?.allVersions[0]?.name; + for (const { type: namespace } of listServices(program)) { + if (!getArmProviderNamespace(program, namespace)) { continue; } - const versionMap = getVersion(program, service.namespace); + const versionMap = getVersion(program, namespace); // If the namespace is versioned and not all versions have the // common-types version and if the service namespace doesn't have a // common-types version, raise a diagnostic. @@ -42,13 +50,32 @@ export const armCommonTypesVersionRule = createRule({ ), ) ) && - !service.namespace.decorators.find( - (x) => x.definition?.name === "@armCommonTypesVersion", - ) + !namespace.decorators.find((x) => x.definition?.name === "@armCommonTypesVersion") ) { context.reportDiagnostic({ - target: service.namespace, + messageId: "default", + target: namespace, }); + continue; + } + + if (latestVersion === undefined) { + continue; + } + + const namespaceVersion = getArmCommonTypesVersion(program, namespace); + const targets = versionMap + ? versionMap.getVersions().map((version) => version.enumMember) + : [namespace]; + for (const target of targets) { + const currentVersion = getArmCommonTypesVersion(program, target) ?? namespaceVersion; + if (currentVersion !== undefined && currentVersion !== latestVersion) { + context.reportDiagnostic({ + messageId: "outdated", + target, + format: { latestVersion, currentVersion }, + }); + } } } }, diff --git a/packages/typespec-azure-resource-manager/test/rules/arm-common-types-version.test.ts b/packages/typespec-azure-resource-manager/test/rules/arm-common-types-version.test.ts index 2c50856509..bacc291e65 100644 --- a/packages/typespec-azure-resource-manager/test/rules/arm-common-types-version.test.ts +++ b/packages/typespec-azure-resource-manager/test/rules/arm-common-types-version.test.ts @@ -1,18 +1,27 @@ import { Tester } from "#test/tester.js"; import { + type DiagnosticMatch, type LinterRuleTester, - type TesterInstance, createLinterRuleTester, } from "@typespec/compiler/testing"; import { beforeEach, it } from "vitest"; import { armCommonTypesVersionRule } from "../../src/rules/arm-common-types-version.js"; -let runner: TesterInstance; let tester: LinterRuleTester; +const ruleCode = "@azure-tools/typespec-azure-resource-manager/arm-common-types-version"; +const latestVersion = "v6"; +const missingVersionMessage = + "Specify the ARM common-types version using the @armCommonTypesVersion decorator on the service namespace or on each version of the service version enum."; +const outdatedVersionDiagnostic = (pos: number, currentVersion = "v3"): DiagnosticMatch => ({ + code: ruleCode, + pos, + message: `Use the latest ARM common-types version '${latestVersion}' instead of '${currentVersion}'.`, +}); + beforeEach(async () => { - runner = await Tester.createInstance(); + const runner = await Tester.createInstance(); tester = createLinterRuleTester( runner, armCommonTypesVersionRule, @@ -24,7 +33,7 @@ it("emits diagnostic when a version in the enum is missing a common type version await tester .expect( ` - @service(#{ title: "Test" }) + /*target*/@service(#{ title: "Test" }) @versioned(Service.Versions) @armProviderNamespace("Contoso.Service") namespace Service; @@ -37,23 +46,27 @@ it("emits diagnostic when a version in the enum is missing a common type version } `, ) - .toEmitDiagnostics({ - code: "@azure-tools/typespec-azure-resource-manager/arm-common-types-version", - }); + .toEmitDiagnostics((x) => ({ + code: ruleCode, + pos: x.pos.target.pos, + message: missingVersionMessage, + })); }); it("emits diagnostic when unversioned service namespace is missing a common type version", async () => { await tester .expect( ` - @service(#{ title: "Test" }) + /*target*/@service(#{ title: "Test" }) @armProviderNamespace("Contoso.Service") namespace Service; `, ) - .toEmitDiagnostics({ - code: "@azure-tools/typespec-azure-resource-manager/arm-common-types-version", - }); + .toEmitDiagnostics((x) => ({ + code: ruleCode, + pos: x.pos.target.pos, + message: missingVersionMessage, + })); }); it("does not emit when the service namespace has a common type version without version on enum values", async () => { @@ -63,7 +76,7 @@ it("does not emit when the service namespace has a common type version without v @service(#{ title: "Test" }) @versioned(Service.Versions) @armProviderNamespace("Contoso.Service") - @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v4) + @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v6) namespace Service; enum Versions { @@ -85,13 +98,188 @@ it("does not emit when the service version enum has a common type version on all namespace Service; enum Versions { - @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v4) + @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v6) v1; - @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) + @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v6) v2; } `, ) .toBeValid(); }); + +it("does not emit when an unversioned service selects the latest version", async () => { + await tester + .expect( + ` + @armProviderNamespace + @armCommonTypesVersion(CommonTypes.Versions.v6) + namespace Service; + `, + ) + .toBeValid(); +}); + +it.each(["v3", "v4", "v5"])( + "reports an older unversioned namespace selection: %s", + async (version) => { + await tester + .expect( + ` + /*target*/@armProviderNamespace + @armCommonTypesVersion(CommonTypes.Versions.${version}) + namespace Service; + `, + ) + .toEmitDiagnostics((x) => outdatedVersionDiagnostic(x.pos.target.pos, version)); + }, +); + +it("reports an older namespace selection for each API version", async () => { + await tester + .expect( + ` + @armProviderNamespace + @armCommonTypesVersion(CommonTypes.Versions.v3) + @versioned(Versions) + namespace Service; + + enum Versions { + /*v1*/v1, + /*v2*/v2, + } + `, + ) + .toEmitDiagnostics((x) => [ + outdatedVersionDiagnostic(x.pos.v1.pos), + outdatedVersionDiagnostic(x.pos.v2.pos), + ]); +}); + +it("reports an older enum-member override of a latest namespace selection", async () => { + await tester + .expect( + ` + @armProviderNamespace + @armCommonTypesVersion(CommonTypes.Versions.v6) + @versioned(Versions) + namespace Service; + + enum Versions { + /*target*/@armCommonTypesVersion(CommonTypes.Versions.v3) + v1, + v2, + } + `, + ) + .toEmitDiagnostics((x) => outdatedVersionDiagnostic(x.pos.target.pos)); +}); + +it("accepts a latest enum-member override of an older namespace selection", async () => { + await tester + .expect( + ` + @armProviderNamespace + @armCommonTypesVersion(CommonTypes.Versions.v3) + @versioned(Versions) + namespace Service; + + enum Versions { + @armCommonTypesVersion(CommonTypes.Versions.v6) + v1, + } + `, + ) + .toBeValid(); +}); + +it("checks explicit per-version selections without a namespace selection", async () => { + await tester + .expect( + ` + @armProviderNamespace + @versioned(Versions) + namespace Service; + + enum Versions { + /*target*/@armCommonTypesVersion(CommonTypes.Versions.v3) + v1, + @armCommonTypesVersion(CommonTypes.Versions.v6) + v2, + } + `, + ) + .toEmitDiagnostics((x) => outdatedVersionDiagnostic(x.pos.target.pos)); +}); + +it("supports string version selections", async () => { + await tester + .expect( + ` + /*target*/@armProviderNamespace + @armCommonTypesVersion("v3") + namespace Service; + `, + ) + .toEmitDiagnostics((x) => outdatedVersionDiagnostic(x.pos.target.pos)); +}); + +it("checks each ARM service independently", async () => { + await tester + .expect( + ` + /*target*/@armProviderNamespace + @armCommonTypesVersion(CommonTypes.Versions.v3) + namespace Older {} + + @armProviderNamespace + @armCommonTypesVersion(CommonTypes.Versions.v6) + namespace Current {} + `, + ) + .toEmitDiagnostics((x) => outdatedVersionDiagnostic(x.pos.target.pos)); +}); + +it("does not check non-ARM services or Azure library namespaces", async () => { + await tester + .expect( + ` + @service + @armCommonTypesVersion(CommonTypes.Versions.v3) + namespace DataPlane {} + + namespace Azure.Core { + model TestModel {} + } + + @armLibraryNamespace + namespace Azure.ResourceManager.TestLibrary {} + `, + ) + .toBeValid(); +}); + +it("checks only the selected version, not legacy common-type usages", async () => { + await tester + .expect( + ` + @armProviderNamespace + @armCommonTypesVersion(CommonTypes.Versions.v6) + namespace Service; + + model Widget is TrackedResource { + ...ResourceNameParameter; + ...Legacy.ManagedServiceIdentityV4Property; + } + + model WidgetProperties {} + + @armResourceOperations + interface Widgets { + get is ArmResourceRead; + } + `, + ) + .toBeValid(); +}); diff --git a/packages/typespec-azure-rulesets/test/validate-rules-defined.test.ts b/packages/typespec-azure-rulesets/test/validate-rules-defined.test.ts index 013e7451be..2d88f1800f 100644 --- a/packages/typespec-azure-rulesets/test/validate-rules-defined.test.ts +++ b/packages/typespec-azure-rulesets/test/validate-rules-defined.test.ts @@ -90,6 +90,21 @@ describe("expect all rules to be defined", () => { } }); + it("enables consolidated ARM common-types version guidance", () => { + const ruleset = $linter.ruleSets?.["resource-manager"]; + ok(ruleset); + ok(ruleset.enable?.["@azure-tools/typespec-azure-resource-manager/arm-common-types-version"]); + const removedRule = + "@azure-tools/typespec-azure-resource-manager/use-latest-version-of-common-types"; + strictEqual(ruleset.enable?.[removedRule], undefined); + strictEqual(ruleset.disable?.[removedRule], undefined); + ok( + !ResourceManagerLinter.rules.some( + (rule) => rule.name === "use-latest-version-of-common-types", + ), + ); + }); + it("keeps RPaaS extension-routing guidance opt-in in the general Azure rulesets", () => { const ruleName = "@azure-tools/typespec-azure-resource-manager/use-scope-for-extension-routing"; strictEqual($linter.ruleSets?.["resource-manager"].enable?.[ruleName], false); diff --git a/website/src/content/docs/docs/howtos/ARM/arm-rules.md b/website/src/content/docs/docs/howtos/ARM/arm-rules.md index a086050854..59d1c8936d 100644 --- a/website/src/content/docs/docs/howtos/ARM/arm-rules.md +++ b/website/src/content/docs/docs/howtos/ARM/arm-rules.md @@ -99,7 +99,7 @@ The tables below provide guidance to rule authors and ARM reviewers on how to ev | Rule | LintDiff | Impact | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [`arm-common-types-version`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-common-types-version/) | — | **API, SDK.** Indicates that common-types are not used, though this should produce other violations. | +| [`arm-common-types-version`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-common-types-version/) | [`LatestVersionOfCommonTypesMustBeUsed`](https://github.com/Azure/azure-openapi-validator/blob/main/docs/latest-version-of-common-types-must-be-used.md) (effective version selection only) | **API, SDK.** Missing or outdated common-types selections can expose stale shared schemas or parameters in generated APIs and SDKs. | | [`arm-custom-resource-no-key`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-custom-resource-no-key/) | — | **API, SDK.** Mainly a correctness issue; the name property should use `@key`. | | [`arm-custom-resource-usage-discourage`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-custom-resource-usage-discourage/) | — | **API.** The resource does not use ARM common-types. | | [`arm-delete-operation-response-codes`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-delete-operation-response-codes/) | [`DeleteResponseCodes`](https://github.com/Azure/azure-openapi-validator/blob/main/docs/delete-response-codes.md) | **API.** RPC violation. | diff --git a/website/src/content/docs/docs/howtos/ARM/rpc-guidelines-coverage.md b/website/src/content/docs/docs/howtos/ARM/rpc-guidelines-coverage.md index 237dc430af..a7a9dc215b 100644 --- a/website/src/content/docs/docs/howtos/ARM/rpc-guidelines-coverage.md +++ b/website/src/content/docs/docs/howtos/ARM/rpc-guidelines-coverage.md @@ -184,7 +184,7 @@ The following TypeSpec linting rules enforce ARM conventions that are not explic | TypeSpec Rule | Description | | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | -| `arm/arm-common-types-version` | Requires specifying the ARM common-types version using `@armCommonTypesVersion`. | +| `arm/arm-common-types-version` | Requires explicitly selecting the latest ARM common-types version using `@armCommonTypesVersion`. | | [`arm/arm-no-record`](/docs/libraries/azure-resource-manager/rules/arm-no-record) | Prevents use of `Record` types for ARM resources, which produce `additionalProperties` in OpenAPI. | | `arm/arm-resource-duplicate-property` | Warns about duplicate properties in resource definitions. | | `arm/arm-resource-interface-requires-decorator` | Requires `@armResourceOperations` decorator on resource interfaces. | diff --git a/website/src/content/docs/docs/libraries/azure-resource-manager/reference/linter.md b/website/src/content/docs/docs/libraries/azure-resource-manager/reference/linter.md index 0f22771a2d..f2f9693119 100644 --- a/website/src/content/docs/docs/libraries/azure-resource-manager/reference/linter.md +++ b/website/src/content/docs/docs/libraries/azure-resource-manager/reference/linter.md @@ -29,7 +29,7 @@ Available ruleSets: | [`@azure-tools/typespec-azure-resource-manager/arm-no-path-casing-conflicts`](../rules/arm-no-path-casing-conflicts.md) | Operation paths must be unique when compared case-insensitively. | | [`@azure-tools/typespec-azure-resource-manager/scope-path-segment-casing`](../rules/scope-path-segment-casing.md) | ARM paths must use the canonical subscriptions and resourceGroups segment casing. | | [`@azure-tools/typespec-azure-resource-manager/no-override-props`](../rules/no-override-props.md) | Disallow redefining properties already defined in a base type. | -| [`@azure-tools/typespec-azure-resource-manager/arm-common-types-version`](../rules/arm-common-types-version.md) | Specify the ARM common-types version using @armCommonTypesVersion. | +| [`@azure-tools/typespec-azure-resource-manager/arm-common-types-version`](../rules/arm-common-types-version.md) | Specify the latest ARM common-types version using @armCommonTypesVersion. | | [`@azure-tools/typespec-azure-resource-manager/arm-delete-operation-response-codes`](../rules/arm-delete-operation-response-codes.md) | Ensure delete operations have the appropriate status codes. | | [`@azure-tools/typespec-azure-resource-manager/arm-put-operation-response-codes`](../rules/arm-put-operation-response-codes.md) | Ensure put operations have the appropriate status codes. | | [`@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes`](../rules/arm-post-operation-response-codes.md) | Ensure post operations have the appropriate status codes. |