Skip to content

feat(ci): add doc review pipeline with auto-labeling and Copilot review #29

feat(ci): add doc review pipeline with auto-labeling and Copilot review

feat(ci): add doc review pipeline with auto-labeling and Copilot review #29

Workflow file for this run

name: Auto-label PRs
on:
pull_request:
types: [opened, synchronize]
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to label'
required: true
type: number
permissions: {}
concurrency:
group: auto-label-${{ github.event.number || inputs.pr_number }}
cancel-in-progress: true
jobs:
auto-label:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
# Skip draft PRs and fork PRs (workflow_dispatch always runs)
if: |
github.event_name == 'workflow_dispatch' ||
(!github.event.pull_request.draft &&
github.event.pull_request.head.repo.full_name == github.repository)
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
persist-credentials: false
sparse-checkout: |
content
data/products.yml
scripts/lib/content-utils.js
.github/scripts/workflow-utils.js
package.json
sparse-checkout-cone-mode: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 22
- name: Install js-yaml
run: npm install --no-save --ignore-scripts --no-package-lock --legacy-peer-deps js-yaml
- name: Apply product labels
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
with:
script: |
const {
getProductLabelMap,
matchFilesToLabels,
} = await import(
`${process.cwd()}/.github/scripts/workflow-utils.js`
);
const prNumber =
context.issue.number ||
Number('${{ inputs.pr_number }}');
if (!prNumber) {
core.setFailed('No PR number available');
return;
}
// --- Build path-to-label mapping from products.yml ---
const pathToLabel = await getProductLabelMap();
core.info(
`Loaded ${pathToLabel.size} path-to-label mappings from products.yml`
);
// --- Get changed files from the PR (paginated) ---
const files = await github.paginate(
github.rest.pulls.listFiles,
{
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
per_page: 100,
}
);
const changedFiles = files.map(f => f.filename);
core.info(`PR has ${changedFiles.length} changed files`);
// --- Match files to product labels ---
const labelsToAdd = matchFilesToLabels(changedFiles, pathToLabel);
if (labelsToAdd.size === 0) {
core.info('No product labels to add');
return;
}
// --- Get existing PR labels to avoid duplicates ---
const { data: prData } = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
});
const existingLabels = new Set(prData.labels.map(l => l.name));
const newLabels = [...labelsToAdd].filter(
l => !existingLabels.has(l)
);
if (newLabels.length === 0) {
core.info('All matching labels already present');
return;
}
// --- Apply labels ---
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: newLabels,
});
core.info(`Added labels: ${newLabels.join(', ')}`);