Commit fbad66da authored by Robert Scott's avatar Robert Scott
Browse files

add "Check cherry-picks" github action

the intention being to catch commits which declare themselves as
cherry-picks, but either:

 - don't refer to a commit in the master or staging branches
 - are significantly altered from their original commit

determining the latter is not an exact science, but the heuristic of
looking for differences in only the added or removed lines seems to
work quite well. still, this should be considered an assistant
for reviewers rather than a hard failure. unfortunately github
workflows don't have a way of raising a gentle warning instead of a
failure.

the formatting of the output also leaves something to be desired due
to the limitations of github actions' "group" commands.
parent 354e1366
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
name: "Check cherry-picks"
on:
  pull_request_target:
    branches:
     - 'release-*'
     - 'staging-*'

permissions: {}

jobs:
  check:
    runs-on: ubuntu-latest
    if: github.repository_owner == 'NixOS'
    steps:
    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
      with:
        fetch-depth: 0
        filter: blob:none
    - name: Check cherry-picks
      env:
        BASE_SHA: ${{ github.event.pull_request.base.sha }}
        HEAD_SHA: ${{ github.event.pull_request.head.sha }}
      run: |
        ./maintainers/scripts/check-cherry-picks.sh "$BASE_SHA" "$HEAD_SHA"
+92 −0
Original line number Diff line number Diff line
#!/usr/bin/env bash
# Find alleged cherry-picks

set -e

if [ $# != "2" ] ; then
  echo "usage: check-cherry-picks.sh base_rev head_rev"
  exit 2
fi

PICKABLE_BRANCHES=${PICKABLE_BRANCHES:-master staging release-??.?? staging-??.??}
problem=0

while read new_commit_sha ; do
  if [ "$GITHUB_ACTIONS" = 'true' ] ; then
    echo "::group::Commit $new_commit_sha"
  else
    echo "================================================="
  fi
  git rev-list --max-count=1 --format=medium "$new_commit_sha"
  echo "-------------------------------------------------"

  original_commit_sha=$(
    git rev-list --max-count=1 --format=format:%B "$new_commit_sha" \
    | grep -Ei -m1 "cherry.*[0-9a-f]{40}" \
    | grep -Eoi -m1 '[0-9a-f]{40}'
  )
  if [ "$?" != "0" ] ; then
    echo "  ? Couldn't locate original commit hash in message"
    [ "$GITHUB_ACTIONS" = 'true' ] && echo ::endgroup::
    continue
  fi

  set -f # prevent pathname expansion of patterns
  for branch_pattern in $PICKABLE_BRANCHES ; do
    set +f # re-enable pathname expansion

    while read -r picked_branch ; do
      if git merge-base --is-ancestor "$original_commit_sha" "$picked_branch" ; then
        echo "  ✔ $original_commit_sha present in branch $picked_branch"

        range_diff_common='git range-diff
          --no-notes
          --creation-factor=100
          '"$original_commit_sha~..$original_commit_sha"'
          '"$new_commit_sha~..$new_commit_sha"'
        '

        if $range_diff_common --no-color | grep -E '^ {4}[+-]{2}' > /dev/null ; then
          if [ "$GITHUB_ACTIONS" = 'true' ] ; then
            echo ::endgroup::
            echo -n "::warning ::"
          else
            echo -n "  ⚠ "
          fi
          echo "Difference between $new_commit_sha and original $original_commit_sha may warrant inspection:"

          $range_diff_common --color

          problem=1
        else
          echo "  ✔ $original_commit_sha highly similar to $new_commit_sha"
          $range_diff_common --color
          [ "$GITHUB_ACTIONS" = 'true' ] && echo ::endgroup::
        fi

        # move on to next commit
        continue 3
      fi
    done <<< "$(
      git for-each-ref \
      --format="%(refname)" \
      "refs/remotes/origin/$branch_pattern"
    )"
  done

  if [ "$GITHUB_ACTIONS" = 'true' ] ; then
    echo ::endgroup::
    echo -n "::error ::"
  else
    echo -n "  ✘ "
  fi
  echo "$original_commit_sha not found in any pickable branch"

  problem=1
done <<< "$(
  git rev-list \
    -E -i --grep="cherry.*[0-9a-f]{40}" --reverse \
    "$1..$2"
)"

exit $problem