Version extension gRPC contracts - #9747
Conversation
Establish stable v1 and preview v1beta protobuf packages, separate generated contracts from the handwritten azdext SDK, and register both contract channels in the host. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 1 pipeline(s). 20 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Exercise every beta service adapter through real gRPC dispatch and consolidate generated unary adaptation and override validation so contract growth does not cause repeated coverage regressions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Introduces versioned stable and beta extension gRPC contracts while retaining the stable Go SDK facade.
Changes:
- Adds
v1/v1betaprotobuf packages and generated clients. - Registers both contract versions through generated adapters.
- Adds generation, compatibility, ownership, and lifecycle guidance.
Reviewed changes
Copilot reviewed 87 out of 135 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
.github/CODEOWNERS |
Protects stable contracts. |
cli/azd/.vscode/cspell.yaml |
Adds generator terms. |
cli/azd/Makefile |
Generates versioned contracts and adapters. |
cli/azd/docs/extensions/contract-versioning.md |
Documents contract lifecycle. |
cli/azd/docs/extensions/extension-framework.md |
Updates contract references. |
cli/azd/grpc/README.md |
Documents generation and compatibility checks. |
cli/azd/grpc/buf.yaml |
Configures Buf validation. |
cli/azd/grpc/generateadapters/main_test.go |
Tests adapter generation rules. |
cli/azd/grpc/generatefacade/main.go |
Generates the stable SDK facade. |
cli/azd/grpc/include/google/protobuf/struct.proto |
Vendors the shared well-known type. |
cli/azd/grpc/proto/azd/extensions/v1/account.proto |
Versions the stable account contract. |
cli/azd/grpc/proto/azd/extensions/v1/ai_model.proto |
Versions the stable AI model contract. |
cli/azd/grpc/proto/azd/extensions/v1/compose.proto |
Versions the stable compose contract. |
cli/azd/grpc/proto/azd/extensions/v1/container.proto |
Versions the stable container contract. |
cli/azd/grpc/proto/azd/extensions/v1/deployment.proto |
Versions the stable deployment contract. |
cli/azd/grpc/proto/azd/extensions/v1/environment.proto |
Versions the stable environment contract. |
cli/azd/grpc/proto/azd/extensions/v1/errors.proto |
Versions stable error details. |
cli/azd/grpc/proto/azd/extensions/v1/event.proto |
Versions the stable event contract. |
cli/azd/grpc/proto/azd/extensions/v1/extension.proto |
Versions the stable lifecycle contract. |
cli/azd/grpc/proto/azd/extensions/v1/framework_service.proto |
Versions the stable framework contract. |
cli/azd/grpc/proto/azd/extensions/v1/models.proto |
Versions shared stable models. |
cli/azd/grpc/proto/azd/extensions/v1/project.proto |
Versions the stable project contract. |
cli/azd/grpc/proto/azd/extensions/v1/prompt.proto |
Versions the stable prompt contract. |
cli/azd/grpc/proto/azd/extensions/v1/telemetry.proto |
Versions the stable telemetry contract. |
cli/azd/grpc/proto/azd/extensions/v1/user_config.proto |
Versions the stable configuration contract. |
cli/azd/grpc/proto/azd/extensions/v1/validation.proto |
Versions the stable validation contract. |
cli/azd/grpc/proto/azd/extensions/v1/workflow.proto |
Versions the stable workflow contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/account.proto |
Adds the beta account contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/compose.proto |
Adds the beta compose contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/container.proto |
Adds the beta container contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/copilot.proto |
Adds the beta Copilot contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/deployment.proto |
Adds the beta deployment contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/environment.proto |
Adds the beta environment contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/errors.proto |
Adds beta error details. |
cli/azd/grpc/proto/azd/extensions/v1beta/event.proto |
Adds the beta event contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/extension.proto |
Adds the beta lifecycle contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/framework_service.proto |
Adds the beta framework contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/models.proto |
Adds shared beta models. |
cli/azd/grpc/proto/azd/extensions/v1beta/project.proto |
Adds the beta project contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/provisioning.proto |
Adds the beta provisioning contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/service_target.proto |
Adds the beta target contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/telemetry.proto |
Adds the beta telemetry contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/user_config.proto |
Adds the beta configuration contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/validation.proto |
Adds the beta validation contract. |
cli/azd/grpc/proto/azd/extensions/v1beta/workflow.proto |
Adds the beta workflow contract. |
cli/azd/internal/grpcserver/server.go |
Registers and translates versioned services. |
cli/azd/pkg/azdext/output_test.go |
Updates stable-message coverage. |
cli/azd/pkg/azdext/contracts/v1/account_grpc.pb.go |
Generates stable account RPCs. |
cli/azd/pkg/azdext/contracts/v1/ai_model_grpc.pb.go |
Generates stable AI model RPCs. |
cli/azd/pkg/azdext/contracts/v1/compose_grpc.pb.go |
Generates stable compose RPCs. |
cli/azd/pkg/azdext/contracts/v1/container_grpc.pb.go |
Generates stable container RPCs. |
cli/azd/pkg/azdext/contracts/v1/copilot_grpc.pb.go |
Generates stable Copilot RPCs. |
cli/azd/pkg/azdext/contracts/v1/deployment_grpc.pb.go |
Generates stable deployment RPCs. |
cli/azd/pkg/azdext/contracts/v1/environment_grpc.pb.go |
Generates stable environment RPCs. |
cli/azd/pkg/azdext/contracts/v1/event_grpc.pb.go |
Generates stable event RPCs. |
cli/azd/pkg/azdext/contracts/v1/extension_grpc.pb.go |
Generates stable lifecycle RPCs. |
cli/azd/pkg/azdext/contracts/v1/framework_service_grpc.pb.go |
Generates stable framework RPCs. |
cli/azd/pkg/azdext/contracts/v1/project_grpc.pb.go |
Generates stable project RPCs. |
cli/azd/pkg/azdext/contracts/v1/prompt_grpc.pb.go |
Generates stable prompt RPCs. |
cli/azd/pkg/azdext/contracts/v1/provisioning_grpc.pb.go |
Generates stable provisioning RPCs. |
cli/azd/pkg/azdext/contracts/v1/service_target_grpc.pb.go |
Generates stable target RPCs. |
cli/azd/pkg/azdext/contracts/v1/telemetry_grpc.pb.go |
Generates stable telemetry RPCs. |
cli/azd/pkg/azdext/contracts/v1/user_config_grpc.pb.go |
Generates stable configuration RPCs. |
cli/azd/pkg/azdext/contracts/v1/validation_grpc.pb.go |
Generates stable validation RPCs. |
cli/azd/pkg/azdext/contracts/v1/workflow_grpc.pb.go |
Generates stable workflow RPCs. |
cli/azd/pkg/azdext/contracts/v1beta/account_grpc.pb.go |
Generates beta account RPCs. |
cli/azd/pkg/azdext/contracts/v1beta/deployment_grpc.pb.go |
Generates beta deployment RPCs. |
cli/azd/pkg/azdext/contracts/v1beta/event_grpc.pb.go |
Generates beta event RPCs. |
cli/azd/pkg/azdext/contracts/v1beta/extension_grpc.pb.go |
Generates beta lifecycle RPCs. |
cli/azd/pkg/azdext/contracts/v1beta/framework_service_grpc.pb.go |
Generates beta framework RPCs. |
cli/azd/pkg/azdext/contracts/v1beta/provisioning_grpc.pb.go |
Generates beta provisioning RPCs. |
cli/azd/pkg/azdext/contracts/v1beta/service_target_grpc.pb.go |
Generates beta target RPCs. |
cli/azd/pkg/azdext/contracts/v1beta/telemetry_grpc.pb.go |
Generates beta telemetry RPCs. |
cli/azd/pkg/azdext/contracts/v1beta/validation_grpc.pb.go |
Generates beta validation RPCs. |
cli/azd/pkg/azdext/contracts/v1beta/workflow_grpc.pb.go |
Generates beta workflow RPCs. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Correct contract documentation and migrate non-Go extension scaffolds to the stable v1 gRPC package so newly generated clients remain compatible with the versioned host. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7d1a4605-370b-43ca-9845-979dd2b4ada3
Set the generated extension and developer-kit manifests to require azd 1.33.0, the first planned release hosting the stable v1 extension endpoints. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7d1a4605-370b-43ca-9845-979dd2b4ada3
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 93 out of 182 changed files in this pull request and generated no new comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
cli/azd/Makefile:49
- This only exposes a manual target; no workflow or
mage preflightpath invokesproto-breaking(orproto-lint). As a result, future PRs can change both v1 and v1beta incompatibly and still pass the cross-channel tests, so the historical compatibility protection requested by #9663 is not actually enforced. Wire these targets into CI against the PR base revision once this baseline lands.
|
Azure Pipelines: Successfully started running 2 pipeline(s). 19 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
|
We can also slightly decouple the extension changes from the core release by having extensions temporarily depend on a merged commit (with this PR's changes) through a Go pseudo-version instead of This lets us prepare and publish migrated extension versions with |
Keep the frozen pre-versioning RPC surface available while extensions migrate to v1, and measure remaining bridge usage for removal. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7d1a4605-370b-43ca-9845-979dd2b4ada3
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 97 out of 191 changed files in this pull request and generated 3 comments.
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
cli/azd/Makefile:49
proto-breakingis only a manual target: nothing in repository workflows ormage preflightinvokes it, and it has no default baseline. A future v1 field renumbering mirrored into v1beta would pass the Go subset test, so the promised historical compatibility gate is not enforced. Run Buf lint and breaking checks in CI for contract changes, using the target branch as the baseline.
cli/azd/grpc/README.md:19
- This contradicts the compatibility bridge registered by
registerLegacyServicesand the rollout described in the PR. Existing binaries can call the frozen/azdext.*surface; only new development should use the versioned packages. Document the temporary bridge and its frozen scope instead of saying the endpoints are absent.
| return err | ||
| } | ||
| descriptions = append(descriptions, description) | ||
| registrar.RegisterService(&descriptions[len(descriptions)-1], implementation) |
| The move from the original `azdext` wire package to | ||
| `azd.extensions.v1` is an intentionally accepted one-time breaking change. | ||
| azd does not register legacy `/azdext.*` runtime endpoints. Compatibility | ||
| checks should use the first merged versioned-contract commit as their baseline, | ||
| not the old unversioned sources. |
| ExtensionLegacyGrpcCallCount = AttributeKey{ | ||
| Key: attribute.Key("extension.grpc.legacy_call_count"), | ||
| Classification: SystemMetadata, | ||
| Purpose: FeatureInsight, | ||
| IsMeasurement: true, |
Move ComposeService and CopilotService out of the stable v1 contracts, register their native beta implementations, and preserve their frozen legacy aliases during migration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7d1a4605-370b-43ca-9845-979dd2b4ada3
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 106 out of 198 changed files in this pull request and generated 5 comments.
Suppressed comments (1)
cli/azd/internal/tracing/fields/fields.go:1227
- This new telemetry field requires a privacy review before merge, but the PR checklist still leaves “Privacy review triggered” unchecked. Complete and record that review before landing the field, as required by
cli/azd/AGENTS.md:329-334.
| "github.com/azure/azure-dev/cli/azd/extensions/microsoft.azd.ai.builder/internal/pkg/qna" | ||
| "github.com/azure/azure-dev/cli/azd/extensions/microsoft.azd.ai.builder/internal/pkg/util" | ||
| "github.com/azure/azure-dev/cli/azd/pkg/azdext" | ||
| v1beta "github.com/azure/azure-dev/cli/azd/pkg/azdext/contracts/v1beta" |
| "strings" | ||
|
|
||
| "github.com/azure/azure-dev/cli/azd/pkg/azdext" | ||
| v1beta "github.com/azure/azure-dev/cli/azd/pkg/azdext/contracts/v1beta" |
| syntax = "proto3"; | ||
|
|
||
| package azdext; | ||
| package azd.extensions.v1; |
| proto-breaking: | ||
| @test -n "$(BUF_BREAKING_AGAINST)" || \ | ||
| (echo "set BUF_BREAKING_AGAINST to a Buf module, image, or Git source" && exit 1) | ||
| $(BUF) breaking grpc --against "$(BUF_BREAKING_AGAINST)" |
| `Compose()` and `Copilot()` are preview accessors. Import | ||
| `github.com/azure/azure-dev/cli/azd/pkg/azdext/contracts/v1beta` for their | ||
| request, response, and enum types. They are intentionally excluded from the | ||
| stable `azdext` contract façade until those services graduate to `v1`. |
Summary
azd.extensions.v1and previewazd.extensions.v1betaprotobuf packagespkg/azdext/contracts/v1andpkg/azdext/contracts/v1beta, while preserving the stable handwrittenpkg/azdextSDK facade/azdext.*RPC surface through a temporary isolated compatibility bridge, measured by telemetry for removalAddresses #9663.
Why this design
This follows the compatibility model described in Microsoft's Versioning gRPC services guidance:
azd.extensions.v1.ProjectServiceandazd.extensions.v1beta.ProjectServicecan be registered side-by-side.UNIMPLEMENTED; therefore the move fromazdext.*toazd.extensions.v1.*needs an explicit migration bridge rather than being treated as a source-only file move.The
v1betaspelling comes from the protobuf API channel convention documented by AIP-185, rather than from Microsoft's article specifically. We use it instead ofv1previewornextbecause:v1identifies the stable compatibility-major surface; protobuf package versions do not expose minor or patch versions such asv1.1.v1betaidentifies a long-lived beta channel associated with major version 1 and is a recognizable convention for generated API packages.v1, which this PR enforces with descriptor-based compatibility tests.v1; thev1betapackage is not renamed and av1.1package is not created.previewremains the product/support label, whilebetais the protobuf package-channel identifier.nextwould not communicate a stability level or compatibility-major lineage.The contract rules also align with the Protocol Buffers updating guidance: field numbers identify data on the wire, must not be changed or reused, and removed numbers/names must be reserved. Buf plus the cross-package stable-subset tests enforce these rules for future changes.
Compatibility bridge and rollout
The package migration changes gRPC service addresses, but this PR no longer requires all extensions to migrate atomically. azd registers three service identities:
azd.extensions.v1.*— the supported stable contract for current extension development.azd.extensions.v1beta.*— the preview contract for narrow opt-in to preview functionality.azdext.*— a temporary, frozen invocation-compatibility bridge for binaries built before contract versioning.The bridge is intentionally isolated in
internal/grpcserver/legacybridge. It does not restore the old generated SDK or create another contract package for developers to import. Instead, it registers aliases for the exact 18-service, 78-RPC legacy surface that existed when versioning was introduced and delegates those calls to the corresponding versioned implementations: stablev1for GA services and nativev1betafor Compose and Copilot. The allowlist is explicit: additions tov1do not become legacy endpoints unless someone deliberately edits the frozen list. Descriptor metadata is omitted because the bridge supports invocation compatibility, not a complete legacy reflection schema.Structured
ActionableErrorDetailvalues also carry protobuf package names in theirAnytype URLs. For legacy calls, the bridge rewrites stableazd.extensions.v1.*detail URLs back toazdext.*; the message bytes remain valid because the initial stable schema preserves the legacy wire format.This is a migration layer, not a third supported development channel. Each authenticated unary call or opened stream through
/azdext.*increments the integer measurementextension.grpc.legacy_call_counton the enclosing command orext.runspan. Remove the bridge, its interceptor hooks, and the measurement after the count remains zero for a full stable release cycle and all supported extensions have migrated toazd.extensions.v1. A privacy review and downstream query/dashboard coordination are required for this new field.Extensions pinned to an existing released SDK remain buildable, and already-published binaries continue to communicate with an azd host built from this change through the bridge. The non-Go scaffolds owned by
microsoft.azd.extensionsare updated here so newly generated JavaScript, Python, and .NET extensions target stablev1. The first planned core release containing the versioned endpoints is azd1.33.0. Newly generated extension manifests and the developer-kit extension therefore setrequiredAzdVersion: ">=1.33.0"; existing first-party extensions should use the same floor when they migrate to the new stable SDK structure.The initial stable snapshot reflects the GA contracts currently on
main, including telemetry, image passthrough, and empty multi-select support.ComposeServiceandCopilotServiceare excluded fromv1because those capabilities are not GA; they exist only inv1beta. Their frozen/azdext.*aliases remain available solely to avoid breaking existing preview extension binaries.Preview-only services
ComposeServiceandCopilotServiceuse native generatedv1betaserver implementations rather than pretending to have stable implementations and adapting back to them. They have no generated files or aliases undercontracts/v1or the stableazdextcontract façade. TheAzdClient.Compose()andAzdClient.Copilot()convenience methods return explicitv1betaclient interfaces, and callers importpkg/azdext/contracts/v1betafor request, response, and enum types. When either service reaches GA, it can be added compatibly tov1and the normal stable-to-beta adapter path will take over.Extension compatibility scenarios
requiredAzdVersionis enforced while resolving an extension install or update. It protects the new extension → old azd direction, but it is not a negotiated gRPC protocol version. The temporary bridge protects the old extension → new azd direction during migration./azdext.*.v1orv1betaextension whoserequiredAzdVersionrequires the new azdv1extension/azd.extensions.v1.*.v1betaextensionrequiredAzdVersionincludes the first azd release supporting beta.v1./azdext.*bridge for the legacy RPC surface. Usage incrementsextension.grpc.legacy_call_count.v1extension version; the telemetry count shows when the bridge can be removed.v1orv1beta./azd.extensions.v1.*and/azd.extensions.v1beta.*. The install-time check is not re-run merely because azd was downgraded.UNIMPLEMENTED./azdext.*.UNIMPLEMENTEDunless a separate tailored compatibility diagnostic is added before removal.Migrating an extension
Existing extension to stable
v1This is the default migration for an existing GA extension:
github.com/Azure/azure-dev/cli/azddependency to a release containing this change.pkg/azdextSDK normally keep the same Go import and API names because generated stable types are re-exported by the stable SDK facade; rebuilding changes their RPC addresses to/azd.extensions.v1.*. Compose and Copilot are preview-only and instead requirecontracts/v1betarequest and response types.pkg/azdext/contracts/v1.requiredAzdVersionto the first azd release that hostsazd.extensions.v1.Example outcome: an extension using
azdext.NewAzdClient().Project()remains source-compatible through the facade. Its old binary continues working temporarily through the frozen bridge, while updating the SDK dependency and rebuilding moves it to the supported/azd.extensions.v1.*address. An extension using Compose or Copilot keeps theAzdClientconvenience accessor but changes those protobuf message imports tocontracts/v1beta.Stable
v1extension opting into a beta featureAn extension does not need to migrate every service to beta. The recommended flow is:
pkg/azdextfacade orpkg/azdext/contracts/v1package.pkg/azdext/contracts/v1betaonly for the service containing the required preview RPC, field, or enum value.v1betarequest/response types for that RPC.requiredAzdVersionto the first azd version implementing the beta feature.Conceptually, a mixed extension can use clients like this against the same host connection:
The existing
azdext.AzdClientconvenience methods are stable-v1only. A beta caller currently uses the generatedcontracts/v1betaclient with a gRPC connection; a future SDK helper could make that more ergonomic without changing the wire model.Moving a beta feature back to stable
v1When a preview feature graduates:
v1contract; do not renamev1betaand do not createv1.1.contracts/v1betatopkg/azdextorcontracts/v1.requiredAzdVersionif the stable feature requires a newer azd.Switching package imports requires a rebuild because
v1.SomeRequestandv1beta.SomeRequestare distinct generated Go types even when their fields are currently identical.Can one extension mix
v1andv1beta?Yes. Version selection is per gRPC service client/RPC, not a single mode negotiated for the whole extension. The host registers both package identities, so one extension can use stable Project and Environment services while using a beta AI Model service.
The boundaries are:
v1betarequest cannot be passed to av1generated client.v1andv1betaare distinct Go types. Data crossing that boundary must be mapped or transcoded explicitly.v1betafor every service is technically possible, but it unnecessarily couples the extension to the preview channel. Stable by default with narrow beta opt-in minimizes migration and compatibility risk.requiredAzdVersionapplies to the extension release as a whole. A single beta dependency therefore raises the minimum azd version for that extension version, even if its other services remain onv1.Versioning model
v1is the stable compatibility-major contract and evolves through protobuf-compatible additions.v1betais the preview channel and may add preview messages, fields, enum values, methods, and services. Compose and Copilot are currently beta-only services.v1; nov1.1protobuf package is created.v1betaimplementations directly.The versioned contracts become the baseline for future Buf historical compatibility checks. The frozen legacy aliases are migration-only runtime registrations and are not a new source contract baseline.
Testing
mage preflightmake protoreproducibility checkTelemetry Change Checklist
New Fields
fields/fields.go; extension-suppliedext.*values are covered by the extension's own review insteaddocs/specs/metrics-audit/telemetry-schema.mdNew Events
events/events.go— not applicable; no event was addedconstwhose Go identifier containsEvent— not applicabledocs/specs/metrics-audit/telemetry-schema.md— not applicableprefix.noun.verb) — not applicablePrivacy
CustomerContentemitted in telemetryTesting
Downstream
Documentation
docs/specs/metrics-audit/feature-telemetry-matrix.md)docs/specs/metrics-audit/telemetry-schema.md)