Skip to content

API Review Hub Onboarding

Travis Prescott edited this page Oct 22, 2026 · 9 revisions

Onboarding a Language to API Review Hub

Use this guide to connect an SDK language repository to API Review Hub (ARH), implement its artifact-generation templates, and test the integration.

Prerequisites

You need:

  • A fork or branch of the language repository.
  • A branch of Azure/azure-sdk-tools when the shared dispatcher or base templates must change.
  • Access to the azure-sdk-engineering-system GitHub organization.

1. Install the GitHub Apps

You only need to do this if working on a fork of a repo. If working on the Azure-owned repo, this should already be done for you.

Open the Azure SDK Engineering System GitHub App settings and install both apps in the repository:

  • API Review Hub coordinates API reviews and approval labels.
  • Azure SDK Automation performs repository actions for the review process.

For a Microsoft-owned repository, follow the GitHub App approval process. A fork owner can normally approve installation under Settings > GitHub Apps.

Contact trprescott@microsoft.com if approval or installation is blocked.

2. Register the Repository

Ask the ARH maintainers to register the repository. Provide:

  • Repository full name, such as Azure/azure-sdk-for-python.

  • GitHub repository ID:

    gh api repos/{OWNER}/{REPO} --jq .id
  • Approval inheritance policy:

    • Pre-release to pre-release (default: yes).
    • Patch update (default: yes).
    • Pre-release to stable (default: no).
    • Minor update (default: no).
    • Major update (default: no).
  • Review policy:

    • Data-plane first beta release (default: no).
    • Data-plane subsequent beta release (default: no).
    • Data-plane stable release (default: yes).
    • Management-plane first beta release (default: no).
    • Management-plane subsequent beta release (default: no).
    • Management-plane stable release (default: no).

As part of registration, the ARH maintainers update eng/pipelines/handle-apireview-hub-request.yml in Azure/azure-sdk-tools to:

  • Add the language to the language parameter values, if needed.
  • Add the language repository resource.

As a maintainer webhook check:

  1. Create or open an issue in the repository.
  2. Add sdk-api-review-approved.
  3. Confirm Azure SDK Automation removes the label and explains that ARH controls it.

If the check fails, the maintainers should verify both GitHub App installations, repository registration, and webhook delivery.

3. Add the ARCHITECTS File

ARH uses .github/ARCHITECTS to determine who may approve an API review. The file follows CODEOWNERS pattern syntax.

# Catch all SDK packages
/sdk/ @your-github-team-or-user

Commit the file to the repository's default branch. See the Python ARCHITECTS file for a larger example.

4. Implement the Pipeline Templates

ARH queues eng/pipelines/handle-apireview-hub-request.yml from Azure/azure-sdk-tools. The dispatcher builds a self-contained tooling artifact once, then runs independent base and target artifact-generation jobs. Each generation job clones only the requested package source ref and downloads the same bin artifact.

The language repository owns:

  1. One required build-tools wrapper that packages all required tooling and scripts into $(BinOutputDirectory).
  2. One required artifact-generation wrapper which will be used on the base and target refs.

The shared base templates own checkouts, artifact transfer, publishing, and ARH callbacks. Language templates must not duplicate that orchestration.

%%{init: {"flowchart": {"defaultRenderer": "elk"}}}%%
flowchart LR
    subgraph Build["Build Tools Job"]
        direction TB
        BT["apireview-hub-build-tools-{language}.yml"]
        BTB["apireview-hub-build-tools-base.yml"]
        BIN[("bin artifacts")]

        BT -->|"wraps and supplies<br/>build steps"| BTB
        BTB -->|"publishes"| BIN
    end

    subgraph Generate["Generate Artifacts Job"]
        direction TB
        AG["apireview-hub-generate-artifact-{language}.yml"]
        AGB["apireview-hub-generate-artifact-base.yml"]

        AG -->|"wraps and supplies<br/>generation steps"| AGB
    end

    BT --> AG
    BIN -->|"used by"| AGB

    classDef language fill:#0ea5e9,stroke:#075985,color:#ffffff
    classDef shared fill:#ea6a2a,stroke:#9a3412,color:#ffffff
    classDef artifact fill:#0e7490,stroke:#164e63,color:#ffffff
    class BT,AG language
    class BTB,AGB shared
    class BIN artifact
    style Build fill:transparent,stroke:#0070c0,stroke-width:3px
    style Generate fill:transparent,stroke:#3f7f35,stroke-width:3px
