cascading-pr/cascading-pr-lib.sh
Earl Warren 0c3c8b591b
All checks were successful
integration / integration (pull_request) Successful in 13m33s
allow running on a ref instead of a PR
When running from a PR, the ref is the head of the PR in the origin
repository. The PR is used to:

* display comments about the location of the destination PR
* asynchronously close / merge the destination PR when something
  happens in the origin PR

When only the ref is available, the destination PR must be closed and
the corresponding branch destroyed immediately after it concludes
because there is no convenient way to know what or when something
else will happen.
2024-01-03 16:12:23 +01:00

244 lines
4.8 KiB
Bash

# SPDX-License-Identifier: MIT
set -o pipefail
declare -A options
PREFIX===============
VERBOSE=false
DEBUG=false
: ${EXIT_ON_ERROR:=true}
: ${TMPDIR:=$(mktemp -d)}
# default loop delay is 3600 sec (1 hour)
: ${LOOPS:=100}
: ${LOOP_DELAY:=36}
: ${RETRY_DELAYS:=1 1 5 5 15 30}
function dependencies() {
if ! which jq curl > /dev/null ; then
apt-get update -qq
apt-get -qq install -y jq curl
fi
}
function retry() {
rm -f $TMPDIR/retry.{out,attempt,err}
local success=false
for delay in $RETRY_DELAYS ; do
if "$@" > $TMPDIR/retry.attempt 2>> $TMPDIR/retry.err ; then
success=true
break
fi
cat $TMPDIR/retry.{err,attempt} >> $TMPDIR/retry.out
cat $TMPDIR/retry.{err,attempt} >&2
echo waiting $delay "$@" >&2
sleep $delay
done
if $success ; then
cat $TMPDIR/retry.attempt
return 0
else
echo retry failed for "$@" >&2
cat $TMPDIR/retry.out >&2
return 1
fi
}
function debug() {
DEBUG=true
VERBOSE=true
set -x
PS4='${BASH_SOURCE[0]}:$LINENO: ${FUNCNAME[0]}: '
}
function verbose() {
VERBOSE=true
}
function log() {
echo "$@" >&2
}
function log_error() {
log "$@"
}
function log_verbose() {
if $VERBOSE ; then
log_info "$@"
fi
}
function log_info() {
echo "$PREFIX $@"
}
function fatal_error() {
log_error "$@"
if $EXIT_ON_ERROR ; then
exit 1
else
return 1
fi
}
function stash_debug() {
echo start $SELF
mkdir -p $TMPDIR
> $TMPDIR/run.out
tail --follow $TMPDIR/run.out | sed --unbuffered -n -e "/^$PREFIX/s/^$PREFIX //p" &
pid=$!
if ! $SELF --debug "$@" >& $TMPDIR/run.out ; then
kill $pid
cat $TMPDIR/run.out
echo fail $SELF
return 1
fi
kill $pid
echo success $SELF
}
function host_port() {
local url="$1"
local host_port="${url##http*://}"
echo ${host_port%%/}
}
function scheme() {
local url="$1"
echo "${url%%://*}"
}
function owner() {
local repo="$1"
echo "${repo%%/*}"
}
function repository() {
local repo="$1"
echo "${repo##*/}"
}
function get_status() {
local api="$1"
local sha="$2"
forgejo-curl.sh api_json $api/commits/$sha/status
}
function check_status() {
local api="$1"
local sha="$2"
local expected_status="$3"
local expected_description="$4"
get_status $api $sha > $TMPDIR/status.json
local status="$(jq --raw-output .state < $TMPDIR/status.json)"
local description="$(jq --raw-output .statuses[0].description < $TMPDIR/status.json)"
if test "$status" = "$expected_status" && test -z "$expected_description" -o "$description" = "$expected_description"; then
echo OK
elif test "$status" = "failure" -o "$status" = "success"; then
echo NOK
else
echo RETRY
fi
}
function wait_success() {
wait_status success "$@"
}
function wait_failure() {
wait_status failure "$@"
}
function wait_running() {
wait_status pending "$@" "Has started running"
}
function wait_log() {
local sha="$1" expected_status="$2" expected_description="$3"
local status="$(jq --raw-output .state < $TMPDIR/status.json)"
local description="$(jq --raw-output .statuses[0].description < $TMPDIR/status.json)"
if test "$expected_description"; then
expected_description=" '$expected_description'"
fi
log_info "$sha status waiting '$expected_status'$expected_description, currently '$status' '$description'"
}
function wait_status() {
local status="$1"
local api="$2"
local sha="$3"
local description="$4"
for i in $(seq $LOOPS); do
if test $(check_status "$api" "$sha" "$status" "$description") != RETRY ; then
break
fi
wait_log "$sha" "$status" "$description"
sleep $LOOP_DELAY
done
if test $(check_status "$api" "$sha" "$status" "$description") = "OK" ; then
log_info "$sha status OK"
else
get_status $api $sha | jq .statuses
log_info "$sha status NOK"
return 1
fi
}
function sanity_check_pr_or_ref() {
local pr="$1" ref="$2"
if test "$pr" -a "$ref" ; then
log_error "--origin-pr $pr and --origin-ref $ref are mutually exclusive"
return 1
fi
if test -z "$pr" -a -z "$ref" ; then
log_error "one of --origin-pr or --origin-ref must be set"
return 2
fi
}
function set_origin_head() {
local pr="${options[origin_pr]}"
local ref="${options[origin_ref]}"
sanity_check_pr_or_ref "$pr" "$ref"
if test "$pr"; then
options[origin_head]=refs/pull/$pr/head
origin_sanity_check
else
options[origin_head]=$ref
fi
}
function origin_has_pr() {
test "${options[origin_pr]}"
}
function set_destination_head() {
local pr="${options[origin_pr]}"
local ref="${options[origin_ref]}"
sanity_check_pr_or_ref "$pr" "$ref"
if $(origin_has_pr); then
options[destination_head]=${options[prefix]}-$pr
else
options[destination_head]=${options[prefix]}-$ref
fi
}