github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/githooks/commit-msg (about)

     1  #!/usr/bin/env bash
     2  #
     3  # -u: we want the variables to be properly assigned.
     4  # -o pipefail: we want to test the result of pipes.
     5  # No -e because we have failing commands and that's OK.
     6  set -uo pipefail
     7  
     8  grep=${GREP:-grep}
     9  awk=${AWK:-awk}
    10  sed=${SED:-sed}
    11  
    12  # Support both terminfo and termcap.
    13  # We want to check whether stderr is a terminal (to enable
    14  # printing colors). We want this test to work even if stdin is
    15  # redirected (e.g. when run from git commit) or stdout is
    16  # redirected (e.g. in the var assignments below).
    17  red=$([ -t 2 ] && { tput setaf 1 || tput AF 1; } 2>/dev/null)
    18  reset=$([ -t 2 ] && { tput sgr0 || tput me; } 2>/dev/null)
    19  warn() {
    20      echo >&2
    21      echo "${red}$*${reset}" >&2
    22      echo >&2
    23  }
    24  
    25  if ! test -s "$1"; then
    26  	warn "commit message is empty!"
    27  	# nothing to check further.
    28  	exit 0
    29  fi
    30  
    31  # First lint test: give a friendly reminder on long lines.
    32  
    33  longlines=$($awk '/^# --* >8 --*/ {exit} /^[^#]/ { if (length() >= 80) print }' <"$1")
    34  if test -n "$longlines"; then
    35      warn "Long line(s) detected:"
    36      echo "$longlines" | $sed -e 's/^/   /g' >&2
    37      echo >&2
    38      echo "Please amend and wrap long lines." >&2
    39      echo "(Long lines are OK if inside a preformatted block or a table.)" >&2
    40      echo >&2
    41  fi
    42  
    43  # Lint the release notes.
    44  #
    45  # It's OK to have multiple entries per commit
    46  # (e.g. changes in different categories). But that means we need to
    47  # extract them separately for analysis.
    48  #
    49  saveIFS=$IFS
    50  IFS='
    51  '
    52  notes=($($grep -iE '^release note' "$1"))
    53  
    54  # Set this to 1 to require a release justification note.
    55  require_justification=0
    56  justification=($($grep -iE '^release justification: \S+' "$1"))
    57  
    58  IFS=$saveIFS
    59  
    60  informed=
    61  inform() {
    62      if [ -z "$informed" ]; then
    63          info=${rnote:0:40}
    64          if [ "$info" != "$rnote" ]; then info="$info..."; fi
    65          warn "Warning: malformed release note entry: $info"
    66      fi
    67      informed=1
    68  }
    69  hint() {
    70      inform
    71      echo "- $*" >&2
    72  }
    73  
    74  if [ "$require_justification" = 1 -a  0 = ${#justification[*]} ]; then
    75      warn "No release justification specified."
    76      echo "Try: 'Release justification: This commit is safe for this release because..." >&2
    77      echo >&2
    78  fi
    79  
    80  if [ 0 = ${#notes[*]} ]; then
    81      warn "No release note specified."
    82      echo "Try: 'Release note (...): ...'" >&2
    83      echo >&2
    84  else
    85      for rnote in "${notes[@]}"; do
    86          informed=
    87          if echo "$rnote" | $grep -qiE '^release note' && ! echo "$rnote" | $grep -qE '^Release note'; then
    88              hint "case matters! Try '${rnote:0:12}' -> 'Release note'"
    89          fi
    90          if echo "$rnote" | $grep -qiE '^release notes'; then
    91              hint "singular please. Use multiple entries if there are multiple notes. Try '${rnote:0:13}' -> 'Release note'"
    92          fi
    93          if echo "$rnote" | $grep -qiE '^release notes? *: *\('; then
    94              entered=$(echo "$rnote" | cut -d')' -f1)\); entered=${entered:0:40}
    95              cat=$(echo "$rnote" | cut -d'(' -f2 | cut -d')' -f1)
    96              hint "place the category before the colon. Try '$entered' -> 'Release note ($cat):'"
    97          fi
    98          if echo "$rnote" | $grep -qiE '^release notes? *: *[^ ]* *:'; then
    99              cat=$(echo "$rnote" | $sed -e 's/^[^:]*: *//g;s/ *:.*$//g')
   100              entered=$(echo "$rnote" | cut -d: -f1-2)
   101              hint "place category within parentheses. Try '$entered:' -> 'Release note ($cat):'"
   102          fi
   103          if echo "$rnote" | $grep -qiE '^release notes?[^:]*: *none' && ! echo "$rnote" | $grep -qE '^Release note: [nN]one'; then
   104              entered=$(echo "$rnote" | $sed -e 's/none.*/none/ig')
   105              hint "for no release notes use 'none' with no category. Try '$entered' -> 'Release note: none'"
   106          fi
   107          if ! echo "$rnote" | $grep -qiE '^release notes? *: *none' && echo "$rnote" | $grep -qiE '^release notes? *: *[^( ]'; then
   108              entered=$(echo "$rnote" | cut -d: -f2-); entered=${entered:0:40}
   109              hint "category missing (can only be omitted if note is 'none'). Try 'Release note (category):$entered'"
   110          fi
   111          if echo "$rnote" | $grep -qiE '^release notes?[^:]*:([^ ]|   *)' || \
   112                  echo "$rnote" | $grep -qiE '^release notes?(\(|  +\()' || \
   113                  echo "$rnote" | $grep -qiE '^release notes? *\( +' || \
   114                  echo "$rnote" | $grep -qiE '^release notes? *\( *[^)]* +\)' ; then
   115              entered=${rnote:0:40}
   116              body=$(echo "$rnote"| cut -d: -f2-|cut -c1-40|$sed -e 's/^ *//g')
   117              cat=$(echo "$rnote" | cut -d'(' -f2 |cut -d')' -f1|$sed -e 's/^ *//g;s/ *$//g')
   118              if test -z "$cat"; then cat=...; fi
   119              hint "some space problem. Try '$entered' -> 'Release note ($cat): $body'"
   120          fi
   121          # Now prune the category
   122          if echo "$rnote" | $grep -qiE '^release notes? *\([^)]*\)'; then
   123              cat=$(echo "$rnote" | cut -d'(' -f2|cut -d')' -f1|$sed -e 's/^ *//g;s/ *$//g')
   124              lower=$(echo "$cat"|tr A-Z a-z)
   125              if [ "$cat" != "$lower" ]; then
   126                  hint "categories in lowercase. Try 'Release note ($cat)' -> 'Release note ($lower)'"
   127              fi
   128              if echo "$rnote" | $grep -qiE '^release notes? *\([^)/]*/'; then
   129                  repl=$(echo "$lower"|$sed -e 's| */ *|, |g')
   130                  hint "use commas to separate categories. Try '($lower)' -> '($repl)'"
   131                  lower=$repl
   132              fi
   133              saveIFS=$IFS
   134              IFS='
   135  '
   136              cats=($(echo "$lower" | tr ',' '\n' | $sed -e 's/^ *//g'))
   137              IFS=$saveIFS
   138              for lcat in "${cats[@]}"; do
   139                  case $lcat in
   140                      "cli change") ;;
   141                      "sql change") ;;
   142                      "admin ui change") ;;
   143                      "general change") ;;
   144                      "build change") ;;
   145                      "enterprise change") ;;
   146                      "backward-incompatible change") ;;
   147                      "performance improvement") ;;
   148                      "bug fix") ;;
   149                      "security update") ;;
   150                      bugfix)
   151                          hint "Try 'Release note (bugfix)' -> 'Release note (bug fix)'" ;;
   152                      security*)
   153                          hint "Try 'Release note ($lcat)' -> 'Release note (security update)'" ;;
   154                      sql*)
   155                          hint "Try 'Release note ($lcat)' -> 'Release note (sql change)'" ;;
   156                      "backwards-incompatible"*|"backward incompatible"*)
   157                          hint "Try '$lcat' -> 'backward-incompatible change'" ;;
   158                      *) hint "unknown category '$lcat'" ;;
   159                  esac
   160              done
   161          fi
   162  	# Do some linting on the message itself.
   163  	msg=$(echo "$rnote" | cut -d':' -f1-)
   164  	if echo "$msg" | $grep -qiE ' *([cC]loses?|[fF]ix(es)?) *#[0-9]+\.? *'; then
   165  	    hint "don't just close or fix. Explain."
   166  	fi
   167      done
   168      if test -n "$informed"; then echo >&2; fi
   169  fi