blob: 21ab9bf492a034d187677ff28f9d71ca4d4b206f [file] [log] [blame] [edit]
#!/bin/bash
# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Releases a CrOS golang bundle to prod.
# Will release either the CI or CTP Golang binaries as defined by
# the `chromeos/infra/ci-uprev-prod` and `chromeos/infra/ctp-uprev-prod`
# builders. These builders can be invoked directly; the primary purpose of this
# script is to show the pending changes.
set -eu
no_changes="No changes pending."
cros_golang_root="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
recipes_bundle="infra/recipe_bundles/chromium.googlesource.com/chromiumos/infra/recipes"
if ! type jq >/dev/null; then
echo "Please install jq, on debian:"
printf "\tsudo apt install jq\n"
exit 1
fi
usage="Release CrOS Golang packages (CI or CTP).
Usage: ${0} [options] {ci|ctp}
Options:
-f,--force bypasses the prompt
-v,--verbose print all changes in go/, not just those in go/src/infra/cros
-h, --help This help output."
function urlencode() {
# The sed magic strips color codes.
echo "$1" | sed 's/\x1b\[[0-9;]*m//g' | jq -sRr @uri
}
function check_bb_auth() {
if ! bb auth-info > /dev/null; then
bb auth-login
fi
}
# Get the git revision associated with a cipd version (dereference it).
cipd_version_to_githash() {
githash=$(cipd describe -json-output /proc/self/fd/2 -version "$2" "$1" 2>&1 > /dev/null |
jq -r '.result.tags|map(.tag|select(startswith("git_revision:")))[0]|sub(".*:";"")')
# Older CIPD packages use tags with revisions in
# https://chromium.googlesource.com/infra/infra, while newer CIPD packges use
# tags with revisions in
# https://chromium.googlesource.com/infra/infra_superproject. Check if the
# revision is in this repo (infra/infra) and if not lookup the revision
# pinned in infra/infra_superproject.
#
# TODO(b/285908152): Always use the superproject branch when all packages have
# migrated to use superproject tags.
if git rev-list --quiet "${githash}" &> /dev/null; then
echo "${githash}"
else
gob-curl --silent "https://chromium.googlesource.com/infra/infra_superproject/+/${githash}/infra?format=TEXT" | base64 --decode
fi
}
# Get the paths of directories associated with the relevant packages.
get_affected_paths() {
paths=()
for package in "${packages[@]}"
do
if [ "$package" = "$recipes_bundle" ]; then
continue
fi
pkg_name=$(echo "${package}" | awk '{split($0,a,"/"); print a[3]}')
# We want to parse everything under the `go_packages:` header.
IFS=$'\n'
pkg_def=($(cat ${cros_golang_root}/../../../../build/packages/$pkg_name.yaml))
unset IFS
found_go_packages_section=0
for line in "${pkg_def[@]}"
do
if [ "$line" = "go_packages:" ]; then
found_go_packages_section=1
elif [ $found_go_packages_section -eq 1 ]; then
if [ "${line:0:3}" != " -" ]; then
# On to the next section.
break
else
# Parse the package location, make it an absolute path, and append
# to path list.
paths+=("${cros_golang_root}/../$(echo $line | sed 's#- infra/##g')")
fi
fi
done
done
echo "${paths[@]}"
}
# Print CrOS golang commits pending release to production.
cros_golang_pending() {
log_fmt="%C(bold blue)%h %C(bold green)[%al]%C(auto)%d %C(reset)%s"
if [[ "$verbose" == "no" ]]; then
paths="-- ${cros_golang_root}/internal $(get_affected_paths)"
else
paths="-- ${cros_golang_root}/../../.."
fi
changes=$(git -C "${cros_golang_root}" log --color --graph --decorate \
--pretty=format:"${log_fmt}" "${latest_prod_sha}".."${earliest_staging_sha}" ${paths})
if [ -z "${changes}" ]; then
echo "${no_changes}"
else
while IFS= read -r line; do
echo -e " ${line}"
done <<< "${changes}"
fi
}
# "Main" function.
prompt="yes"
verbose="no"
which_golang=""
while [[ $# -ne 0 ]]; do
case $1 in
-f|--force) prompt="no";;
-v|--verbose) verbose="yes";;
-h|--help)
exec printf '%b\n' "${usage}"
;;
-*)
echo "invalid option $1" >/dev/stderr
exec printf '\nUsage:\n%b\n' "${usage}"
exit 1
;;
*)
which_golang="$1"
;;
esac
shift
done
if [[ -z "$which_golang" ]]; then
echo "must select a set of binaries to release (ci or ctp)"
echo
exec printf '%b\n' "${usage}"
exit 1
fi
uprev_builder=""
staging_builder_name=""
if [[ "$which_golang" == "ci" ]] || [[ "$which_golang" == "CI" ]]; then
which_golang="CI"
uprev_builder="chromeos/infra/ci-uprev-prod"
staging_builder_name="ci-uprev-staging"
elif [[ "$which_golang" == "ctp" ]] || [[ "$which_golang" == "CTP" ]]; then
which_golang="CTP"
uprev_builder="chromeos/infra/ctp-uprev-prod"
staging_builder_name="ctp-uprev-staging"
else
echo "must select a valid set of binaries to release (ci or ctp)"
echo
exec printf '%b\n' "${usage}"
exit 1
fi
# Update to remote.
git -C "${cros_golang_root}" remote update > /dev/null
output=$(bb ls ${uprev_builder} -n 1 -p)
packages=$(grep package_name <<< $output | awk '{printf "%s\n", substr($NF, 2, length($NF)-3)}')
packages=($packages)
# We're releasing multiple packages whose staging and prod labels may be at
# different SHAs. Find the most includive range (earliest start point and
# latest end point).
earliest_staging_sha=""
earliest_staging_sha_time=0
latest_prod_sha=""
latest_prod_sha_time=0
for package in "${packages[@]}"
do
if [[ $package == "$recipes_bundle" ]]; then
continue
fi
sha=$(cipd_version_to_githash $package "staging")
sha_time=$(git show -s --format=%ct $sha)
if (( $earliest_staging_sha_time == 0 )) || (( earliest_staging_sha_time < sha_time )); then
earliest_staging_sha_time=$sha_time
earliest_staging_sha=$sha
fi
sha=$(cipd_version_to_githash $package "prod")
sha_time=$(git show -s --format=%ct $sha)
if (( $latest_prod_sha_time == 0 )) || (( latest_prod_sha_time > sha_time )); then
latest_prod_sha_time=$sha_time
latest_prod_sha=$sha
fi
done
echo "CIPD versions for various packages can be found here: https://chrome-infra-packages.appspot.com/p/chromiumos/infra"
echo
echo "=== Checking for pending changes ==="
printf "Here are the changes from the provided (or default main) environment:\n"
pending=$(cros_golang_pending)
echo "${pending}"
echo
echo "Is your change not listed? It might not have been picked up by the staging builder yet (https://ci.chromium.org/p/chromeos/builders/infra/$staging_builder_name)."
echo
if [[ $pending == "$no_changes" ]]; then
echo "No changes pending. Exiting early."
exit 0
fi
# TODO(b/217943800): Staging health check.
if [[ "${prompt}" == "yes" ]]; then
read -rp "Deploy to prod? (y/N): " answer
if [[ "${answer^^}" != "Y" ]]; then
exit 0
fi
fi
check_bb_auth
output=$(bb add "${uprev_builder}")
uprev_build=$(echo $output | head -n1 | awk '{printf $1}')
packages=$(grep package_name <<< $output | awk '{printf "%s\n", substr($NF, 2, length($NF)-3)}')
email_subject="ChromeOS ${which_golang} Golang Release - $(TZ='America/Los_Angeles' date)"
email_message="We've released ${which_golang} Golang to prod!
Uprev build: ${uprev_build}
Deployed packages:
${packages}
Here is a summary of the changes:
${pending}"
email_link="https://mail.google.com/mail/?view=cm&fs=1&bcc=chromeos-infra-releases@google.com&to=chromeos-continuous-integration-team@google.com&su=$(urlencode "${email_subject}")&body=$(urlencode "${email_message}")"
echo
echo "Please click this link and send an email to chromeos-infra-releases!"
echo
echo "${email_link}"