diff --git a/README.md b/README.md index 6485518..c875922 100644 --- a/README.md +++ b/README.md @@ -280,6 +280,7 @@ jobs: and `squash!` commits. - if `no_revert_sha1` is not empty, no validation is done on revert commits. +- if `no_merge` is not empty, merge commits are rejected (fail instead of skip). - `jira_in_header` jira reference can be put in the commit header. - `header_length` allow to override the max length of the header line. - `jira_types` takes a space separated list `"feat fix"` as a parameter to override the default types requiring a jira @@ -314,6 +315,7 @@ Then run `pre-commit install --hook-type commit-msg` to install the - if `allow-temp` is set, no validation is done on `fixup!` and `squash!` commits. - if `no-revert-sha1` is set, no validation is done on revert commits. +- if `--no-merge` is set, merge commits are rejected (fail instead of skip). - if `--jira-in-header` jira reference can be put in the commit header. - `--header-length` allow to override the max length of the header line. - `--body-length` allow to override the max length of body lines. diff --git a/action.yml b/action.yml index 984aef6..21c5bb7 100644 --- a/action.yml +++ b/action.yml @@ -28,6 +28,9 @@ inputs: jira_in_header: description: 'If not empty, allow for jira ref in header' required: false + no_merge: + description: 'If not empty, merge commits are rejected.' + required: false runs: using: "composite" steps: @@ -51,4 +54,5 @@ runs: GLOBAL_MAX_LENGTH: ${{ inputs.header_length }} GLOBAL_BODY_MAX_LENGTH: ${{ inputs.body_length }} GLOBAL_JIRA_IN_HEADER: ${{ inputs.jira_in_header }} + COMMIT_VALIDATOR_NO_MERGE: ${{ inputs.no_merge }} shell: bash diff --git a/check.bats b/check.bats index efc48ed..0637e16 100644 --- a/check.bats +++ b/check.bats @@ -32,3 +32,21 @@ teardown() { run env COMMIT_VALIDATOR_NO_JIRA=1 bash -c "cd '$REPO' && bash '$SCRIPT' '$BASE..$BASE'" [ "$status" -eq 0 ] } + +@test "check: skips merge commits by default" { + git -C "$REPO" checkout -q -b feature + git -C "$REPO" commit --allow-empty -q -m "feat(widget): add widget" + git -C "$REPO" checkout -q master 2>/dev/null || git -C "$REPO" checkout -q main 2>/dev/null || git -C "$REPO" checkout -q - + git -C "$REPO" merge --no-ff -q --no-edit feature + run env COMMIT_VALIDATOR_NO_JIRA=1 bash -c "cd '$REPO' && bash '$SCRIPT' '$BASE..HEAD'" + [ "$status" -eq 0 ] +} + +@test "check: --no-merge rejects merge commits in range" { + git -C "$REPO" checkout -q -b feature + git -C "$REPO" commit --allow-empty -q -m "feat(widget): add widget" + git -C "$REPO" checkout -q master 2>/dev/null || git -C "$REPO" checkout -q main 2>/dev/null || git -C "$REPO" checkout -q - + git -C "$REPO" merge --no-ff -q --no-edit feature + run env COMMIT_VALIDATOR_NO_JIRA=1 COMMIT_VALIDATOR_NO_MERGE=1 bash -c "cd '$REPO' && bash '$SCRIPT' '$BASE..HEAD'" + [ "$status" -ne 0 ] +} diff --git a/check.sh b/check.sh index 0c87e5b..2c3b91e 100755 --- a/check.sh +++ b/check.sh @@ -5,12 +5,22 @@ set -eu DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" source $DIR/validator.sh -git log --no-merges --pretty="%H" --no-decorate $1 | +NO_MERGES_FLAG="--no-merges" +if [[ -n "${COMMIT_VALIDATOR_NO_MERGE:-}" ]]; then + NO_MERGES_FLAG="" +fi + +git log $NO_MERGES_FLAG --pretty="%H" --no-decorate $1 | while IFS= read -r COMMIT do - MESSAGE=`git log -1 --pretty='%B' $COMMIT` - echo "checking commit ${COMMIT}..." - validate "$MESSAGE" + MESSAGE=`git log -1 --pretty='%B' $COMMIT` + echo "checking commit ${COMMIT}..." + FIRST_WORD=$(echo "${MESSAGE%% *}" | tr '[:upper:]' '[:lower:]') + if [[ "${FIRST_WORD}" == merge ]]; then + echo "error: merge commits are not allowed" + exit 1 + fi + validate "$MESSAGE" done echo "All commits successfully checked" diff --git a/check_message.bats b/check_message.bats index df892cf..416ef01 100644 --- a/check_message.bats +++ b/check_message.bats @@ -39,6 +39,24 @@ teardown() { [ "$status" -eq 0 ] } +@test "check_message: --no-merge rejects MERGE_MSG path" { + echo "Merge branch 'foo' into 'bar'" > "$TMPFILE" + run bash "$SCRIPT" --no-merge "/some/path/MERGE_MSG" + [ "$status" -ne 0 ] +} + +@test "check_message: --no-merge rejects message starting with 'Merge'" { + echo "Merge branch 'foo' into 'bar'" > "$TMPFILE" + run bash "$SCRIPT" --no-merge "$TMPFILE" + [ "$status" -ne 0 ] +} + +@test "check_message: --no-merge still accepts valid commit" { + echo "feat(widget): add a wonderful widget" > "$TMPFILE" + run bash "$SCRIPT" --no-merge --no-jira "$TMPFILE" + [ "$status" -eq 0 ] +} + @test "check_message: strips comment lines before validating" { printf "# This is a comment\nfeat(scope): valid subject\n" > "$TMPFILE" run bash "$SCRIPT" --no-jira "$TMPFILE" diff --git a/check_message.sh b/check_message.sh index a7a5887..ee4ec31 100755 --- a/check_message.sh +++ b/check_message.sh @@ -2,13 +2,14 @@ set -eu -unset COMMIT_VALIDATOR_ALLOW_TEMP COMMIT_VALIDATOR_NO_JIRA COMMIT_VALIDATOR_NO_REVERT_SHA1 GLOBAL_JIRA_IN_HEADER GLOBAL_MAX_LENGTH GLOBAL_BODY_MAX_LENGTH GLOBAL_JIRA_TYPES +unset COMMIT_VALIDATOR_ALLOW_TEMP COMMIT_VALIDATOR_NO_JIRA COMMIT_VALIDATOR_NO_REVERT_SHA1 COMMIT_VALIDATOR_NO_MERGE GLOBAL_JIRA_IN_HEADER GLOBAL_MAX_LENGTH GLOBAL_BODY_MAX_LENGTH GLOBAL_JIRA_TYPES while [[ $# -gt 0 ]]; do case "$1" in --no-jira ) COMMIT_VALIDATOR_NO_JIRA=1; shift ;; --allow-temp ) COMMIT_VALIDATOR_ALLOW_TEMP=1; shift ;; --no-revert-sha1 ) COMMIT_VALIDATOR_NO_REVERT_SHA1=1; shift ;; + --no-merge ) COMMIT_VALIDATOR_NO_MERGE=1; shift ;; --jira-in-header ) GLOBAL_JIRA_IN_HEADER=1; shift ;; --header-length=* ) GLOBAL_MAX_LENGTH="${1#*=}"; shift ;; --header-length ) GLOBAL_MAX_LENGTH="$2"; shift 2 ;; @@ -28,6 +29,10 @@ source "$DIR/validator.sh" if [[ "$1" == *MERGE_MSG ]] then + if [[ -n "${COMMIT_VALIDATOR_NO_MERGE:-}" ]]; then + echo "error: merge commits are not allowed" + exit 1 + fi # ignore merge message (merge with --no-ff without conflict) exit fi @@ -38,9 +43,12 @@ MESSAGE=$(sed '/^#/d' "$1") FIRST_WORD=$(echo "${MESSAGE%% *}" | tr '[:upper:]' '[:lower:]') if [[ "${FIRST_WORD}" == merge ]] then - # ignore merge commits (merge after conflict resolution) + if [[ -n "${COMMIT_VALIDATOR_NO_MERGE:-}" ]]; then + echo "error: merge commits are not allowed" + exit 1 + fi + # ignore merge commits (merge after conflict resolution) exit - fi # print message so you don't lose it in case of errors