diff --git a/website/src/content/docs/docs/howtos/Versioning/ARM/01-about-versioning.md b/website/src/content/docs/docs/howtos/Versioning/ARM/01-about-versioning.md new file mode 100644 index 0000000000..5bb86c7ccb --- /dev/null +++ b/website/src/content/docs/docs/howtos/Versioning/ARM/01-about-versioning.md @@ -0,0 +1,31 @@ +--- +title: About Versioning ARM APIs in the azure-rest-api-specs Repository +llmstxt: true +--- + +TypeSpec uses a versioning library that models the changes in each new version of the API, rather than having a separate api description for each api-version. +This works well when APIs evolve according to versioning guidelines, without breaking changes. For the most part, this means that this system is very good at +modeling differences between stable api-versions for Azure APIs, but can be cumbersome when describing differences between preview APIs. + +Additionally, in Azure. preview APIs have a limited lifespan and limited support in SDKs and other tooling. For this reason and others, specs should only have a _single active preview_ at any point during the spec development process. + +At the same time, Azure ResourceManager teams may need to maintain OpenAPI files for preview versions until they are retired, some reasons for this include: + +- RPaaS live validation support +- ARM registration support + +This document describes how to evolve APIs according to these guidelines, and how to meet both the single active preview guideline and the need to maintain some preview versions in Swagger in some situations, focusing on authoring of new APIs: + +- [How to add a new preview version when the last version was preview](./02-preview-after-preview.md) +- [How to add a new stable version when the last version was preview](./03-stable-after-preview.md) +- [How to add a new preview version when the last version was stable](./04-preview-after-stable.md) +- [How to add a new stable version when the last version was stable](./05-stable-after-stable.md) + +This document also describes how to move an existing multi-api typespec spec with multiple previews: + +- [How to convert a spec with multiple preview versions into a spec with a single active preview](./06-converting-specs.md) + +Additionally, there are some services that may always have features that remain in preview after a stable version is released. This can happen, for example, if there are multiple independent teams that own different resources in a service and operate on their own schedule. The recommended way to handle +this scenario is to model your ResourceProvider as multiple services, so each version and the corresponding SDKs can version independently. For some older services, this is not an option, so there is specialized guidance on how to maintain preview features over stable api releases: + +- [How to manage a single active preview if your service always has some features in preview](./07-perpetual-preview.md) diff --git a/website/src/content/docs/docs/howtos/Versioning/ARM/02-preview-after-preview.md b/website/src/content/docs/docs/howtos/Versioning/ARM/02-preview-after-preview.md new file mode 100644 index 0000000000..c857d0de72 --- /dev/null +++ b/website/src/content/docs/docs/howtos/Versioning/ARM/02-preview-after-preview.md @@ -0,0 +1,102 @@ +--- +title: Adding a Preview Version when the Last Version was a Preview +llmstxt: true +--- + +When the last api-version in your TypeSpec spec is a preview, adding a new preview version is simply replacing the latest preview version with a new preview version. +This includes: + +## Making Changes to your TypeSpec spec + +- Rename the latest preview version to match the new preview version, in all instances in the spec + - In vscode, highlight the version name and select `rename symbol` from the context menu to rename the version throughout your spec + - In any editor, search and replace the latest preview version in the spec with the new preview version +- Update the version value of this version to match the new api-version string, for example: + + ~~v2025_12_01_preview: "2025-12-01-preview",~~ + v2026_01_01_preview: "2026-01-01-preview", + +- The new preview version should already be the _last version_ of the versions enum, also ensure it is decorated with `@previewVersion` +- Update any mention in documentation of the old api-version to use the new api-version +- Change the name of the `examples` version folder for the latest preview to match the new preview version + + ```bash + > mv examples/2025-12-01-preview examples/2026-01-01-preview + ``` + +- Make changes to the API description based on how the API has changed between the preview versions (if it has) + - If any type that was introduced in the latest preview is _not_ in the new preview, simply delete the type + + ```diff lang=tsp + - @added(Versions.`2026-01-01-preview`) + - model Foo {} + ``` + + - If any type was removed in the latest preview but **appears** in the new preview, remove the decorator + + ```diff lang=tsp + - @removed(Versions.`2026-01-01-preview`) + model Bar {} + ``` + + - Similarly, if there is any change from the latest preview that does not apply to the new preview version, reverse the decorator (unlikely). + + ```diff lang=tsp + - @renamedFrom(Versions.`2026-01-01-preview`, "oldProp") + - newProp: string; + + oldProp: string; + ``` + + ```diff lang=tsp + - @typeChangedFrom(Versions.`2026-01-01-preview`, string) + - changedProp: int32; + + changedProp: string; + ``` + + - If any other types are removed in the new preview (unlikely) mark these with an `@removed` decorator referencing the new version + + ```diff lang=tsp + + @removed(Versions.`2026-01-01-preview`) + model Bar {} + ``` + + - If any types are added, renamed, or otherwise modified in the new version, mark them with the appropriate versioning decorator. + +- Add and modify examples to match the api changes + +## Preparing a PR into the azure-rest-api-specs repo + +- Navigate to the root directory of the repo in your local fork or clone + + ```bash + C:\repos\azure-rest-api-specs\specifications\myRp > cd \repos\azure-rest-api-specs + C:\repos\azure-rest-api-specs > + ``` + +- Pull the latest version from the repo + + ```bash + C:\repos\azure-rest-api-specs > git fetch upstream main + C:\repos\azure-rest-api-specs > git pull upstream main + ``` + +- Reinstall dependencies + + ```bash + C:\repos\azure-rest-api-specs > npm install + ``` + +- Compile your spec + + ```bash + C:\repos\azure-rest-api-specs > cd specification\myRpShortname\resource-manager\Microsoft.MyRP + C:\repos\azure-rest-api-specs\specification\myRpShortname\resource-manager\Microsoft.MyRP > npx tsp compile . + ``` + +- If you _don't_ need the older preview version, remove the OpenAPI directory for that version and update the `README.md` file to use the new version instead. + + ```bash + C:\repos\azure-rest-api-specs\specification\myRpShortname\resource-manager\Microsoft.MyRP > rm -r 2025-12-01-preview + ``` + +- If you _do_ need the older preview version, update README.md to include a new entry for the new preview version. diff --git a/website/src/content/docs/docs/howtos/Versioning/ARM/03-stable-after-preview.md b/website/src/content/docs/docs/howtos/Versioning/ARM/03-stable-after-preview.md new file mode 100644 index 0000000000..a98b495121 --- /dev/null +++ b/website/src/content/docs/docs/howtos/Versioning/ARM/03-stable-after-preview.md @@ -0,0 +1,125 @@ +--- +title: Adding a Stable Version when the Last Version was a Preview +llmstxt: true +--- + +When the last api-version in your TypeSpec spec is a preview, adding a new stable version means you must remove any preview types or other api-changes from the preview and only leave those type changes that are now stable. + +This includes the followign steps: + +## Making Changes to your TypeSpec spec + +- Add a new entry to the versions enum for the new stable version +- Update any mention in documentation of the old api-version to use the new api-version +- Change the name of the `examples` version folder for the latest preview to match the new stable version + + ```bash + > mv examples/2025-10-01-preview examples/2026-01-01 + ``` + +- Determine which type changes from the latest preview are now stable + - Update the versioning decorators for those changes to reference the new stable version + + ```diff lang=tsp + - @added(Versions.`2025-10-01-preview`) + + @added(Versions.`2026-01-01`) + remainingProperty?: string; + ``` + + - For changes in the latest preview (p) that are _not_ in the new stable version + - For any type with an `@added(p)` decorator, delete the type + + ```diff lang=tsp + - @added(Versions.`2025-10-01-preview`) + - model Foo {} + ``` + + - For any property or parameter with a `@typeChangedFrom(p, Type)` decorator, replace the property type with the `Type` argument, and then remove the decorator, for example + + ```diff lang=tsp + - @typeChangedFrom(Versions.`2025-12-01-preview`, string) + - property: int32; + + property: string; + ``` + + - For any operation with an `@returnTypeChangedFrom(p, ReturnType)` decorator, replace the return type with the `ReturnType` argument and then remove the decorator, for example: + + ```diff lang=tsp + - @returnTypeChangedFrom(Versions.`2025-12-01-preview`, void) + - move is ArmResourceActionSync(Widget, MoveOptions, MoveResult); + + move is ArmResourceActionSync(Widget, MoveOptions, void); + ``` + + - For any type with an `@renamedFrom(p, Name)` decorator, replace the name of the type with the `Name` argument in the decorator and remove the decorator, for example: + + ```diff lang=tsp + - @renamedFrom(Versions.`2025-12-01-preview`, "oldProperty") + - newProperty: int32; + + oldProperty: int32; + ``` + + - For any property or parameter with a `@madeOptional(p)` decorator, remove the decorator, and make the property required, for example: + + ```diff lang=tsp + - @madeOptional(Versions.`2025-12-01-preview`) + - property?: int32; + + property: int32; + ``` + + - For any type with an `@removed(p)` decorator, remove the decorator + + ```diff lang=tsp + - @removed( Versions.`2025-12-01-preview`) + property?: string; + ``` + +- Model any additional changes in the new stable version and mark with the appropriate versioning decorator, referencing the new stable version +- Remove the preview version from the version enum + + ```diff lang=tsp + enum Versions { + - @Azure.Core.previewVersion + - `2025-10-01-preview`, + + `2026-01-01`, + } + + ``` + +- Add and modify examples to match the api changes in the new stable version + +## Preparing a PR into the azure-rest-api-specs repo + +- Navigate to the root directory of the repo in your local fork or clone + + ```bash + C:\repos\azure-rest-api-specs\specifications\myRp > cd \repos\azure-rest-api-specs + C:\repos\azure-rest-api-specs > + ``` + +- Pull the latest version from the repo + + ```bash + C:\repos\azure-rest-api-specs > git fetch upstream main + C:\repos\azure-rest-api-specs > git pull upstream main + ``` + +- Reinstall dependencies + + ```bash + C:\repos\azure-rest-api-specs > npm install + ``` + +- Compile your spec + + ```bash + C:\repos\azure-rest-api-specs > cd specification\myRpShortname\resource-manager\Microsoft.MyRP + C:\repos\azure-rest-api-specs\specification\myRpShortname\resource-manager\Microsoft.MyRP > npx tsp compile . + ``` + +- If you _don't_ need the older preview version, remove the OpenAPI directory for that version and update the `README.md` file to use the new version instead. + + ```bash + C:\repos\azure-rest-api-specsC:\repos\azure-rest-api-specs\specification\myRpShortname\resource-manager\Microsoft.MyRP > rm -r 2025-12-01-preview + ``` + +- If you _do_ need the older preview version, update README.md to include a new entry for the new stable version. diff --git a/website/src/content/docs/docs/howtos/Versioning/ARM/04-preview-after-stable.md b/website/src/content/docs/docs/howtos/Versioning/ARM/04-preview-after-stable.md new file mode 100644 index 0000000000..e3a206a28a --- /dev/null +++ b/website/src/content/docs/docs/howtos/Versioning/ARM/04-preview-after-stable.md @@ -0,0 +1,79 @@ +--- +title: Adding a Preview Version when the Last Version was a Stable Version +llmstxt: true +--- + +When the latest api-version in your TypeSpec spec is a stable version, adding a new preview version is simply adding any new types and operations in the new preview and marking them with the appropriate versioning decoration, as described in [TypeSpec Versioning for Azure ResourceManager APIs](../../ARM/versioning.md). + +## Making Changes to your TypeSpec spec + +- Add a new version to the `Versions` enum in your spec for the new preview. + + ```diff lang=tsp + enum Versions { + `2025-10-01`, + + `2026-01-01-preview`, + } + ``` + +- Decorate this version with the `@previewVersion` decorator from the `Azure.Core` library + + ```diff lang=tsp + enum Versions { + `2025-10-01`, + + @Azure.Core.previewVersion + `2026-01-01-preview`, + } + ``` + +- Make changes to the API description based on how the API has changed + - If any types are removed in the new preview (unlikely) mark these with an `@removed` decorator referencing the new version + + ```diff lang=tsp + + @removed(Versions.`2026-01-01-preview`) + model Foo {} + ``` + + - If any types are added, renamed, or otherwise modified in the new version, mark them with the appropriate versioning decorator + + ```diff lang=tsp + + @added(Versions.`2026-01-01-preview`) + + model Foo {} + + + @renamedFrom(Versions.`2026-01-01-preview`, "Bar") + - model Bar {} + + model Baz {} + ``` + +- Create a new examples folder for the new version `examples\` and populate it with examples. + +## Preparing a PR into the azure-rest-api-specs repo + +- Navigate to the root directory of the repo in your local fork or clone + + ```bash + C:\repos\azure-rest-api-specs\specifications\myRp > cd \repos\azure-rest-api-specs + C:\repos\azure-rest-api-specs > + ``` + +- Pull the latest version from the repo + + ```bash + C:\repos\azure-rest-api-specs > git fetch upstream main + C:\repos\azure-rest-api-specs > git pull upstream main + ``` + +- Reinstall dependencies + + ```bash + C:\repos\azure-rest-api-specs > npm install + ``` + +- Compile your spec + + ```bash + C:\repos\azure-rest-api-specs > cd specification\myRpShortname\resource-manager\Microsoft.MyRP + C:\repos\azure-rest-api-specs\specification\myRpShortname\resource-manager\Microsoft.MyRP > npx tsp compile . + ``` + + - Update README.md to include a new entry for the new preview version. diff --git a/website/src/content/docs/docs/howtos/Versioning/ARM/05-stable-after-stable.md b/website/src/content/docs/docs/howtos/Versioning/ARM/05-stable-after-stable.md new file mode 100644 index 0000000000..ecf397977c --- /dev/null +++ b/website/src/content/docs/docs/howtos/Versioning/ARM/05-stable-after-stable.md @@ -0,0 +1,45 @@ +--- +title: Adding a Stable Version when the Last Version was a Stable Version +llmstxt: true +--- + +When the latest api-version in your TypeSpec spec is a stable version, adding a new stable version is simply adding any new types and operations in the new stable and marking them with the appropriate versioning decoration, as described in [TypeSpec Versioning for Azure ResourceManager APIs](../../ARM/versioning.md). + +## Making Changes to your TypeSpec spec + +- Add a new version to the `Versions` enum in your spec for the new stable api-version. +- Make changes to the API description based on how the API has changed + - If any types are removed in this stable (unlikely) mark these with an `@removed` decorator referencing the new version + - If any types are added, renamed, or otherwise modified in the new version, mark them with the appropriate versioning decorator +- Create a new examples folder for the new version `examples\` and populate it with examples. + +## Preparing a PR into the azure-rest-api-specs repo + +- Navigate to the root directory of the repo in your local fork or clone + + ```bash + C:\repos\azure-rest-api-specs\specifications\myRp > cd \repos\azure-rest-api-specs + C:\repos\azure-rest-api-specs > + ``` + +- Pull the latest version from the repo + + ```bash + C:\repos\azure-rest-api-specs > git fetch upstream main + C:\repos\azure-rest-api-specs > git pull upstream main + ``` + +- Reinstall dependencies + + ```bash + C:\repos\azure-rest-api-specs > npm install + ``` + +- Compile your spec + + ```bash + C:\repos\azure-rest-api-specs > cd specification\myRpShortname\resource-manager\Microsoft.MyRP + C:\repos\azure-rest-api-specs\specification\myRpShortname\resource-manager\Microsoft.MyRP > npx tsp compile . + ``` + + - Update README.md to include a new entry for the new stable version. diff --git a/website/src/content/docs/docs/howtos/Versioning/ARM/06-converting-specs.md b/website/src/content/docs/docs/howtos/Versioning/ARM/06-converting-specs.md new file mode 100644 index 0000000000..69706bfdc7 --- /dev/null +++ b/website/src/content/docs/docs/howtos/Versioning/ARM/06-converting-specs.md @@ -0,0 +1,187 @@ +--- +title: Converting Existing Specs with Multiple Previews +llmstxt: true +--- + +Converting a spec with multiple preview versions into a spec with a single, latest preview version is complex because the changes that occur in any +preview version are carried over into the next version by default. This means that each versioning decorator referencing a preview version must be dealt with _in order_ to end with the correct decoration. + +For each type decorated with a versioning decorator that references a preview version, follow these steps. If the type is decorated with _more than one instance_ of a versioning decorator referencing different preview versions, each of these must be processed _in version order (earliest version first)_ + +A conversion consists of the following steps, outlined in the sections below + +- Normalizing version decoration (optional) +- Determining which versions should be removed: this should include all preview api-versions **except** the last version (if the last version is a preview, that version will **not** be removed.) +- Handling each versioning decorator application that references a deleted preview version (except the **first** version) +- Removing the first version (if it is a preview) +- Removing the deleted preview versions from the Versions enum +- If there is a remaining preview version, it should be the last version in the versions enum. Ensure it is decorated with the `@previewVersion` decorator. +- Cleaning up (optional) + +In this document we use the notation `@(T, u, [arg])` this indicates an instance of the decorator `` decorating type `T` using version `u` in a set of monotonically increasing versions `1..n` + +## Normalizing Version Decoration (optional) + +Normalizing version decoration consists of removing redundant decorators and follows a few rules, described below. For these rules, consider an ordered set of versions `1...n`. + +- Trim any sequence of `@removed` decorators without an `@added` between them: A decorator `@removed(T, c)` with type `T` and `c` in `1..n` may be safely removed if there is another decorator `@removed(T, a)` with `a` in `1..n` and `a < c` unless there is a decorator `@added(T, b)` with `b` in `1..n` and `a < b < c`. +- Trim any sequence of `@added` decorators without any `@removed` between them: A decorator `@added(T, c)` with type `T` and `c` in `1..n` may be safely removed if there is another decorator `@added(T,a)` with `a` in `1..n` and `a < c` unless there is a decorator `@removed(T,b)` with `b` in `1..n` and `a < b < c`. +- Any duplicated application of a versioning decorator to a type with the same parameters may be safely removed, that is: + - `@added(T, a)` and `@added(T, a)` can be simplified to `@added(T, a)` + - `@removed(T, a)` and `@removed(T, a)` can be simplified to `@removed(T, a)` + - `@madeOptional(T, a)` and `@madeOptional(T, a)` can be simplified to `@madeOptional(T, a)` + - `@renamedFrom(T, a, name)` and `@renamedFrom(T, a, name)` can be simplified to `@renamedFrom(T, a, name)` + - `@typeChangedFrom(T, a, U)` and `@typeChangedFrom(T, a, U)` can be simplified to `@typeChangedFrom(T, a, U)` + - `@returnTypeChangedFrom(T, a, U)` and `@returnTypeChangedFrom(T, a, U)` can be simplified to `@returnTypeChangedFrom(T, a, U)` +- If `@added(T, a)` and `@removed(T,a)` occur, `@added(T, a)` may be removed. +- If any versioning decorator with 3 arguments `@dec(T, v, a)` where `T` is a type, `v` is a version and `a` is a valid arg value occurs with another application of the same decorator `@dec(T, v, b)` and `b != a`, then the innermost of the two decorators can be removed. +- If any of the following decorators reference the first version, they may be removed: + - `@added` + - `@renamedFrom` + - `@madeOptional` + - `@typeChangedFrom` + - `@returnTypeChangedFrom` +- if `@removed(T, 1)` occurs, where `T` is the decorated type and `1` is the first version: + - If `T` occurs in any subsequent version (i.e. if there is any), this decorator should remain + - Otherwise, the decorated type can simply be removed. + +## Handling Each Versioning Decorator That References a Preview Version + +## `@renamedFrom(T, u, name)` decorator + +- Based on the version referenced in the decorator, determine the immediate successor version `u + 1` +- If version `u + 1` does not exist (the version argument is the last version) then this version will not be deleted +- If there is one or more `@renamedFrom(T, u + 1, anotherName)` decorators, remove them. +- Change `@renamedFrom(T, u, name)` to `@renamedFrom(T, u + 1, name)` + + ```diff lang=tsp + - @renamedFrom(Versions.`2025-10-01-preview`, "oldName") + + @renamedFrom(Versions.`2025-11-01`, "oldName") + newName: string; + ``` + +## `@typeChangedFrom(T, u, type)` decorator + +- Based on the version referenced in the decorator, determine the immediate successor version `u + 1` +- If version `u + 1` does not exist (the version argument is the last version) then this version will not be deleted +- If there is one or more `@typeChangedFrom` decorators referencing the immediate successor version, remove them. +- Change `@typeChangedFrom(T, u, type)` to `@typeChangedFrom(T, u + 1, type)` + + ```diff lang=tsp + - @typeChangedFrom(Versions.`2025-10-01-preview`, int32) + + @typeChangedFrom(Versions.`2025-11-01`, int32) + newType: string; + ``` + +## `@returnTypeChangedFrom(T, u, returnType)` decorator + +- Based on the version referenced in the decorator, determine the immediate successor version `u + 1` +- If version `u + 1` does not exist (the version argument is the last version) then this version will not be deleted +- If there is one or more `@returnTypeChangedFrom` decorators referencing the immediate successor version, remove them. +- Change `@returnTypeChangedFrom(T, u, returnType)` to `@returnTypeChangedFrom(T, u + 1, returnType)` + + ```diff lang=tsp + - @returnTypeChangedFrom(Versions.`2025-10-01-preview`, void) + + @returnTypeChangedFrom(Versions.`2025-11-01`, void) + move is ArmResourceActionSync; + ``` + +## `@madeOptional(T, u)` decorator + +- Based on the version referenced in the decorator, determine the immediate successor version `u + 1` +- If version `u + 1` does not exist (the version argument is the last version) then this version will not be deleted +- If there is one or more `@madeOptional` decorators referencing the immediate successor version, remove them. +- Change `@madeOption(T, u)` to `@madeOptional(T, u + 1)` + + ```diff lang=tsp + - @madeOptional(Versions.`2025-10-01-preview`) + + @madeOptional(Versions.`2025-11-01`) + nowOptional?: string; + ``` + +## `@added(T, u)` decorator + +- Based on the version referenced in the decorator, determine the immediate successor version `u + 1` +- If version `u + 1` does not exist (the version argument is the last version) then this version will not be deleted +- If there is one or more `@added(T, u + 1)` decorators referencing the immediate successor version + - Remove them. + - Change the version argument in the decorator to match the successor version [ `@added(T, u) -> @added(T, u + 1)`] + + ```diff lang=tsp + - @added(Versions.`2025-10-01-preview`) + - @added(Versions.`2025-11-01`) + @added(Versions.`2025-11-01`) + newType: string; + ``` + +- If there is an `@removed(T, u + 1)` decorator referencing the immediate successor version + - If the type does not occur in any previous version `v < u`, delete the type altogether + + ```diff lang=tsp + - @added(Versions.`2025-10-01-preview`) + - @removed(Versions.`2025-11-01`) + - goneType: string; + ``` + + - If the type does occur in a previous version, remove the `@added(T, u)` decorator. + + ```diff lang=tsp + @added(Versions.`2025-06-01`) + - @added(Versions.`2025-10-01-preview`) + @removed(Versions.`2025-11-01`) + remainType: string; + ``` + +- If there are no `@added(T, u + 1)` or `@removed(T, u + 1)` decorators referencing the immediate successor version, change the version in the decorator to the immediate successor version: [`@added(T, u)` -> `@added(T, u + 1)`] + + ```diff lang=tsp + - @added(Versions.`2025-10-01-preview`) + + @added(Versions.`2025-11-01`) + @removed(Versions.`2026-02-01`) + remainType: string; + ``` + +## `@removed(T, u)` decorator + +- Based on the version referenced in the decorator, determine the immediate successor version `u + 1` +- If version `u + 1` does not exist (the version argument is the last version) then this version will not be deleted +- If there is one or more `@removed(T, u + 1)` decorators referencing the immediate successor version + - Remove them. + - Change the version argument in the decorator to match the successor version [`@removed(T, u) -> @removed(T, u + 1)`] + + ```diff lang=tsp + - @removed(Versions.`2025-10-01-preview`) + - @removed(Versions.`2025-11-01`) + @removed(Versions.`2025-11-01`) + remainType: string; + ``` + +- if there is one or more `@added(T, u + 1)` decorators in the immediate successor version + - Remove the `@removed(T, u)` decorator + + ```diff lang=tsp + - @removed(Versions.`2025-10-01-preview`) + @added(Versions.`2025-11-01`) + remainType: string; + ``` + +- If there are no `@removed(T, u + 1)` or `@removed(T, u + 1)` decorators referencing the immediate successor version, change the version in the decorator to the immediate successor version: [`@removed(T, u)` -> `@removed(T, u + 1)`] + + ```diff lang=tsp + - @removed(Versions.`2025-10-01-preview`) + + @removed(Versions.`2025-11-01`) + remainType: string; + ``` + +## Removing the _First_ version in the Specification + +TypeSpec specs require no versioning decoration for the first version of a spec, and all versioning decorators except `@removed` have no impact on the type graph for subsequent versions and can be safely removed. Any `@removed` decorator referencing the first version should be treated as follows: + +- If the decorated type _does not occur_ in any subsequent versions, it can be removed +- If the decorated type _does occur_ in subsequent versions, the version referenced in the `@removed` decorator should be updated to its immediate successor + +Note that the last version of a specification should _never_ be removed in a conversion, as if the last version is a preview, it will become the active preview. + +## Cleaning Up (optional) + +After removing the preview versions, repeat the [steps to normalize version decoration](#normalizing-version-decoration-optional) to remove redundant decoration. diff --git a/website/src/content/docs/docs/howtos/Versioning/ARM/07-perpetual-preview.md b/website/src/content/docs/docs/howtos/Versioning/ARM/07-perpetual-preview.md new file mode 100644 index 0000000000..76cfd3cec0 --- /dev/null +++ b/website/src/content/docs/docs/howtos/Versioning/ARM/07-perpetual-preview.md @@ -0,0 +1,182 @@ +--- +title: How to Manage a Single Active Preview When Some Features Always Remain In Preview +llmstxt: true +--- + +For some Resource Providers, whenever a new stable version is released, a new preview version is created, because some preview features are not ready to be stable, but may become stable in a future version. To accommodate this need and account for the limitations of breaking change checks, which require a single version change for any PR into the rest-api-specs repo, the recommended solution is to introduce a stable and subsequent preview _together_ in your TypeSpec api description and then split this change into two PRs: one representing the new stable and the second representing the subsequent preview. This involves the following steps described in the sections below: + +- Create the new preview version based on the latest preview version +- Create the stable version (which should immediately precede the new preview version) +- Fill in examples for each +- Create a copy of the spec with just the stable release changes for your first PR +- After this PR is merged, create a PR with the whole spec, effectively adding the new preview + +## Creating New Preview and Stable Versions + +- If the existing preview version is `A`, add the new stable version `A + 1` and the new preview version `A + 2` to the Versions enumeration, ensure that version `A + 2` is decorated with `@previewVersion` from the `Azure.Core` library (and remove that decoration from any other version). +- For all changes from preview version `A` that are part of stable version `A + 1` + - if a new type was added in `A` and is now stable (`@added(T, A)`), add the new decorator `@added(T, A + 1)` + + ```diff lang=tsp + @added(Versions.`2025-10-01-preview`) + + @added(Versions.`2025-11-01`) + remainType: string; + ``` + + - if a type was made optional in `A` and this change is now stable (`@madeOptional(T, A)`), add the new decorator `@madeOptional(T, A + 1)` + + ```diff lang=tsp + @madeOptional(Versions.`2025-10-01-preview`) + + @madeOptional(Versions.`2025-11-01`) + optionalType?: string; + ``` + + - if a type was renamed in `A` and this change is now stable (`@renamedFrom(T, A, Name)`), add the new decorator `@renamedFrom(T, A + 1, Name)` + + ```diff lang=tsp + @renamedFrom(Versions.`2025-10-01-preview`, "oldName") + + @renamedFrom(Versions.`2025-11-01`, "oldName") + newName: string; + ``` + + - if a property or parameter had its type changed in `A` and is now stable (`@typeChangedFrom(T, A, U)`), add the new decorator `@typeChangedFrom(T, A + 1, U)` + + ```diff lang=tsp + @typeChangedFrom(Versions.`2025-10-01-preview`, int32) + + @typeChangedFrom(Versions.`2025-11-01`, int32) + changedType: string; + ``` + + - if an operation returnType was changed in `A` and this change is now stable (`@returnTypeChangedFrom(T, A, U)`), add the new decorator `@returnTypeChangedFrom(T, A + 1, U)` + + ```diff lang=tsp + @returnTypeChangedFrom(Versions.`2025-10-01-preview`, void) + + @returnTypeChangedFrom(Versions.`2025-11-01`, void) + move is ArmResourceActionSync + ``` + + - If a type was removed in `A` and this change is now stable (`@removed(T, A)`), add the new decorator `@removed(T, A + 1)` + + ```diff lang=tsp + @removed(Versions.`2025-10-01-preview`) + + @removed(Versions.`2025-11-01`) + goneType: string; + ``` + +- Change all versioning decorators `dec(T, A, args)` to `dec(T, A + 2, args)` where `T` is a type, `A` is the latest preview version, `A + 2` is the new preview version you added in the first step and `args` are any additional arguments to the decorator. Note that, after this change, some decorators will be duplicated in version `A + 1` and version `A + 2`. This is expected. + + ```diff lang=tsp + - @removed(Versions.`2025-10-01-preview`) + @removed(Versions.`2025-11-01`) + + @removed(Versions.`2025-12-01-preview`) + goneType: string; + ``` + +- Add any new type changes to stable version (A + 1) and decorate appropriately, as shown in the [versioning guide](../versioning.md). Note that these changes should also appear in the new preview (A + 2) +- Remove version `A` from the versions enumeration + + ```diff lang=tsp + enum Versions { + - @previewVersion + - `2025-10-01-preview`, + + `2025-11-01`, + + @previewVersion + + `2025-12-01-preview`, + } + ``` + +- Create examples directories for the new stable version (A + 1) and populate them with appropriate examples +- If version A _is not needed_ in the specs repo + - Remove its example folder + + ```bash + > rm -r examples/2025-10-01-preview + ``` + + - Remove all references to version A in `README.md` + +## Create A Copy of the Spec for the Stable Version only + +- Create a complete copy of your spec + - This copy will be used to contain just the new stable version (use this to create the first PR into the specs repo). Call this Copy. + - The original version will contain both the new stable _and_ preview versions (use this to create the final PR after the first PR is merged). Call this Original. +- Do the following with the Copy + - Remove all decorators that reference version `A + 2` and use the same parameters as in `A + 1` + - For example, if these decorators exist: `@added(T, A + 2) @added(T, A + 1)`, then, after this step, only `@added(T, A + 1)` should remain in this copy of the spec. + + ```diff lang=tsp + @added(Versions.`2025-11-01`) + - @added(Versions.`2025-12-01-preview`) + addedType: string; + ``` + + - Undo and remove any remaining decorators referencing `A + 2` using the following guide: + - For any type `T` decorated with `@added(T, A + 2)`, delete the type `T` and all its decorators + + ```diff lang=tsp + - @added(Versions.`2025-12-01-preview`) + - goneType: string; + ``` + + - For any type `T` decorated with `@removed(T, A + 2)`, remove the decorator + + ```diff lang=tsp + - @removed(Versions.`2025-12-01-preview`) + remainType: string; + ``` + + - For any type `T` decorated with `@renamedFrom(T, A + 2, oldName)` rename the type `oldName` and remove the decorator + + ```diff lang=tsp + - @renamedFrom(Versions.`2025-12-01-preview`, "oldName") + - newName: string; + + oldName: string; + ``` + + - For any property or parameter `T` decorated with `@typeChangedFrom(T, A + 2, oldType)` set the type of the property to `oldType` and remove the decorator + + ```diff lang=tsp + - @typeChangedFrom(Versions.`2025-12-01-preview`, int32) + - changedType: string; + + changedType: int32; + ``` + + - For any operation `T` decorated with `@returnTypeChangedFrom(T, A + 2, oldType)` set the return type of the operation to `oldType` and remove the decorator + + ```diff lang=tsp + - @returnTypeChangedFrom(Versions.`2025-12-01-preview`, void) + - move is ArmResourceActionSync; + + move is ArmResourceActionSync; + ``` + + - For any property or parameter `T` decorated with `@madeOptional(T, A + 2)` make the parameter or property `T` required and remove the decorator + + ```diff lang=tsp + - @madeOptional(Versions.`2025-12-01-preview`) + - remainType?: string; + + remainType: string; + ``` + + - Remove version `A + 2` from the version enum. + + ```diff lang=tsp + enum Versions { + `2025-11-01`, + - @previewVersion + - `2025-12-01-preview`, + } + ``` + + - Compile the spec to produce artifacts (especially the new stable version (`A + 1`) openapi ) + - Add the new stable version (`A + 1`) to the README.md file. + - Create and merge the PR + +## Create a PR with the Combined Spec + +- Do the following with the Original + - Follow the instructions for normalizing decoration in the [converting specifications](./06-converting-specs.md#normalizing-version-decoration-optional) document. This will remove any redundant decoration between the new stable and preview versions (`A + 1` and `A + 2`). + - Add any type changes that are introduced in the new preview and decorate appropriately, following the [versioning guide](../../ARM/versioning.md) + - Add a new example folder for the new preview version and populate with appropriate examples. + - Compile the spec to produce artifacts (especially the new stable and preview version (`A + 1` and `A + 2` ) apis). + - Copy the README.md from copy 1 and add the new preview version to the file. + - Create and merge the final PR - this copy will be your specification going forward. diff --git a/website/src/content/docs/docs/howtos/Versioning/preview-version.md b/website/src/content/docs/docs/howtos/Versioning/preview-version.md index 25af41a40d..33a9d67e00 100644 --- a/website/src/content/docs/docs/howtos/Versioning/preview-version.md +++ b/website/src/content/docs/docs/howtos/Versioning/preview-version.md @@ -5,16 +5,26 @@ llmstxt: true See [`@typespec/versioning` documentation](https://typespec.io/docs/libraries/versioning/guide) for the general versioning concept. This guide expands on how Azure Services should define Preview versions. -## Preview Versioning Rules +See [Common ARM Versioning Scenarios](../ARM/versioning.md) for how to make specific kinds of common API changes in ARM specs. + +## Preview Versioning Rules for All Azure APIs + +- Always make the last enum value the preview and apply `@previewVersion` to it. +- Only one version may be marked with the `@previewVersion` decorator. +- Mark all changes from the latest stable with appropriate versioning decorators, using `Versions.` as the version argument (where `` is the name of the last enum value) + +## Preview Versioning Rules for ARM APIs + +ARM APIs sometimes have special requirements for retaining swagger for preview APIs that are not yet retired. For detailed information about ARM API Versioning see [Supporting a Single Active Preview in ARM APIs](./ARM/01-about-versioning.md). + +## Preview Versioning Rules for Data Plane APIs -- Always make the last enum the preview and apply `@previewVersion` to it. -- Apply `@added(Versions.PreviewVersion)` to all preview items - these items will not show up in any GA Version - For a new GA, add a new version enum **BEFORE** the preview enum value. Manually change all **preview** items that are GA'ing so that the `@added` version value matches the new GA enum value - For any items remaining in **preview**, rename the **old preview** enum value to the **new preview** enum value. -## Usage Examples +### Usage Examples -### New preview Version +#### New preview Version In the following example we introduce a new preview version called `v3Preview` which includes everything from `v2` plus adds a new property to the `Widget` resource. @@ -65,7 +75,7 @@ model Widget { } ``` -### Adding a new stable (GA) version +#### Adding a new stable (GA) version This example builds on the previous one, where `v3` is introduced which GA's the `nickname` property introduced in `v3Preview`