From 747684313feff160d8fec6ecfa738bf9e5c8a245 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Thu, 5 Dec 2024 09:29:05 -0400 Subject: [PATCH] chore: validate PR titles and commits, and autoassign PRs --- .github/scripts/colors.sh | 12 +++++ .github/scripts/commit_check.sh | 4 ++ .github/scripts/parse_commits.sh | 30 +++++++++++ .github/workflows/auto_assign_pr.yml | 12 +++++ .github/workflows/commit_lint.yml | 77 ++++++++++++++++++++++++++++ .github/workflows/pr_lint.yml | 35 +++++++++++++ test | 1 + 7 files changed, 171 insertions(+) create mode 100644 .github/scripts/colors.sh create mode 100755 .github/scripts/commit_check.sh create mode 100644 .github/scripts/parse_commits.sh create mode 100644 .github/workflows/auto_assign_pr.yml create mode 100644 .github/workflows/commit_lint.yml create mode 100644 .github/workflows/pr_lint.yml create mode 100644 test diff --git a/.github/scripts/colors.sh b/.github/scripts/colors.sh new file mode 100644 index 0000000000..6b19a153f6 --- /dev/null +++ b/.github/scripts/colors.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# Colors +export YLW='\033[1;33m' +export RED='\033[0;31m' +export GRN='\033[0;32m' +export BLU='\033[0;34m' +export BLD='\033[1m' +export RST='\033[0m' + +# Clear line +export CLR='\033[2K' diff --git a/.github/scripts/commit_check.sh b/.github/scripts/commit_check.sh new file mode 100755 index 0000000000..1cdb57d32d --- /dev/null +++ b/.github/scripts/commit_check.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +source .github/scripts/parse_commits.sh +parse_commits "$@" diff --git a/.github/scripts/parse_commits.sh b/.github/scripts/parse_commits.sh new file mode 100644 index 0000000000..bd0fdc1dd0 --- /dev/null +++ b/.github/scripts/parse_commits.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# The output of this script is as follows: +# 1. One line "checking commits between: " +# 2. One line for each commit message that is not well-formed + +set -euo pipefail + +source .github/scripts/colors.sh + +parse_commits() { + + BASE_BRANCH=${BASE_BRANCH:-master} + + start_commit=${1:-origin/${BASE_BRANCH}} + end_commit=${2:-HEAD} + exit_code=0 + + echo -e "${GRN}Checking commits between:${RST} $start_commit $end_commit" + # Run the loop in the current shell using process substitution + while IFS= read -r message || [ -n "$message" ]; do + # Check if commit message follows conventional commits format + if [[ ! $message =~ ^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(.*\))?:.*$ ]]; then + echo -e "${YLW}Commit message is ill-formed:${RST} $message" + exit_code=1 + fi + done < <(git log --format=%s "$start_commit".."$end_commit") + + exit ${exit_code} +} \ No newline at end of file diff --git a/.github/workflows/auto_assign_pr.yml b/.github/workflows/auto_assign_pr.yml new file mode 100644 index 0000000000..2f75055858 --- /dev/null +++ b/.github/workflows/auto_assign_pr.yml @@ -0,0 +1,12 @@ +name: Auto Assign PR to Creator + +on: + pull_request: + types: + - opened + +jobs: + assign_creator: + runs-on: ubuntu-latest + steps: + - uses: toshimaru/auto-author-assign@v1.6.2 diff --git a/.github/workflows/commit_lint.yml b/.github/workflows/commit_lint.yml new file mode 100644 index 0000000000..323edfbe9e --- /dev/null +++ b/.github/workflows/commit_lint.yml @@ -0,0 +1,77 @@ +name: "Conventional Commits" + +on: + pull_request: + types: + - opened + - edited + - reopened + - synchronize +jobs: + main: + name: Validate commit messages + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + + - name: Check commit message + id: check_commit_message + if: always() + run: | + set +e + + base_sha=${{ github.event.pull_request.base.sha }} + head_sha=${{ github.event.pull_request.head.sha }} + + output=$(.github/scripts/commit_check.sh "${base_sha}" "${head_sha}" 2>&1) + exit_code=$? + + echo "${output}" | sed '$d' + echo "exit_code=${exit_code}" >> $GITHUB_OUTPUT + + invalid_commit_messages=$(echo "${output}" | sed '1d;$d') + invalid_commit_messages=$(echo "${output}" | sed '1d;$d') + invalid_commit_messages=$(echo "${invalid_commit_messages}" | sed 's/\x1b\[[0-9;]*m//g') # Remove color codes + invalid_commit_messages=$(echo "${invalid_commit_messages}" | sed 's/^Commit message is ill-formed: //') # Remove prefix + + if [[ $exit_code -ne 0 ]]; then + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + echo "error_message<<$EOF" >> "$GITHUB_ENV" + echo "${invalid_commit_messages}" >> "$GITHUB_ENV" + echo "$EOF" >> "$GITHUB_ENV" + fi + + - name: "Publish failed commit messages" + uses: marocchino/sticky-pull-request-comment@v2 + # When the previous steps fails, the workflow would stop. By adding this + # condition you can continue the execution with the populated error message. + if: always() && (steps.check_commit_message.outputs.exit_code != 0) + with: + header: commit-message-lint-error + message: | + Commits must follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) + Please fix these commit messages: + ``` + ${{ env.error_message }} + ``` + + # Delete a previous comment when the issue has been resolved + - name: "Delete previous comment" + if: ${{ steps.check_commit_message.outputs.exit_code == 0 }} + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: commit-message-lint-error + delete: true + + - name: "Mark as failed" + if: steps.check_commit_message.outputs.exit_code != 0 + uses: actions/github-script@v7 + with: + script: | + core.setFailed("Some commit messages are ill-formed") diff --git a/.github/workflows/pr_lint.yml b/.github/workflows/pr_lint.yml new file mode 100644 index 0000000000..24bda9d2fa --- /dev/null +++ b/.github/workflows/pr_lint.yml @@ -0,0 +1,35 @@ +name: "Conventional Commits" + +on: + pull_request: + types: + - opened + - edited + - reopened + - synchronize +jobs: + main: + name: Validate PR title + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: amannn/action-semantic-pull-request@v5 + id: lint_pr_title + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: marocchino/sticky-pull-request-comment@v2 + # When the previous steps fails, the workflow would stop. By adding this + # condition you can continue the execution with the populated error message. + if: always() && (steps.lint_pr_title.outputs.error_message != null) + with: + header: pr-title-lint-error + message: | + Pull requests titles must follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) + + # Delete a previous comment when the issue has been resolved + - if: ${{ steps.lint_pr_title.outputs.error_message == null }} + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: pr-title-lint-error + delete: true diff --git a/test b/test new file mode 100644 index 0000000000..30d74d2584 --- /dev/null +++ b/test @@ -0,0 +1 @@ +test \ No newline at end of file