Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/pr-auto-semver.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ jobs:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_BODY: ${{ github.event.pull_request.body }}
steps:

- uses: actions/checkout@v4
with:
fetch-depth: 0
Expand Down
79 changes: 79 additions & 0 deletions .github/workflows/pr-labels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: PR Label Validation Reusable Workflow

# Validate that the PR has exactly one of the required category labels and none of the
# forbidden labels. This is triggered on label changes so that manually adding or removing
# a label re-runs the validation.

# NOTES:
# - this workflow only works for github pull_request trigger
# - Github labels are managed by Terraform, see https://github.com/swissgeo/infra-terraform/blob/main/github/modules/issue-labels/main.tf

# Usage example:
# on:
# pull_request:
# types:
# - labeled
# - unlabeled

on:
workflow_call:
inputs:
required_labels:
description: 'JSON array of labels; exactly one must be present on the PR'
type: string
default: '["bug", "feature", "new-release", "data-integration"]'
forbidden_labels:
description: 'JSON array of labels that must not be present on the PR'
type: string
default: '["DO NOT MERGE :bomb:", "WIP :construction:"]'

jobs:
pr-labels:
name: Validate PR labels
runs-on: ubuntu-latest
steps:
- name: Check required and forbidden labels
uses: actions/github-script@v9
env:
REQUIRED_LABELS: ${{ inputs.required_labels }}
FORBIDDEN_LABELS: ${{ inputs.forbidden_labels }}
with:
script: |
// Every PR is also an issue in GitHub's API, so the issues endpoint returns PR labels too.
const { data: issueLabels } = await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const labels = issueLabels.map((label) => label.name);
core.info(`PR labels: ${JSON.stringify(labels)}`);

const forbiddenLabels = JSON.parse(process.env.FORBIDDEN_LABELS);
for (const label of forbiddenLabels) {
if (labels.includes(label)) {
core.setFailed(`PR has forbidden label: ${label}`);
return;
}
}

// If the skip-release-note label is present, no further check is needed
if (labels.includes("skip-release-note")) {
core.info(`PR has skip-release-note label`);
return;
}

// Exactly one of these category labels is required (other labels are allowed alongside it)
const requiredLabels = JSON.parse(process.env.REQUIRED_LABELS);
const matched = requiredLabels.filter((label) => labels.includes(label));

if (matched.length === 0) {
core.setFailed(`PR must have one of the following labels: ${requiredLabels.join(", ")}`);
return;
}

if (matched.length > 1) {
core.setFailed(`PR must have only one of the following labels, found: ${matched.join(", ")}`);
return;
}

core.info(`PR label check passed: ${matched.join(", ")}`);
8 changes: 8 additions & 0 deletions workflow-templates/pr-labels.properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "PR Label Validation Workflow",
"description": "PR label validation workflow template for Swissgeo project. This template validates that a PR has exactly one required category label and no forbidden labels, re-checking whenever a label is added or removed.",
"iconName": "swissgeo-logo",
"categories": [
"JavaScript", "TypeScript", "Python"
]
}
11 changes: 11 additions & 0 deletions workflow-templates/pr-labels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: on-pr-label

on:
pull_request:
types:
- labeled
- unlabeled

jobs:
pr-labels:
uses: swissgeo/.github/.github/workflows/pr-labels.yml@main