From 829ca2253600593df7647fd696b5b4b16b9bc463 Mon Sep 17 00:00:00 2001 From: Thomas Leplus Date: Wed, 6 Aug 2025 20:03:59 +0200 Subject: [PATCH 1/2] ci(labels): automatic PR label management --- .github/pull_request_template.md | 3 +- .github/release.yml | 17 ++++ .github/workflows/check-pr.yml | 134 +++++++++++++++++++++++++++++++ CONTRIBUTING.md | 8 +- 4 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 .github/release.yml create mode 100644 .github/workflows/check-pr.yml diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2c59ff9..07b3821 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -6,10 +6,11 @@ Please check the boxes below to confirm that you have followed the required guidelines for contributions: +- [ ] All commits are [signed](https://docs.github.com/en/authentication/managing-commit-signature-verification). +- [ ] The title for this pull request is the same as the first commit's message and it follows the [conventional commits specification](https://www.conventionalcommits.org). See commit history for examples. - [ ] If this pull request includes code changes, they were all properly tested. Automated tests were also included where possible. - [ ] If applicable, this pull request includes the relevant documentation for this change. - [ ] If this pull request is related to an existing issue, you can use the same description below but in any case include a [link to the issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue) like `Fixes #ISSUE_NUMBER.` or `Closes #ISSUE_NUMBER.`. -- [ ] All the commits in this pull request were squashed into a single commit. That commit is [signed](https://docs.github.com/en/authentication/managing-commit-signature-verification). diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..b84ff83 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,17 @@ +changelog: + exclude: + labels: + - no-release-notes + categories: + - title: "New Features :sparkles:" + labels: + - feat + - title: "Bug Fixes :bug:" + labels: + - fix + - title: "Dependency Upgrades :package:" + labels: + - dependencies + - title: "Other Changes :broom:" + labels: + - "*" diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml new file mode 100644 index 0000000..eec907d --- /dev/null +++ b/.github/workflows/check-pr.yml @@ -0,0 +1,134 @@ +--- +name: "Check PR" +on: pull_request + +permissions: + actions: write + contents: write + pull-requests: write + +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Check commits + shell: bash + run: | + # Check the commits + commits_json=$(curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" "${PR_COMMITS_URL}") + echo -n 'Commits: ' + echo "${commits_json}" | jq '.' + commit_count="$(echo "${commits_json}" | jq -r 'length')" + # Check single commit + if [ "${commit_count}" -eq 1 ] ; then + commit_title="$(echo "${commits_json}" | jq -r '.[0].commit.message' | head -n 1)" + echo "Commit title: ${commit_title}" + if [[ "${commit_title}" != "${PR_TITLE}" ]] ; then + >&2 echo 'Single commit must have same title as PR.' + exit 1 + fi + fi + # Check that all commits are signed + for ((i = 0 ; i < commit_count ; i++ )); do + if [[ "$(echo "${commits_json}" | jq -r ".[${i}].commit.verification.verified")" == 'false' ]] ; then + >&2 echo "Commit $(echo "${commits_json}" | jq -r ".[${i}].sha") must be signed." + exit 1 + fi + done + env: + PR_TITLE: ${{github.event.pull_request.title}} + PR_COMMITS_URL: ${{github.event.pull_request.commits_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + - name: Authenticate CLI with a PAT + env: + RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} + if: env.RELEASE_TOKEN != '' + shell: bash + run: echo "${RELEASE_TOKEN}" | gh auth login --with-token + - name: Update PR labels + shell: bash + run: | + # Check PR title is a conventional commit message + regexp='^((build|chore|ci|docs|feat|fix|perf|refactor|style|test)(\([a-zA-Z0-9\-]+\))?)!?: .*$' + if ! [[ "${PR_TITLE}" =~ ${regexp} ]] ; then + >&2 echo 'Non conventional PR title.' + exit 1 + fi + scoped_type="${BASH_REMATCH[1]}" + type="${BASH_REMATCH[2]}" + add_labels=() + remove_labels=() + # Remove the labels we manage + for label in build chore ci docs feat fix perf refactor test style ; do + if [[ "${label}" == "${type}" ]]; then + echo "Label to add: ${label}" + # shellcheck disable=SC2076 + if [[ "${PR_LABELS}" =~ "\"${label}\"" ]] ; then + echo "Label ${label} already present" + else + add_labels+=("${label}") + fi + else + echo "Label to remove: ${label}" + # shellcheck disable=SC2076 + if [[ "${PR_LABELS}" =~ "\"${label}\"" ]] ; then + remove_labels+=("${label}") + else + echo "Label ${label} not present" + fi + fi + done + # If scope is dependency-related, add 'dependencies' label + regexp2='^[a-zA-Z0-9]+\([a-zA-Z0-9\-]*deps\)$' + if [[ "${scoped_type}" =~ ${regexp2} ]] ; then + echo "Label to add: dependencies" + # shellcheck disable=SC2076 + if [[ "${PR_LABELS}" =~ "\"dependencies\"" ]] ; then + echo "Label dependencies already present" + else + add_labels+=('dependencies') + fi + # otherwise do not remove it since we are not the only ones to manage this label (e.g. dependabot) + fi + # For certain types/scopes, add 'no-release-notes' label + if [[ "${type}" == 'chore' \ + || "${type}" == 'ci' \ + || "${type}" == 'docs' \ + || "${type}" == 'style' \ + || "${type}" == 'test' ]] ; then + echo "Label to add: no-release-notes" + # shellcheck disable=SC2076 + if [[ "${PR_LABELS}" =~ "\"no-release-notes\"" ]] ; then + echo "Label no-release-notes already present" + else + add_labels+=('no-release-notes') + fi + else + echo "Label to remove: no-release-notes" + # shellcheck disable=SC2076 + if [[ "${PR_LABELS}" =~ "\"no-release-notes\"" ]] ; then + remove_labels+=('no-release-notes') + else + echo "Label no-release-notes not present" + fi + fi + # Update the labels + function join_by { local IFS="$1"; shift; echo "$*"; } + if [ ${#add_labels[@]} -eq 0 ] && [ ${#remove_labels[@]} -eq 0 ]; then + echo 'No label to change' + elif [ ${#add_labels[@]} -eq 0 ]; then + echo "Removing labels: $(join_by , "${remove_labels[@]}")" + gh pr edit "${PR_URL}" --remove-label "$(join_by , "${remove_labels[@]}")" + elif [ ${#remove_labels[@]} -eq 0 ]; then + echo "Adding labels: $(join_by , "${add_labels[@]}")" + gh pr edit "${PR_URL}" --add-label "$(join_by , "${add_labels[@]}")" + else + echo "Adding labels: $(join_by , "${add_labels[@]}")" + echo "Removing labels: $(join_by , "${remove_labels[@]}")" + gh pr edit "${PR_URL}" --add-label "$(join_by , "${add_labels[@]}")" --remove-label "$(join_by , "${remove_labels[@]}")" + fi + env: + PR_TITLE: ${{github.event.pull_request.title}} + PR_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }} + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5030c12..fe68a58 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,14 +19,14 @@ request. We then kindly ask that: - all code changes must be tested manually and automated tests should be included when possible. - all necessary documentation should be included as well. -- commit messages must follow the [conventional commits specification](https://www.conventionalcommits.org). +- the first commit's message on a pull request must follow the + [conventional commits specification](https://www.conventionalcommits.org). See commit history for examples. -- commits on a single pull request must be squashed together to keep - make reviews easier. -- commits must be signed (this is supported by most Git clients as +- all commits must be signed (this is supported by most Git clients as well as the GitHub web UI, see link below). ## Resources - [Managing commit signature verification](https://docs.github.com/en/authentication/managing-commit-signature-verification) - [Using Pull Requests](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) +- [Commit Message Format](https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md) From df528deb93024f350907358223c0a803f859f934 Mon Sep 17 00:00:00 2001 From: Thomas Leplus Date: Wed, 6 Aug 2025 21:04:27 +0200 Subject: [PATCH 2/2] Update check-pr.yml --- .github/workflows/check-pr.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml index eec907d..a2ec47e 100644 --- a/.github/workflows/check-pr.yml +++ b/.github/workflows/check-pr.yml @@ -3,8 +3,6 @@ name: "Check PR" on: pull_request permissions: - actions: write - contents: write pull-requests: write jobs: @@ -39,12 +37,6 @@ jobs: PR_TITLE: ${{github.event.pull_request.title}} PR_COMMITS_URL: ${{github.event.pull_request.commits_url}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - - name: Authenticate CLI with a PAT - env: - RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} - if: env.RELEASE_TOKEN != '' - shell: bash - run: echo "${RELEASE_TOKEN}" | gh auth login --with-token - name: Update PR labels shell: bash run: |