diff --git a/.github/workflows/enforce-label.yml b/.github/workflows/enforce-label.yml index e8d9751..f6863d3 100644 --- a/.github/workflows/enforce-label.yml +++ b/.github/workflows/enforce-label.yml @@ -1,9 +1,13 @@ name: Enforce PR label on: - pull_request: + pull_request_target: types: [labeled, unlabeled, opened, edited, synchronize] +permissions: + issues: write + pull-requests: write + jobs: pr-title: name: Check conventional commit PR title @@ -11,6 +15,10 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + # WARN: With `pull_request_target` it is unsafe to execute user code, we execute + # pixi/commitlint from target branch. + ref: ${{ github.event.pull_request.base.sha }} - name: Setup Pixi uses: prefix-dev/setup-pixi@v0.9.3 with: @@ -18,14 +26,65 @@ jobs: cache: true manifest-path: pyproject.toml - name: Verify conventional commit conventions on PR title + id: commitlint run: echo "${{ github.event.pull_request.title }}" | pixi run commitlint - # TODO: we have not added the labels but we can add them automatically - # from conventional commit titles - # pr-label: - # name: Check PR label - # runs-on: ubuntu-latest - # permissions: - # pull-requests: write - # steps: - # - name: enforce-triage-label - # uses: jupyterlab/maintainer-tools/.github/actions/enforce-label@v1 + - name: Apply PR label + if: success() + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: |- + const title = context.payload.pull_request.title; + + // Extract conventional commit type and optional scope + const match = title.match(/^([a-z]+)(?:\(([a-z]+)\))?:/); + if (!match) { + console.log("No conventional commit type found in PR title"); + return; + } + + const commitType = match[1]; + const commitScope = match[2]; // optional + console.log(`Detected commit type: ${commitType}`); + if (commitScope) { + console.log(`Detected commit scope: ${commitScope}`); + } + + // Map commit types to PR labels + const typeToLabel = { + "fix": "bug", + "feat": "feature", + "doc": "documentation", + "chore": "maintenance", + "ci": "maintenance", + "dev": "maintenance", + "test": "maintenance", + "perf": "enhancement", + "release": "maintenance", + "revert": "maintenance" + }; + + const label = typeToLabel[commitType]; + if (!label) { + console.log(`No label mapping found for commit type: ${commitType}`); + return; + } + + console.log(`Applying type label: ${label}`); + + // Collect labels to add + const labelsToAdd = [label]; + + // Add scope label if applicable (client or server) + if (commitScope && ["client", "server"].includes(commitScope)) { + console.log(`Applying scope label: ${commitScope}`); + labelsToAdd.push(commitScope); + } + + // Apply labels (GitHub API is idempotent - ignores already present labels) + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels: labelsToAdd + });