|
| 1 | +#!/bin/bash |
| 2 | +# Ensure commits in beta are in master & commits in stable are in beta + master. |
| 3 | +set -euo pipefail |
| 4 | +IFS=$'\n\t' |
| 5 | + |
| 6 | +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" |
| 7 | + |
| 8 | +# We don't care about commits that predate this automation check, so we pass a |
| 9 | +# `<limit>` argument to `git cherry`. |
| 10 | +BETA_LIMIT="53fd98ca776cb875bc9e5514f56b52eb74f9e7a9" |
| 11 | +STABLE_LIMIT="a178d0322ce20e33eac124758e837cbd80a6f633" |
| 12 | + |
| 13 | +verify_backported_commits_main() { |
| 14 | + ci_base_branch=$(ciBaseBranch) |
| 15 | + |
| 16 | + if [[ "$ci_base_branch" != "beta" && "$ci_base_branch" != "stable" ]]; then |
| 17 | + echo 'Skipping. This is only run when merging to the beta or stable branches.' |
| 18 | + exit 0 |
| 19 | + fi |
| 20 | + |
| 21 | + echo 'git: unshallowing the repository so we can check commits' |
| 22 | + git fetch \ |
| 23 | + --no-tags \ |
| 24 | + --no-recurse-submodules \ |
| 25 | + --progress \ |
| 26 | + --prune \ |
| 27 | + --unshallow |
| 28 | + |
| 29 | + if [[ $ci_base_branch == "beta" ]]; then |
| 30 | + verify_cherries master "$BETA_LIMIT" \ |
| 31 | + || exit 1 |
| 32 | + |
| 33 | + elif [[ $ci_base_branch == "stable" ]]; then |
| 34 | + (verify_cherries master "$STABLE_LIMIT" \ |
| 35 | + & verify_cherries beta "$STABLE_LIMIT") \ |
| 36 | + || exit 1 |
| 37 | + |
| 38 | + fi |
| 39 | +} |
| 40 | + |
| 41 | +# Verify all commits in `HEAD` are backports of a commit in <upstream>. See |
| 42 | +# https://git-scm.com/docs/git-cherry for an explanation of the arguments. |
| 43 | +# |
| 44 | +# $1 = <upstream> |
| 45 | +# $2 = <limit> |
| 46 | +verify_cherries() { |
| 47 | + # commits that lack a `backport-of` comment. |
| 48 | + local no_backports=() |
| 49 | + # commits with an incorrect `backport-of` comment. |
| 50 | + local bad_backports=() |
| 51 | + |
| 52 | + commits=$(git cherry "origin/$1" HEAD "$2") |
| 53 | + |
| 54 | + if [[ -z "$commits" ]]; then |
| 55 | + echo "All commits in \`HEAD\` are present in \`$1\`" |
| 56 | + return 0 |
| 57 | + fi |
| 58 | + |
| 59 | + commits=$(echo "$commits" | grep '^\+' | cut -c 3-) |
| 60 | + |
| 61 | + while read sha; do |
| 62 | + # Check each commit in <current>..<upstream> |
| 63 | + backport_sha=$(get_backport "$sha") |
| 64 | + |
| 65 | + if [[ "$backport_sha" == "nothing" ]]; then |
| 66 | + echo "✓ \`$sha\` backports nothing" |
| 67 | + continue |
| 68 | + fi |
| 69 | + |
| 70 | + if [[ -z "$backport_sha" ]]; then |
| 71 | + no_backports+=("$sha") |
| 72 | + continue |
| 73 | + fi |
| 74 | + |
| 75 | + if ! is_in_master "$backport_sha"; then |
| 76 | + bad_backports+=("$sha") |
| 77 | + continue |
| 78 | + fi |
| 79 | + |
| 80 | + echo "✓ \`$sha\` backports \`$backport_sha\`" |
| 81 | + done <<< "$commits" |
| 82 | + |
| 83 | + failure=0 |
| 84 | + |
| 85 | + if [ ${#no_backports[@]} -ne 0 ]; then |
| 86 | + echo 'Error: Could not find backports for all commits.' |
| 87 | + echo |
| 88 | + echo 'All commits in \`HEAD\` are required to have a corresponding upstream commit.' |
| 89 | + echo 'It looks like the following commits:' |
| 90 | + echo |
| 91 | + for commit in "${no_backports[@]}"; do |
| 92 | + echo " $commit" |
| 93 | + done |
| 94 | + echo |
| 95 | + echo "do not match any commits in \`$1\`. If this was intended, add the text" |
| 96 | + echo '\`backport-of: <SHA of a commit already in master>\`' |
| 97 | + echo 'somewhere in the message of each of these commits.' |
| 98 | + echo |
| 99 | + failure=1 |
| 100 | + fi |
| 101 | + |
| 102 | + if [ ${#bad_backports[@]} -ne 0 ]; then |
| 103 | + echo 'Error: Found incorrectly marked commits.' |
| 104 | + echo |
| 105 | + echo 'The following commits:' |
| 106 | + echo |
| 107 | + for commit in "${bad_backports[@]}"; do |
| 108 | + echo " $commit" |
| 109 | + done |
| 110 | + echo |
| 111 | + echo 'have commit messages marked \`backport-of: <SHA>\`, but the SHA is not in' |
| 112 | + echo '\`master\`.' |
| 113 | + echo |
| 114 | + failure=1 |
| 115 | + fi |
| 116 | + |
| 117 | + return $failure |
| 118 | +} |
| 119 | + |
| 120 | +# Get the backport of a commit. It echoes one of: |
| 121 | +# |
| 122 | +# 1. A SHA of the backported commit |
| 123 | +# 2. The string "nothing" |
| 124 | +# 3. An empty string |
| 125 | +# |
| 126 | +# $1 = <sha> |
| 127 | +get_backport() { |
| 128 | + # This regex is: |
| 129 | + # |
| 130 | + # ^.* - throw away any extra starting characters |
| 131 | + # backport-of: - prefix |
| 132 | + # \s\? - optional space |
| 133 | + # \(\) - capture group |
| 134 | + # [a-f0-9]\+\|nothing - a SHA or the text 'nothing' |
| 135 | + # .* - throw away any extra ending characters |
| 136 | + # \1 - replace it with the first match |
| 137 | + # {s//\1/p;q} - print the first occurrence and quit |
| 138 | + # |
| 139 | + git show -s --format=%B "$1" \ |
| 140 | + | sed -n '/^.*backport-of:\s\?\([a-f0-9]\+\|nothing\).*/{s//\1/p;q}' |
| 141 | +} |
| 142 | + |
| 143 | +# Check if a commit is in master. |
| 144 | +# |
| 145 | +# $1 = <sha> |
| 146 | +is_in_master() { |
| 147 | + git merge-base --is-ancestor "$1" origin/master 2> /dev/null |
| 148 | +} |
| 149 | + |
| 150 | +verify_backported_commits_main |
0 commit comments