Loading

Build-tools wrapper

Create:

eng/pipelines/templates/jobs/apireview-hub-build-tools-{language}.yml

Declare and explicitly forward the shared parameters:

parameters:
  - name: languageRepoResource
    type: string
  - name: toolsRef
    type: string
  - name: languageToolsRef
    type: string

jobs:
  - template: /eng/common/pipelines/templates/jobs/apireview-hub-build-tools-base.yml
    parameters:
      languageRepoResource: ${{ parameters.languageRepoResource }}
      toolsRef: ${{ parameters.toolsRef }}
      languageToolsRef: ${{ parameters.languageToolsRef }}
      stepList:
        # Language-owned build steps.

Declare and forward any supported pool or image override in the same way.

The language-specific build steps have these directories available:

  • $(ToolsSourceDirectory): shared azure-sdk-tools/eng sources at toolsRef.
  • $(LanguageToolsSourceDirectory): language repository sources at languageToolsRef; also, an alias of $(Build.SourcesDirectory) and $(Pipeline.Workspace)/s.
  • $(BinOutputDirectory): destination for every script, binary, configuration file, and other tool required by generation jobs.

The custom stepList may install a runtime, restore dependencies, compile tools, or package tools. Copy all required tooling and script files into $(BinOutputDirectory); generation jobs cannot rely on either source checkout. Do not publish artifacts or write generated output outside $(BinOutputDirectory).

Artifact-generation wrapper

Create:

eng/pipelines/templates/jobs/apireview-hub-generate-artifact-{language}.yml

For example, the Python wrapper declares and forwards:

parameters:
  - name: kind
    type: string
  - name: repositoryOwner
    type: string
    default: Azure
  - name: ref
    type: string
  - name: packageName
    type: string
  - name: operationId
    type: string

jobs:
  - template: /eng/common/pipelines/templates/jobs/apireview-hub-generate-artifact-base.yml
    parameters:
      kind: ${{ parameters.kind }}
      language: python
      repositoryFullName: ${{ format('{0}/{1}', parameters.repositoryOwner, 'azure-sdk-for-python') }}
      ref: ${{ parameters.ref }}
      packageName: ${{ parameters.packageName }}
      operationId: ${{ parameters.operationId }}
      stepList:
        # Language-owned generation steps.

Use your language's fixed language and repository names. Declare and forward any supported pool or image override in the same way.

The shared base template provides:

  • $(Build.SourcesDirectory): language source repository at the requested base or target ref.
  • $(BinDirectory): the self-contained tooling directory downloaded from the build-tools job.
  • $(ArtifactOutputDirectory): destination for the generated review bundle.

Generation steps must:

  1. Install or activate tooling from $(BinDirectory).
  2. Locate packageName under $(Build.SourcesDirectory).
  3. Build the package when required.
  4. Generate the API review files.
  5. Copy api.md, api.metadata.yml, and any additional supplemental files into $(ArtifactOutputDirectory).

Do not clone repositories, switch refs, check out tooling sources, download or publish pipeline artifacts, call ARH, or fetch a different tooling revision.

5. Emit a Valid Review Bundle

Each base or target bundle must contain:

  • api.md: the generated API surface.
  • api.metadata.yml: metadata describing the package and API surface.

api.metadata.yml requires these non-empty fields:

apiHash: 607621e4437753200b71dcdcddaddcabeaf41944273a4600713b004471117189
packageVersion: 1.2.0
packageRelativePath: sdk/example/example-package
  • apiHash is calculated using the language's configured hash algorithm and input files, typically including at least api.md and optionally other files. ARH treats it as an opaque, non-empty string and persists it unchanged without recalculating or verifying it.
  • packageVersion is the version read from the checked-out package source.
  • packageRelativePath is the package path relative to $(Build.SourcesDirectory), using / separators.

Expose the same hash calculation for reuse during the release pipeline, using the same algorithm and input-file selection. A reusable script or template is recommended so artifact generation and release checks cannot drift.

Additional language-specific metadata, such as runtime or generator versions, is allowed. Supplemental review files may also be included in the bundle.

Place the complete bundle in $(ArtifactOutputDirectory); the shared template handles validation, publishing, and notifying ARH.

