mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-09 19:13:26 +03:00

GitHub comments have a length limit, so we can't just dump everything. The 10k limit is arbitrary, but the assumption is that reviewing the range-diff is not the sensible thing to do once it becomes a certain size - reviewing the regular diff and treating the commit as "new" is easier to do in that case. Thus, truncating should work out fine, especially when the full range-diff is still available in the runner log. This could still end up in with an error, if a PR has multiple commits, which all hit the limit. Let's get there first, before we try to fix that hypothetical case, too.
150 lines
5.4 KiB
Bash
Executable file
150 lines
5.4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Find alleged cherry-picks
|
|
|
|
set -euo pipefail
|
|
|
|
if [[ $# != "2" && $# != "3" ]] ; then
|
|
echo "usage: check-cherry-picks.sh base_rev head_rev [markdown_file]"
|
|
exit 2
|
|
fi
|
|
|
|
markdown_file="$(realpath ${3:-/dev/null})"
|
|
[ -v 3 ] && rm -f "$markdown_file"
|
|
|
|
# Make sure we are inside the nixpkgs repo, even when called from outside
|
|
cd "$(dirname "${BASH_SOURCE[0]}")"
|
|
|
|
PICKABLE_BRANCHES="master staging release-??.?? staging-??.?? haskell-updates python-updates staging-next staging-next-??.??"
|
|
problem=0
|
|
|
|
# Not everyone calls their remote "origin"
|
|
remote="$(git remote -v | grep -i 'NixOS/nixpkgs' | head -n1 | cut -f1 || true)"
|
|
|
|
commits="$(git rev-list --reverse "$1..$2")"
|
|
|
|
log() {
|
|
type="$1"
|
|
shift 1
|
|
|
|
local -A prefix
|
|
prefix[success]=" ✔ "
|
|
if [ -v GITHUB_ACTIONS ]; then
|
|
prefix[warning]="::warning::"
|
|
prefix[error]="::error::"
|
|
else
|
|
prefix[warning]=" ⚠ "
|
|
prefix[error]=" ✘ "
|
|
fi
|
|
|
|
echo "${prefix[$type]}$@"
|
|
|
|
# Only logging errors and warnings, which allows comparing the markdown file
|
|
# between pushes to the PR. Even if a new, proper cherry-pick, commit is added
|
|
# it won't change the markdown file's content and thus not trigger another comment.
|
|
if [ "$type" != "success" ]; then
|
|
local -A alert
|
|
alert[warning]="WARNING"
|
|
alert[error]="CAUTION"
|
|
echo >> $markdown_file
|
|
echo "> [!${alert[$type]}]" >> $markdown_file
|
|
echo "> $@" >> $markdown_file
|
|
fi
|
|
}
|
|
|
|
endgroup() {
|
|
if [ -v GITHUB_ACTIONS ] ; then
|
|
echo ::endgroup::
|
|
fi
|
|
}
|
|
|
|
while read -r new_commit_sha ; do
|
|
if [ -v GITHUB_ACTIONS ] ; 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}' || true
|
|
)
|
|
if [ -z "$original_commit_sha" ] ; then
|
|
endgroup
|
|
log warning "Couldn't locate original commit hash in message of $new_commit_sha."
|
|
problem=1
|
|
continue
|
|
fi
|
|
|
|
set -f # prevent pathname expansion of patterns
|
|
for pattern in $PICKABLE_BRANCHES ; do
|
|
set +f # re-enable pathname expansion
|
|
|
|
# Reverse sorting by refname and taking one match only means we can only backport
|
|
# from unstable and the latest stable. That makes sense, because even right after
|
|
# branch-off, when we have two supported stable branches, we only ever want to cherry-pick
|
|
# **to** the older one, but never **from** it.
|
|
# This makes the job significantly faster in the case when commits can't be found,
|
|
# because it doesn't need to iterate through 20+ branches, which all need to be fetched.
|
|
branches="$(git for-each-ref --sort=-refname --format="%(refname)" \
|
|
"refs/remotes/${remote:-origin}/$pattern" | head -n1)"
|
|
|
|
while read -r picked_branch ; do
|
|
if git merge-base --is-ancestor "$original_commit_sha" "$picked_branch" ; then
|
|
range_diff_common='git --no-pager 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 2> /dev/null | grep -E '^ {4}[+-]{2}' > /dev/null ; then
|
|
log success "$original_commit_sha present in branch $picked_branch"
|
|
endgroup
|
|
log warning "Difference between $new_commit_sha and original $original_commit_sha may warrant inspection."
|
|
|
|
# First line contains commit SHAs, which we already printed.
|
|
$range_diff_common --color | tail -n +2
|
|
|
|
echo -e "> <details><summary>Show diff</summary>\n>" >> $markdown_file
|
|
echo '> ```diff' >> $markdown_file
|
|
# The output of `git range-diff` is indented with 4 spaces, which we need to match with the
|
|
# code blocks indent to get proper syntax highlighting on GitHub.
|
|
diff="$($range_diff_common | tail -n +2 | sed -Ee 's/^ {4}/> /g')"
|
|
# Also limit the output to 10k bytes (and remove the last, potentially incomplete line), because
|
|
# GitHub comments are limited in length. The value of 10k is arbitrary with the assumption, that
|
|
# after the range-diff becomes a certain size, a reviewer is better off reviewing the regular diff
|
|
# in GitHub's UI anyway, thus treating the commit as "new" and not cherry-picked.
|
|
# Note: This could still lead to a too lengthy comment with multiple commits touching the limit. We
|
|
# consider this too unlikely to happen, to deal with explicitly.
|
|
max_length=10000
|
|
if [ "${#diff}" -gt $max_length ]; then
|
|
printf -v diff "%s\n\n[...truncated...]" "$(echo "$diff" | head -c $max_length | head -n-1)"
|
|
fi
|
|
echo "$diff" >> $markdown_file
|
|
echo '> ```' >> $markdown_file
|
|
echo "> </details>" >> $markdown_file
|
|
|
|
problem=1
|
|
else
|
|
log success "$original_commit_sha present in branch $picked_branch"
|
|
log success "$original_commit_sha highly similar to $new_commit_sha"
|
|
$range_diff_common --color
|
|
endgroup
|
|
fi
|
|
|
|
# move on to next commit
|
|
continue 3
|
|
fi
|
|
done <<< "$branches"
|
|
done
|
|
|
|
endgroup
|
|
log error "$original_commit_sha given in $new_commit_sha not found in any pickable branch."
|
|
|
|
problem=1
|
|
done <<< "$commits"
|
|
|
|
exit $problem
|