#!/bin/bash # SPDX-License-Identifier: MIT set -e SELF=${BASH_SOURCE[0]} SELF_DIR="$( cd "$( dirname "$SELF" )" && pwd )" source $SELF_DIR/cascading-pr-lib.sh trap "rm -fr $TMPDIR" EXIT function repo_login() { local repo="$1" ( export DOT=$TMPDIR/$repo forgejo-curl.sh logout forgejo-curl.sh --token "${options[destination_token]}" login "${options[destination_url]}" ) } function repo_curl() { local repo=$1 shift DOT=$TMPDIR/$repo forgejo-curl.sh "$@" } function exists_branch() { repo_curl ${options[destination_repo]} api_json ${options[destination_api]}/branches/${options[destination_head]} >& /dev/null } function delete_branch() { if ! $(exists_branch) ; then log_info "branch ${options[destination_head]} does not exists" return fi repo_curl ${options[destination_repo]} api_json -X DELETE ${options[destination_api]}/branches/${options[destination_head]} log_info "branch ${options[destination_head]} deleted" } function upsert_branch() { if $(exists_branch) ; then log_info "branch ${options[destination_head]} already exists" return fi cat > $TMPDIR/data < $TMPDIR/data < $TMPDIR/destination-pr.json log_info "PR created $(pr_url destination)" } function close_pr() { local direction=$1 if test "$(pr_state ${direction})" = "open"; then log_info "closing $(pr_url ${direction})" local number=$(pr_number $direction) repo_curl ${options[${direction}_repo]} api_json -X PATCH --data '{"state":"closed"}' ${options[${direction}_api]}/issues/$number delete_branch $(pr_head ${direction}) else log_info "no open PR found" fi } function pr_get_origin() { repo_curl ${options[origin_repo]} api_json ${options[origin_api]}/pulls/${options[origin_pr]} > $TMPDIR/origin-pr.json } function pr_get_destination() { local title=$(pr_destination_title) repo_curl ${options[destination_repo]} api --get --data state=open --data type=pulls --data-urlencode q="$title" ${options[destination_api]}/issues | jq --raw-output .[0] > $TMPDIR/destination-pr.json } function pr() { local direction=$1 if ! test -f $TMPDIR/${direction}-pr.json; then pr_get_$direction fi cat $TMPDIR/${direction}-pr.json } function pr_state() { pr $1 | jq --raw-output .state } function pr_url() { pr $1 | jq --raw-output .url } function pr_number() { pr $1 | jq --raw-output .number } function pr_head() { pr $1 | jq --raw-output .head.ref } function pr_merged() { pr $1 | jq --raw-output .merged } function upsert_clone() { local direction=$1 branch=$2 clone=$3 local fetch=true if ! test -d $TMPDIR/$direction; then git -c credential.helper="store --file=$TMPDIR/$direction.git-credentials" clone $clone -b $branch $TMPDIR/$direction fetch=false fi ( cd $TMPDIR/$direction git config credential.helper "store --file=$TMPDIR/$direction.git-credentials" if $fetch; then git fetch origin fi git config user.email cascading-pr@example.com git config user.name cascading-pr ) } function push() { local direction=$1 branch=$2 clone=$3 ( cd $TMPDIR/$direction git add . if git commit -m 'cascading-pr update'; then git push --force origin $branch log_info "pushed" else log_info "nothing to push" fi git rev-parse HEAD > ../$direction.sha ) } function wait_destination_ci() { local sha="$1" local repo_api=${options[destination_url]}/api/v1/repos/${options[destination_repo]} wait_success $repo_api $sha } function update() { upsert_clone origin ${options[origin_head]} ${options[origin_clone]} upsert_clone destination ${options[destination_head]} ${options[destination_clone]} ( cd $TMPDIR/origin ${options[update]} $TMPDIR/destination $TMPDIR/destination-pr.json $TMPDIR/origin-pr.json ) push destination ${options[destination_head]} ${options[destination_clone]} } function set_clone() { local direction=$1 local token=${options[${direction}_token]} if [[ "$token" =~ ^@ ]] ; then local file=${token##@} ( echo -n ${options[${direction}_scheme]}://any: cat $file echo @${options[${direction}_host_port]}/${options[${direction}_repo]} ) > $TMPDIR/$direction.git-credentials else echo ${options[${direction}_scheme]}://any:${options[${direction}_token]}@${options[${direction}_host_port]}/${options[${direction}_repo]} > $TMPDIR/$direction.git-credentials fi options[${direction}_clone]=${options[${direction}_scheme]}://${options[${direction}_host_port]}/${options[${direction}_repo]} } function finalize_options() { options[origin_api]=${options[origin_url]}/api/v1/repos/${options[origin_repo]} options[origin_scheme]=$(scheme ${options[origin_url]}) options[origin_host_port]=$(host_port ${options[origin_url]}) set_clone origin options[origin_head]=$(pr_head origin) options[destination_api]=${options[destination_url]}/api/v1/repos/${options[destination_repo]} options[destination_scheme]=$(scheme ${options[destination_url]}) options[destination_host_port]=$(host_port ${options[destination_url]}) set_clone destination options[destination_base]=${options[destination_branch]} : ${options[prefix]:=${options[origin_repo]}} options[destination_head]=${options[prefix]}-${options[origin_pr]} } function run() { local state=$(pr_state origin) case "$state" in open) log_info "PR is open, update or create the cascade branch and PR" repo_login ${options[destination_repo]} upsert_branch upsert_pr repo_login ${options[origin_repo]} update wait_destination_ci $(cat $TMPDIR/destination.sha) ;; closed) if "$(pr_merged origin)"; then log_info "PR was merged, update the cascade PR" repo_login ${options[destination_repo]} pr origin pr destination update else log_info "PR is closed, close the cascade PR and remove the branch" repo_login ${options[destination_repo]} close_pr destination fi ;; *) log_info "state '$state', do nothing" ;; esac } function main() { while true; do case "$1" in --verbose) shift verbose ;; --debug) shift debug ;; --origin-url) shift options[origin_url]=$1 shift ;; --origin-repo) shift options[origin_repo]=$1 shift ;; --origin-token) shift options[origin_token]=$1 shift ;; --origin-pr) shift options[origin_pr]=$1 shift ;; --destination-url) shift options[destination_url]=$1 shift ;; --destination-repo) shift options[destination_repo]=$1 shift ;; --destination-token) shift options[destination_token]=$1 shift ;; --destination-branch) shift options[destination_branch]=$1 shift ;; --update) shift options[update]=$1 shift ;; --prefix) shift options[prefix]=$1 shift ;; *) finalize_options "${1:-run}" return 0 ;; esac done } dependencies if echo "${@}" | grep --quiet -e '--debug' ; then main "${@}" else stash_debug "${@}" fi