Search code examples
bashgitcontinuous-integrationgithooks

How to automatically clean the latest git-commit message from special keywords like p.e. "[build]" that are meant to trigger a CI pipeline?


In some CI setups special keywords might be used to trigger specific behavior in the pipeline. For example the CI pipelines might build feature branches if and only if the latest git commit message in a branch-push contains keywords like [build].

Even though this works the commit-messages become polluted with these keywords. So I was wondering whether it's possible to make some sort of local git-commit / git-push hook to automatically cleanup the latest git-commit message if it contains the [build] keyword a few seconds after the original commit message makes it across (to trigger the CI pipeline).


Solution

  • Here is the solution I came up with. Seems to be working fine based on my tests. Place this script as '.git/hooks/pre-push' and make sure to 'chmod +x .git/hooks/pre-push' too.

    It detects whether the latest git-commit message contains the keyword [build] and if it does it pushes it across, waits for a few seconds (*), it amends the latest git-commit message to clean it up and it then carefully force-repushes the branch.

    Feature branches cooperate very well with this technique even when multiple devs are working on them - it's highly unlikely that within that sensitive period of 3secs someone else will tweak the branch.

    • in Azure pipelines the build triggers immediately so even 2secs are enough - but YMMV so feel free to change the amount of seconds you want to wait to ensure that the build triggers in the pipeline.

    Note that technique is meant to be applied on feature branches - not the cornerstone branches and especially not 'master', 'main' or even 'develop'.

    #!/bin/bash
    
    # https://stackoverflow.com/a/77462557/863651
    #
    # to make this pre-push-hook trigger across all of your repositories just name it 'pre-push', put it under your git-global-hooks directory and give it +x permissions
    #
    # tip: to setup your git-global-hooks directory simply run  'git config --global core.hooksPath /path/to/my/centralized/hooks'  (requires git 2.9+ released in June 2016 on your machine)
    
    set -e
    
    declare current_branch="$(git symbolic-ref HEAD)"
    if [[ "$current_branch" == "refs/head/main" || "$current_branch" == "refs/head/master" || "$current_branch" == "refs/head/develop" ]]; then
        exit 0 # better not fiddle with the commit history of these branches
    fi
    
    declare latest_commit_message="$(git log --format=%B -n 1 HEAD)"
    
    declare latest_commit_message_lowercased="$(echo "$latest_commit_message" | tr '[:upper:]' '[:lower:]')"
    # "${latest_commit_message,,}"   unfortunately we cant use this syntax to lowercase the string on macos because it runs on an old version of bash
    
    if [[ "${latest_commit_message_lowercased}" == *"[build]"* ]]; then
        # commit message contains [build] keyword - will push it and clean it up a few seconds later via ammend + force repush
        
        cat <<EOL >./.git/ammend_commit_a_few_seconds_later.sh
    #!/bin/bash
    
    set -e
    
    sleep 3
    
    declare  commit_message_to_clean_up=\$(git log --format=%B -n 1 HEAD) # get the latest commit message
    declare  commit_message_cleaned="\$(echo "\${commit_message_to_clean_up}" | sed -E 's/[ \\t]*\\[build\\][ \\t]*/ /ig' | sed -E '1s/^[ \\t]+//')" # we cant use \s in these regexes because if we do then in macos these regexes go haywire
    
    if [[ "\${commit_message_to_clean_up}" == "\${commit_message_cleaned}" ]]; then  # just to be on the safe side
        # echo "** commit-ammender: nothing to do"
        exit 0
    fi
    
    git commit --amend --message "\${commit_message_cleaned}" # amend the latest commit message to replace [build] with a single whitespace
    
    git push origin "\$(git rev-parse --abbrev-ref HEAD)" --force-with-lease # force-push the branch
    
    echo "Branch pushed and commit message amended."
    
    rm -f "./.git/ammend_commit_a_few_seconds_later.sh"   # in macos sometimes this helper file is not getting deleted   doesnt really matter "out of sight - out of mind"
    EOL
    
        chmod +x ./.git/ammend_commit_a_few_seconds_later.sh
    
        ./.git/ammend_commit_a_few_seconds_later.sh &
    fi
    
    exit 0