6. Test with Request-scoped Refs

Develop the dispatcher, shared templates, and language templates on branches.

Pass the development refs in the create-review request, most commonly using the AzSDK CLI:

  • pipelineRef: azure-sdk-tools ref containing the dispatcher to queue. Set environment variable: AZSDK_API_REVIEW_PIPELINE_REF
  • toolsRef: azure-sdk-tools ref containing shared tools and base templates. Set environment variable: AZSDK_API_REVIEW_TOOLS_REF
  • languageToolsRef: language repository ref containing its tools and wrappers. Set environment variable: AZSDK_API_REVIEW_LANGUAGE_TOOLS_REF

Any omitted ref defaults to main.

These refs are independent of the package source refs. baseTag and targetBranch.name select the package revisions; the three tooling refs select the pipeline and tooling used to generate both bundles.

Example request body:

{
  "language": "python",
  "packageName": "azure-keyvault-keys",
  "packageType": "data",
  "baseTag": "azure-keyvault-keys_4.10.0",
  "targetBranch": {
    "owner": "your-fork",
    "repo": "azure-sdk-for-python",
    "name": "feature/api-change"
  },
  "pipelineRef": "feature/arh-language-onboarding",
  "toolsRef": "feature/arh-language-onboarding",
  "languageToolsRef": "feature/arh-language-onboarding"
}

Use the equivalent options when invoking azsdk api-review create directly or through the Azure SDK CLI. Confirm that the proposed command includes the three development refs before approving it.

For an existing package, packageType may be omitted. A new package must provide it. If supplied for an existing package, it must match the registered value.

7. Verify the End-to-end Flow

Request a review using the development refs from Step 6 and confirm that ARH successfully creates the review pull request with the expected API changes. If it fails, share the operation ID with the ARH maintainers.

8. Merge and Retest from main

Merge the language-repository templates and any required azure-sdk-tools dispatcher or base-template changes. Then repeat the create-review test without pipelineRef, toolsRef, or languageToolsRef; all three default to main.

No shared configuration change or temporary dispatcher edit needs to be reverted.

9. Integrate the Release Gate

Use the following shared step templates from Azure/azure-sdk-tools. They are synced into each language repository, so reference the local /eng/common paths, without an @azure-sdk-tools repository alias.

Template Purpose Placement
create-apiview-revision.yml Creates APIView revisions only; does not check release approval. After generating the package artifacts and package-info files.
get-package-approval-status.yml Calls azsdk package get-approval-status and fails when the overall approval result does not permit release. Before publishing packages.
mark-package-released.yml Calls azsdk package mark-released to record successful publishing through ARH and report the CLI's backend results. After packages have been successfully published.

Update existing release pipelines that relied on APIView revision creation to also check approval. Revision creation is not a release gate, and marking a package released is not an approval check.

Prepare the metadata and authentication

  • Sync the templates and their supporting scripts and CLI installer into the language repository before referencing them.
  • Make the package artifacts and package-info JSON files available in the job that uses each template. Download them explicitly when crossing jobs or stages.
  • Each package-info file must contain Name, Version, and, for ARH, ApiHash. Populate ApiHash using the same calculation and input files used for the review bundle in Step 5. The templates read package-info JSON, not api.metadata.yml.
  • Pass explicit file paths in the PackageInfoFiles list; do not pass a directory or glob. Absolute paths avoid ambiguity because the ARH scripts run from $(Pipeline.Workspace).
  • Both ARH templates require the Azure SDK Tools CLI installed in the calling job. Use /eng/common/pipelines/templates/steps/install-azsdk-cli.yml, which sets $(AZSDK), or provide that variable yourself. Use a CLI release supporting both API and package name approval results; the scripts currently enforce a minimum version of 0.6.38.
  • Authorize the ADO to ARH Service Connection service connection for the consuming pipeline. Coordinate its identity and ARH access with the maintainers. APIView revision creation separately uses APIView prod deployment.
  • The scripts derive the language from the repository's shared language settings. RepoOwner is optional; set it when targeting a registered fork rather than the default owner. SourceRootPath defaults to $(Build.SourcesDirectory) and must point at the language checkout containing eng/common, especially in multi-checkout jobs.

Example package-info content, using the actual package name, release version, and generated hash:

