merge-into-chromeos: Create chain of merge commits

We can use the gerrit api to detect previous merge commits
the bot has made, then use those as the base for the next merge.

This will create smaller merges and is easier to manage as we do not
have to abandon so many changes.

One complication is detecting possible cherry picks the oncall may
have added to the previous merge commit.

Only open merge commits are taken into account.

BUG=b:213932296
TEST=./ci/kokoro/simulate build-merge-into-chromeos.sh

Change-Id: I795109be4c45bec49068c3e292a3947505755442
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3381832
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: Dennis Kempin <denniskempin@google.com>
This commit is contained in:
Dennis Kempin 2022-01-11 14:25:27 -08:00
parent a06f22f2ac
commit 5a250fcb26

View file

@ -5,12 +5,81 @@
set -e
cd "${KOKORO_ARTIFACTS_DIR}/git/crosvm"
ORIGIN=https://chromium.googlesource.com/chromiumos/platform/crosvm
RETRIES=3
readonly GERRIT_URL=https://chromium-review.googlesource.com
readonly ORIGIN=${GERRIT_URL}/chromiumos/platform/crosvm
readonly RETRIES=3
gerrit_api() {
# Call gerrit API. Strips XSSI protection line from output.
# See: https://gerrit-review.googlesource.com/Documentation/dev-rest-api.html
local url="${GERRIT_URL}/${1}"
curl --silent "$url" | tail -n +2
}
query_change() {
# Query gerrit for a specific change.
# See: https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#get-change
gerrit_api "changes/$1/?o=CURRENT_REVISION"
}
query_changes() {
# Query gerrit for a list of changes.
# See: https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#list-changes
local query=$(printf '%s+' "$@")
gerrit_api "changes/?q=${query}"
}
query_related_changes() {
# Query related changes from gerrit.
# See: https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#get-related-changes
gerrit_api "changes/$1/revisions/current/related"
}
get_previous_merge_id() {
# Query all open merge commits previously made by crosvm-bot. May be null if
# none are open.
query=(
project:chromiumos/platform/crosvm
branch:chromeos
status:open
owner:crosvm-bot@crosvm-packages.iam.gserviceaccount.com
)
# Pick the one that was created last.
query_changes "${query[@]}" |
jq --raw-output 'sort_by(.created)[-1].change_id'
}
get_last_change_in_chain() {
# Use the related changes API to find the last change in the chain of
# commits.
local change_id=$1
# The list of commits is sorted by the git commit order, with the latest
# change first and includes the current change.
local last_change
last_change=$(query_related_changes "$change_id" | jq --raw-output \
"[.changes[] | select(.status == \"NEW\")][0].change_id")
# If there are no related changes the list will be empty.
if [ "$last_change" == "null" ]; then
echo "${change_id}"
else
echo "${last_change}"
fi
}
fetch_change() {
# Fetch the provided change and print the commit sha.
local change_id=$1
# Find the git ref we need to fetch.
local change_ref
change_ref=$(query_change "$change_id" |
jq --raw-output -e ".revisions[.current_revision].ref")
git fetch -q origin "${change_ref}"
}
gerrit_prerequisites() {
set -e
# Authenticate to GoB if we don't already have a cookie.
# This should only happen when running in Kokoro, not locally.
# See: go/gob-gce
@ -18,10 +87,11 @@ gerrit_prerequisites() {
git clone https://gerrit.googlesource.com/gcompute-tools \
"${KOKORO_ARTIFACTS_DIR}/gcompute-tools"
"${KOKORO_ARTIFACTS_DIR}/gcompute-tools/git-cookie-authdaemon" --no-fork
fi
git config user.name "Crosvm Bot"
git config user.email crosvm-bot@crosvm-packages.iam.gserviceaccount.com
# Setup correct user info for the service account.
git config user.name "Crosvm Bot"
git config user.email crosvm-bot@crosvm-packages.iam.gserviceaccount.com
fi
# We cannot use the original origin that kokoro used, as we no longer have
# access the GoB host via rpc://.
@ -53,7 +123,6 @@ upload_with_retries() {
}
main() {
set -e
gerrit_prerequisites
# Make a copy of the merge script, so we are using the HEAD version to
@ -64,14 +133,38 @@ main() {
git clean -f -d -x
git checkout -f
# Remember the HEAD that Kokoro checked out. This is usually origin/main
# but can also be manually specified when triggering the job.
local target=$(git rev-parse HEAD)
# Parent commit to use for this merge.
local parent_commit="origin/chromeos"
# Perform merge on a tracking branch.
git checkout -b chromeos origin/chromeos
# Query gerrit to find the latest merge commit and fetch it to be used as
# a parent.
local previous_merge="$(get_previous_merge_id)"
if [ "$previous_merge" != "null" ]; then
# The oncall may have uploaded a custom merge or cherry-pick on top
# of the detected merge. Find the last changed in that chain.
local last_change_in_chain=$(get_last_change_in_chain "${previous_merge}")
echo "Found previous merge: ${GERRIT_URL}/q/${previous_merge}"
echo "Last change in that chain: ${GERRIT_URL}/q/${last_change_in_chain}"
fetch_change "${last_change_in_chain}"
parent_commit="FETCH_HEAD"
fi
local merge_list=$(git log --oneline --decorate=no --no-color \
"${parent_commit}..origin/main")
if [ -z "$merge_list" ]; then
echo "Already up to date, nothing to merge."
return
else
echo "Merge list:"
echo "${merge_list}"
echo ""
fi
echo "Checking out parent: ${parent_commit}"
git checkout -b chromeos "${parent_commit}"
git branch --set-upstream-to origin/chromeos chromeos
"${KOKORO_ARTIFACTS_DIR}/create_merge" "${target}"
"${KOKORO_ARTIFACTS_DIR}/create_merge" "origin/main"
upload_with_retries
}