From f8a5edfe9fe917367841d55bffa41cef7962b0b3 Mon Sep 17 00:00:00 2001 From: Agustin Borgna Date: Mon, 6 May 2024 14:26:30 +0100 Subject: [PATCH] ci: Add a CI check for conventional commit PR titles --- .github/workflows/pr-title.yml | 160 +++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 .github/workflows/pr-title.yml diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000..3cd9a8b --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,160 @@ +name: Check Conventional Commits format + +on: + pull_request_target: + branches: + - main + types: + - opened + - edited + - synchronize + - labeled + - unlabeled + merge_group: + types: [checks_requested] + +permissions: + pull-requests: write + +jobs: + main: + name: Validate Conventional Commit PR title + runs-on: ubuntu-latest + # The action does not support running on merge_group events, + # but if the check succeeds in the PR there is no need to check it again. + if: github.event_name == 'pull_request_target' + outputs: + # Whether the PR title indicates a breaking change. + breaking: ${{ steps.breaking.outputs.breaking }} + # Whether the PR body contains a "BREAKING CHANGE:" footer describing the breaking change. + has_breaking_footer: ${{ steps.breaking.outputs.has_breaking_footer }} + steps: + - name: Validate the PR title format + uses: amannn/action-semantic-pull-request@v5 + id: lint_pr_title + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + # Configure which types are allowed (newline-delimited). + # Default: https://github.com/commitizen/conventional-commit-types + types: | + feat + fix + docs + style + refactor + perf + test + ci + chore + revert + # Configure which scopes are allowed (newline-delimited). + # These are regex patterns auto-wrapped in `^ $`. + #scopes: | + # .* + # Configure that a scope must always be provided. + requireScope: false + # Configure which scopes are disallowed in PR titles (newline-delimited). + # For instance by setting the value below, `chore(release): ...` (lowercase) + # and `ci(e2e,release): ...` (unknown scope) will be rejected. + # These are regex patterns auto-wrapped in `^ $`. + #disallowScopes: | + # release + # [A-Z]+ + # Configure additional validation for the subject based on a regex. + # This example ensures the subject doesn't start with an uppercase character. + #subjectPattern: ^(?![A-Z]).+$ + # If `subjectPattern` is configured, you can use this property to override + # the default error message that is shown when the pattern doesn't match. + # The variables `subject` and `title` can be used within the message. + #subjectPatternError: | + # The subject "{subject}" found in the pull request title "{title}" + # didn't match the configured pattern. Please ensure that the subject + # doesn't start with an uppercase character. + # If the PR contains one of these newline-delimited labels, the + # validation is skipped. If you want to rerun the validation when + # labels change, you might want to use the `labeled` and `unlabeled` + # event triggers in your workflow. + ignoreLabels: | + ignore-semantic-pull-request + + # `action-semantic-pull-request` does not parse the title, so it cannot + # detect if it is marked as a breaking change. + # + # Since at this point we know the PR title is a valid conventional commit, + # we can use a simple regex that looks for a '!:' sequence. It could be + # more complex, but we don't care about false positives. + - name: Check for breaking change flag + id: breaking + run: | + if [[ "${PR_TITLE}" =~ ^.*\!:.*$ ]]; then + echo "breaking=true" >> $GITHUB_OUTPUT + else + echo "breaking=false" >> $GITHUB_OUTPUT + fi + + # Check if the PR comment has a "BREAKING CHANGE:" footer describing + # the breaking change. + if [[ "${PR_BODY}" != *"BREAKING CHANGE:"* ]]; then + echo "has_breaking_footer=false" >> $GITHUB_OUTPUT + else + echo "has_breaking_footer=true" >> $GITHUB_OUTPUT + fi + env: + PR_TITLE: ${{ github.event.pull_request.title }} + PR_BODY: ${{ github.event.pull_request.body }} + + # Post a help comment if the PR title indicates a breaking change but does + # not contain a "BREAKING CHANGE:" footer. + - name: Require "BREAKING CHANGE:" footer for breaking changes + id: breaking-comment + if: ${{ steps.breaking.outputs.breaking == 'true' && steps.breaking.outputs.has_breaking_footer == 'false' }} + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: pr-title-lint-error + message: | + Hey there and thank you for opening this pull request! 👋🏼 + + It looks like your proposed title indicates a breaking change. If that's the case, + please make sure to include a "BREAKING CHANGE:" footer in the body of the pull request + describing the breaking change and any migration instructions. + - name: Fail if the footer is required but missing + if: ${{ steps.breaking.outputs.breaking == 'true' && steps.breaking.outputs.has_breaking_footer == 'false' }} + run: exit 1 + + - name: Post a comment if the PR badly formatted + 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: | + Hey there and thank you for opening this pull request! 👋🏼 + + We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) + and it looks like your proposed title needs to be adjusted. + + Your title should look like this. The scope field is optional. + ``` + (): + ``` + + If the PR includes a breaking change, mark it with an exclamation mark: + ``` + !: + ``` + and include a "BREAKING CHANGE:" footer in the body of the pull request. + + Details: + ``` + ${{ steps.lint_pr_title.outputs.error_message }} + ``` + + # Delete previous comments when the issues have been resolved + # This step doesn't run if any of the previous checks fails. + - name: Delete previous comments + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: pr-title-lint-error + delete: true