{
  "Name": "azure-example",
  "Version": "1.2.0",
  "ApiHash": "607621e4437753200b71dcdcddaddcabeaf41944273a4600713b004471117189"
}

Create APIView revisions

Keep or add this step in the artifact-generation/build job after the package-info file and artifacts exist. The example assumes artifacts are staged under $(Build.ArtifactStagingDirectory); adapt the paths to the language pipeline.

steps:
  # Existing steps build and stage the package and its package-info file.
  - template: /eng/common/pipelines/templates/steps/create-apiview-revision.yml
    parameters:
      ArtifactPath: $(Build.ArtifactStagingDirectory)
      ConfigFileDir: $(Build.ArtifactStagingDirectory)/PackageInfo
      PackageInfoFiles:
        - $(Build.ArtifactStagingDirectory)/PackageInfo/azure-example.json
      SourceRootPath: $(Build.SourcesDirectory)

Preserve existing language-specific Artifacts, ArtifactName, or PackageName parameters when already configured. GenerateApiReviewForManualOnly: true restricts revision creation to manual runs. The template itself limits the APIView task to internal, non-PR runs outside -pr repositories; it does not authorize publishing.

Check approval before publishing

Insert the following steps into the release job before its existing publishing steps, after resolving or downloading the package-info files:

steps:
  - template: /eng/common/pipelines/templates/steps/install-azsdk-cli.yml
    parameters:
      SourceRootPath: $(Build.SourcesDirectory)

  - template: /eng/common/pipelines/templates/steps/get-package-approval-status.yml
    parameters:
      PackageInfoFiles:
        - $(Build.ArtifactStagingDirectory)/PackageInfo/azure-example.json
      SourceRootPath: $(Build.SourcesDirectory)
      # RepoOwner: your-fork-owner

Gate every package being published, including packages whose API review policy does not require architect review. For ARH-managed packages, package name approval is always required: reviewNotRequired applies only to API review and cannot bypass missing package name approval. Release may proceed only when the overall result is approved.

Keep publishing dependent on successful completion of the check. Do not use continueOnError or publishing conditions such as always() or succeededOrFailed() that let approval failures through. If the check and publishing are in different jobs or stages, add explicit dependencies and require success.

Check the template's run condition against the language's release triggers. The current approval task runs only in the internal project, after preceding steps succeed, for manual runs (or an empty build reason), or for IndividualCI on refs/heads/main. Other triggers can skip the task without failing the job. If the language publishes under another trigger, coordinate a shared-template condition update before enabling that release path. Releases from other refs still require package name approval; a skipped task is not approval.

The approval script ignores approval-check failures for package-info entries with ReleaseStatus: "Unreleased". Do not mark a package Unreleased if the pipeline will publish it; pass the intended release candidates and accurate metadata.

Record release completion after publishing

Insert this step after successful publishing, using the same package coordinates and API hash that were checked. Pass only package-info files for packages actually published.

steps:
  # Append after the existing successful publishing steps.
  - template: /eng/common/pipelines/templates/steps/mark-package-released.yml
    parameters:
      PackageInfoFiles:
        - $(Build.ArtifactStagingDirectory)/PackageInfo/azure-example.json
      SourceRootPath: $(Build.SourcesDirectory)
      # RepoOwner: your-fork-owner

When release recording is in a separate job, install the CLI there too, download the same metadata, and require the publishing job to succeed. Do not include this template in build-only, validation-only, or dry-run paths: it records a real release. For multi-package publishing, record only successful publications; do not submit a batch containing unpublished packages.

The templates recognize separate authorized skip controls: Skip.CreateApiReview, Skip.CheckPackageApproval, and Skip.MarkPackageReleased, each paired with IsRequesterAuthorizedToSkipApiReview: true. Keep the existing authorization mechanism; do not set these variables simply to onboard a language or bypass missing approvals.

Verify the release integration

Before enabling production publishing, confirm:

  1. The local synced templates resolve, metadata files are available, and both service connections are authorized.
  2. An unapproved ARH package blocks publishing, even when API review is optional.
  3. An approved package passes with the release artifact's exact version and API hash.
  4. Every supported release trigger actually executes the approval check rather than skipping it.
  5. Build-only and dry-run paths do not mark packages released.
  6. A successful controlled publication records the release, and a recording failure remains visible for repair without republishing the package.