Repository navigation
Add automated namespace approval workflow for spec PRs #44085
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Mike Harder (mikeharder)
merged 24 commits into
Azure:main
from
samvaity:namespace-approval
Jul 8, 2026
Merged
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
3b59a3d
Add automated namespace approval workflow for spec PRs
samvaity 4f28a24
Address review feedback: unlabeled guard, remove C++/Rust
samvaity 37aa3d1
Add unit tests and namespace format validation
samvaity 67ed6c8
Address review: split workflow, use repo helpers, add zod schema
samvaity 01ea7c6
Fix CI: prettier formatting and cspell word
samvaity cf496ef
Fix: use sparse-checkout add instead of git checkout for hydration
samvaity c11675f
Parse tspconfig.yaml directly, add commit status gate, fix plane dete…
samvaity 1b9a3b6
Split status workflow into separate files following repo convention
samvaity 02ccf7e
Fix: deduplicate job name in code workflow to match repo convention
samvaity a55968c
Address review: add else fallback in status check, fix check name in …
samvaity bb3a30c
Add Namespace Approval to summarize-checks for Next Steps to Merge
samvaity 9dc65fd
Add Java Maven artifact dual namespace, selective reset, lint and typ…
samvaity 849da69
Fix namespace approval TS type errors
samvaity 843da16
Address review feedback: error handling, shared helpers, and cleanup
samvaity 9f03703
Fix CodeQL ReDoS alert: replace regex with string methods in tspconfi…
samvaity 759d377
Add coverage test for tspconfig filter in changed-files
samvaity 2a9c182
Fix namespace-approval TypeScript null and env typing errors
samvaity 7cead2e
Address review issues: shared approvers, pagination, async format, ti…
samvaity 93c8ba4
Prevent duplicate 'Namespace Approved' comments on concurrent label e…
samvaity 54ea1e9
Rename labels to namespace-<lang>-*, add @handles, integrate with NSTM
samvaity 05b0587
Extract shared labels helper, remove createLabel, clarify plane detec…
samvaity b430ea0
Fix NSTM: clarify namespace-approved-all is mgmt-plane only
samvaity c890e3e
changed-files helper cleanup
mikeharder 3a7483f
Merge branch 'main' into namespace-approval
mikeharder File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| # Namespace Approval Process | ||
|
|
||
| ## Overview | ||
|
|
||
| When a service team adds or modifies SDK namespace configuration in their `tspconfig.yaml`, the namespace must be approved by authorized architects before the spec PR can merge. | ||
|
|
||
| This process is automated via GitHub Actions on this repository. | ||
|
|
||
| ## How It Works | ||
|
|
||
| ### Detection | ||
|
|
||
| When a PR modifies any `tspconfig.yaml` file, the workflow: | ||
|
|
||
| 1. Identifies which languages have namespace/package-name configuration | ||
| 2. Extracts namespaces using the `@azure-tools/typespec-metadata` emitter (primary) with YAML parsing fallback | ||
| 3. Determines if the service is data-plane or management-plane | ||
| 4. Applies `<lang>-namespace-pending` labels for each detected language | ||
|
|
||
| ### Approval | ||
|
|
||
| - **Data plane:** Each language architect approves their language by applying `<lang>-namespace-approved` | ||
| - **Management plane:** ArthurMa1978 or m-nash can apply `namespace-approved-all` to approve all languages at once | ||
|
|
||
| ### Status Check | ||
|
|
||
| A required status check (`namespace-approval`) blocks merge until all pending namespaces are approved. The check: | ||
|
|
||
| - ✅ Passes when all `*-namespace-pending` labels have corresponding `*-namespace-approved` labels | ||
| - ❌ Fails when any pending namespace lacks approval | ||
| - ⏭️ Skipped when the PR doesn't modify tspconfig.yaml (no namespace changes) | ||
|
|
||
| ### Reset on Change | ||
|
|
||
| If a service team pushes a commit that **changes** a namespace value (not just TypeSpec code), the approval for that language is reset: | ||
|
|
||
| - The `<lang>-namespace-approved` label is removed | ||
| - The `<lang>-namespace-pending` label is re-applied | ||
| - The status check fails again until re-approved | ||
|
|
||
| TypeSpec-only changes (no namespace modification) do **not** reset approval. | ||
|
|
||
| ## Labels | ||
|
|
||
| | Label | Applied by | Meaning | | ||
| | --------------------------- | ---------------- | ---------------------------------------- | | ||
| | `<lang>-namespace-pending` | Bot | Namespace detected, awaiting approval | | ||
| | `<lang>-namespace-approved` | Architect | Namespace approved for this language | | ||
| | `namespace-approved-all` | Architect (mgmt) | Shortcut: approves all pending languages | | ||
| | `Mgmt` | Bot | PR is management plane | | ||
|
|
||
| Where `<lang>` is one of: `dotnet`, `java`, `python`, `typescript`, `go`, `cpp`, `rust` | ||
|
samvaity marked this conversation as resolved.
Resolved
|
||
|
|
||
| ## Authorized Approvers | ||
|
|
||
| Defined in [`.github/namespace-approvers.yml`](../.github/namespace-approvers.yml): | ||
|
|
||
| <!-- cspell:disable --> | ||
|
|
||
| | Scope | Approvers | | ||
| | -------------------- | ----------------------------- | | ||
| | .NET | @jsquire, @m-nash | | ||
| | Java | @JonathanGiles, @alzimmermsft | | ||
| | Python | @xirzec, @kashifkhan | | ||
| | TypeScript | @xirzec, @maorleger | | ||
| | Go | @jhendrixMSFT | | ||
| | C++ | @LarryOsterman | | ||
| | Rust | @heaths, @LarryOsterman | | ||
| | Mgmt (all languages) | @ArthurMa1978, @m-nash | | ||
|
|
||
| <!-- cspell:enable --> | ||
|
|
||
| ## For Service Teams | ||
|
|
||
| No action needed beyond your normal workflow: | ||
|
|
||
| 1. Add/modify namespace in your `tspconfig.yaml` | ||
| 2. Open a PR as usual | ||
| 3. The bot will apply pending labels and post a status table | ||
| 4. Wait for architects to approve — they're notified via labels | ||
| 5. Once approved, the status check passes and the PR can merge | ||
|
|
||
| ## For Architects | ||
|
|
||
| 1. Watch for PRs with `<lang>-namespace-pending` labels | ||
| 2. Review the proposed namespace in the PR diff | ||
| 3. If acceptable, apply `<lang>-namespace-approved` label | ||
| 4. For management plane PRs, you can use `namespace-approved-all` to approve all at once | ||
|
|
||
| ## Configuration | ||
|
|
||
| To update approvers, submit a PR modifying `.github/namespace-approvers.yml`. Changes take effect immediately on merge. | ||
|
|
||
| ## Relationship to API Review | ||
|
|
||
| Namespace approval is **separate from API review**: | ||
|
|
||
| - **Namespace approval** (this repo): gates spec PR merge, ensures library naming is correct | ||
| - **API review** (azure-sdk repo): reviews API surface design (methods, models, parameters) | ||
|
|
||
| A new service typically needs namespace approval first (on their spec PR), then API review later (when SDK is built). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # cspell:words jsquire alzimmermsft xirzec kashifkhan maorleger jhendrixMSFT LarryOsterman heaths ArthurMa1978 | ||
| # Namespace Approvers Configuration | ||
| # This file defines who can approve namespace changes per language. | ||
| # To update, submit a PR to this file. In the future, this may be | ||
| # synced automatically from ARCHITECTS files in each language repo. | ||
|
|
||
| data-plane: | ||
| dotnet: | ||
| - jsquire | ||
| - m-nash | ||
| java: | ||
| - JonathanGiles | ||
| - alzimmermsft | ||
| python: | ||
| - xirzec | ||
| - kashifkhan | ||
| typescript: | ||
| - xirzec | ||
| - maorleger | ||
| go: | ||
| - jhendrixMSFT | ||
| cpp: | ||
| - LarryOsterman | ||
| rust: | ||
| - heaths | ||
| - LarryOsterman | ||
|
|
||
| management-plane: | ||
| all: | ||
| - ArthurMa1978 | ||
| - m-nash |
|
samvaity marked this conversation as resolved.
Resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| name: Namespace Approval | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: | ||
|
samvaity marked this conversation as resolved.
Resolved
|
||
| - opened | ||
| - synchronize | ||
| - labeled | ||
| - unlabeled | ||
|
|
||
| permissions: | ||
| pull-requests: write | ||
|
samvaity marked this conversation as resolved.
Resolved
|
||
| contents: read | ||
|
|
||
| jobs: | ||
| detect-namespaces: | ||
| if: github.event.action == 'opened' || github.event.action == 'synchronize' | ||
| name: Detect Namespace Changes | ||
|
|
||
| runs-on: ubuntu-24.04 | ||
|
|
||
| outputs: | ||
| has_pending: ${{ steps.detect.outputs.has_pending }} | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| with: | ||
| fetch-depth: 0 | ||
|
samvaity marked this conversation as resolved.
Resolved
|
||
|
|
||
| - name: Setup Node and install dependencies | ||
| uses: ./.github/actions/setup-node-install-deps | ||
|
|
||
| - name: Install dependencies for github-script actions | ||
| uses: ./.github/actions/install-deps-github-script | ||
|
|
||
| - id: detect | ||
| name: Detect namespace changes | ||
| uses: actions/github-script@v8 | ||
| with: | ||
| script: | | ||
| const { default: detectNamespaces } = | ||
| await import('${{ github.workspace }}/.github/workflows/src/namespace-approval/detect-namespaces.js'); | ||
| await detectNamespaces({ github, context, core }); | ||
|
|
||
| validate-approval: | ||
| if: github.event.action == 'labeled' | ||
| name: Validate Namespace Approval | ||
|
|
||
| runs-on: ubuntu-24.04 | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| with: | ||
| ref: ${{ github.event.pull_request.base.ref }} | ||
| sparse-checkout: | | ||
| .github | ||
|
|
||
| - name: Install dependencies for github-script actions | ||
| uses: ./.github/actions/install-deps-github-script | ||
|
|
||
| - name: Validate namespace approval label | ||
| uses: actions/github-script@v8 | ||
| with: | ||
| script: | | ||
| const { default: validateApproval } = | ||
| await import('${{ github.workspace }}/.github/workflows/src/namespace-approval/validate-approval.js'); | ||
| await validateApproval({ github, context, core }); | ||
|
|
||
| status-check: | ||
| name: Namespace Approval Status | ||
| needs: [detect-namespaces, validate-approval] | ||
| if: always() | ||
|
|
||
| runs-on: ubuntu-24.04 | ||
|
|
||
| steps: | ||
| - name: Verify namespace approval status | ||
| uses: actions/github-script@v8 | ||
| with: | ||
| script: | | ||
| const { data: pr } = await github.rest.pulls.get({ | ||
|
samvaity marked this conversation as resolved.
Resolved
|
||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| pull_number: context.payload.pull_request.number, | ||
| }); | ||
| const labels = pr.labels.map((l) => l.name); | ||
|
|
||
| if (!labels.includes("namespace-review-required")) { | ||
| core.info("No namespace review required, passing"); | ||
| return; | ||
| } | ||
|
|
||
| const pendingLabels = labels.filter((l) => l.endsWith("-namespace-pending")); | ||
| if (pendingLabels.length > 0) { | ||
| core.setFailed( | ||
| `Namespace approval pending for: ${pendingLabels | ||
| .map((l) => l.replace("-namespace-pending", "")) | ||
| .join(", ")}`, | ||
| ); | ||
| } else if (labels.includes("namespace-approved")) { | ||
| core.info("All namespaces approved - merge allowed"); | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.