From c0d8f029da5ee33c970bfa597fd6783c2ffb12ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Olender?= <92638966+TC-MO@users.noreply.github.com> Date: Thu, 29 Oct 2026 17:51:04 +0100 Subject: [PATCH 1/3] ci: Request TW review from the PR's team label Team labels map to R&D groups, so the right writer gets pulled in even on fork PRs, without relying on CODEOWNERS paths. --- .github/workflows/request-tw-review.yaml | 38 ++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/request-tw-review.yaml diff --git a/.github/workflows/request-tw-review.yaml b/.github/workflows/request-tw-review.yaml new file mode 100644 index 0000000000..f95c90faf1 --- /dev/null +++ b/.github/workflows/request-tw-review.yaml @@ -0,0 +1,38 @@ +name: Request TW review by label + +on: + # Runs from master with a write token, so fork PRs work too. Never check out PR code here. + pull_request_target: + types: [labeled] + +permissions: + pull-requests: write + +jobs: + request_review: + name: Request TW review + runs-on: ubuntu-latest + steps: + - name: Request the writer for the team label + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + LABEL: ${{ github.event.label.name }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + run: | + # Team labels → R&D group → technical writer + case "$LABEL" in + t-core-services|t-infra|t-console|t-unblocking) reviewer=TC-MO ;; # Foundation + t-publishers|t-billing|t-tooling|t-builders|"t-c&c") reviewer=szaganek ;; # Creators + t-acquisition|t-growth|t-ai|t-integrations|t-discovery) reviewer=marcel-rbro ;; # Integrators + *) echo "No writer mapped to $LABEL"; exit 0 ;; + esac + + # GitHub rejects a review request from the PR author + if [ "$reviewer" = "$PR_AUTHOR" ]; then + echo "$reviewer is the PR author, skipping" + exit 0 + fi + + gh pr edit "$PR_NUMBER" --add-reviewer "$reviewer" From f6e887ddb61521c6f8f2e4bf7b7e6b05b82b4eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Olender?= <92638966+TC-MO@users.noreply.github.com> Date: Thu, 29 Oct 2026 17:56:09 +0100 Subject: [PATCH 2/3] ci: Wait for ready-for-review before requesting the TW Matches CODEOWNERS, so writers aren't pinged on drafts or closed PRs. --- .github/workflows/request-tw-review.yaml | 38 ++++++++++++++++-------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/.github/workflows/request-tw-review.yaml b/.github/workflows/request-tw-review.yaml index f95c90faf1..864cb19031 100644 --- a/.github/workflows/request-tw-review.yaml +++ b/.github/workflows/request-tw-review.yaml @@ -3,7 +3,7 @@ name: Request TW review by label on: # Runs from master with a write token, so fork PRs work too. Never check out PR code here. pull_request_target: - types: [labeled] + types: [labeled, ready_for_review] permissions: pull-requests: write @@ -11,28 +11,40 @@ permissions: jobs: request_review: name: Request TW review + # Like CODEOWNERS: no requests on drafts or closed PRs + if: github.event.pull_request.state == 'open' && !github.event.pull_request.draft runs-on: ubuntu-latest steps: - name: Request the writer for the team label env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - LABEL: ${{ github.event.label.name }} + # The new label, or all labels when a draft becomes ready + LABELS: ${{ github.event.action == 'labeled' && github.event.label.name || join(github.event.pull_request.labels.*.name, ' ') }} PR_NUMBER: ${{ github.event.pull_request.number }} PR_AUTHOR: ${{ github.event.pull_request.user.login }} run: | - # Team labels → R&D group → technical writer - case "$LABEL" in - t-core-services|t-infra|t-console|t-unblocking) reviewer=TC-MO ;; # Foundation - t-publishers|t-billing|t-tooling|t-builders|"t-c&c") reviewer=szaganek ;; # Creators - t-acquisition|t-growth|t-ai|t-integrations|t-discovery) reviewer=marcel-rbro ;; # Integrators - *) echo "No writer mapped to $LABEL"; exit 0 ;; - esac + set -f # label names are not globs + reviewers="" + for label in $LABELS; do + # Team labels → R&D group → technical writer + case "$label" in + t-core-services|t-infra|t-console|t-unblocking) reviewer=TC-MO ;; # Foundation + t-publishers|t-billing|t-tooling|t-builders|"t-c&c") reviewer=szaganek ;; # Creators + t-acquisition|t-growth|t-ai|t-integrations|t-discovery) reviewer=marcel-rbro ;; # Integrators + *) continue ;; + esac + # GitHub rejects a review request from the PR author + [ "$reviewer" = "$PR_AUTHOR" ] && continue + case ",$reviewers," in + *",$reviewer,"*) ;; + *) reviewers="${reviewers:+$reviewers,}$reviewer" ;; + esac + done - # GitHub rejects a review request from the PR author - if [ "$reviewer" = "$PR_AUTHOR" ]; then - echo "$reviewer is the PR author, skipping" + if [ -z "$reviewers" ]; then + echo "No writer to request for: $LABELS" exit 0 fi - gh pr edit "$PR_NUMBER" --add-reviewer "$reviewer" + gh pr edit "$PR_NUMBER" --add-reviewer "$reviewers" From cae3cb1e80ec431a1645063f585cf3364f158434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Olender?= <92638966+TC-MO@users.noreply.github.com> Date: Fri, 30 Oct 2026 09:58:01 +0100 Subject: [PATCH 3/3] ci: Fall back to TC-MO when no TW is requested The PR toolkit skips authors outside Product Engineering, so about half of outside docs PRs get no team label and no writer. --- .github/workflows/request-tw-review.yaml | 36 ++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/.github/workflows/request-tw-review.yaml b/.github/workflows/request-tw-review.yaml index 864cb19031..df04b63f38 100644 --- a/.github/workflows/request-tw-review.yaml +++ b/.github/workflows/request-tw-review.yaml @@ -3,7 +3,7 @@ name: Request TW review by label on: # Runs from master with a write token, so fork PRs work too. Never check out PR code here. pull_request_target: - types: [labeled, ready_for_review] + types: [opened, labeled, ready_for_review] permissions: pull-requests: write @@ -12,7 +12,7 @@ jobs: request_review: name: Request TW review # Like CODEOWNERS: no requests on drafts or closed PRs - if: github.event.pull_request.state == 'open' && !github.event.pull_request.draft + if: github.event.action != 'opened' && github.event.pull_request.state == 'open' && !github.event.pull_request.draft runs-on: ubuntu-latest steps: - name: Request the writer for the team label @@ -48,3 +48,35 @@ jobs: fi gh pr edit "$PR_NUMBER" --add-reviewer "$reviewers" + + fallback: + name: Request triage TW when no writer is set + # PRs from writers and bots need no triage + if: >- + (github.event.action == 'opened' || github.event.action == 'ready_for_review') + && !github.event.pull_request.draft + && github.event.pull_request.user.type != 'Bot' + && !contains(fromJSON('["TC-MO", "szaganek", "marcel-rbro"]'), github.event.pull_request.user.login) + runs-on: ubuntu-latest + steps: + - name: Request TC-MO if no writer was requested + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + # The PR toolkit adds the team label 20-30 s after the PR is ready; give the label job time to run + sleep 90 + + writers=$(gh pr view "$PR_NUMBER" --json reviewRequests,reviews,state,isDraft --jq ' + if .state != "OPEN" or .isDraft then "skip" + else [.reviewRequests[].login, .reviews[].author.login] + | map(select(. == "TC-MO" or . == "szaganek" or . == "marcel-rbro")) | join(",") + end') + + if [ -n "$writers" ]; then + echo "Nothing to do: $writers" + exit 0 + fi + + gh pr edit "$PR_NUMBER" --add-reviewer TC-MO