From 657e223acce3a6fbd249297b0aab97edd69c95d4 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 13 Oct 2023 19:10:50 +0200 Subject: [PATCH] close cascading PR if the origin PR is closed --- cascading-pr.sh | 123 +++++++++++++++---- tests/originrepo/.forgejo/workflows/test.yml | 7 +- tests/run.sh | 10 ++ 3 files changed, 113 insertions(+), 27 deletions(-) diff --git a/cascading-pr.sh b/cascading-pr.sh index 24c984c..977a8ff 100755 --- a/cascading-pr.sh +++ b/cascading-pr.sh @@ -24,34 +24,88 @@ function repo_curl() { 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() { - local repo_api=${options[destination_url]}/api/v1/repos/${options[destination_repo]} - if repo_curl ${options[destination_repo]} api_json $repo_api/branches/${options[destination_head]} >& /dev/null ; then + if $(exists_branch) ; then log_info "branch ${options[destination_head]} already exists" return fi - repo_curl ${options[destination_repo]} api_json --data-raw '{"new_branch_name":"'${options[destination_head]}'","old_branch_name":"'${options[destination_base]}'"}' $repo_api/branches + repo_curl ${options[destination_repo]} api_json --data-raw '{"new_branch_name":"'${options[destination_head]}'","old_branch_name":"'${options[destination_base]}'"}' ${options[destination_api]}/branches log_info "branch ${options[destination_head]} created" } -function upsert_pr() { - local repo_api=${options[destination_url]}/api/v1/repos/${options[destination_repo]} - local title="cascading-pr from ${options[origin_url]}/${options[origin_repo]}/pulls/${options[origin_pr]}" - repo_curl ${options[destination_repo]} api --get --data state=open --data type=pulls --data-urlencode q="$title" $repo_api/issues | jq --raw-output .[0] > $TMPDIR/destination-pr.json - url=$(jq --raw-output .url < $TMPDIR/destination-pr.json) - if test "$url" != "null"; then - log_info "PR already exists $url" - return - fi - repo_curl ${options[destination_repo]} api_json --data-raw '{"title":"'"$title"'","base":"'${options[destination_base]}'","head":"'${options[destination_head]}'"}' $repo_api/pulls > $TMPDIR/destination-pr.json - url=$(jq --raw-output .url < $TMPDIR/destination-pr.json) - log_info "PR created $url" +function pr_destination_title() { + echo "cascading-pr from ${options[origin_url]}/${options[origin_repo]}/pulls/${options[origin_pr]}" } -function origin_pr_head() { - local repo_api=${options[origin_url]}/api/v1/repos/${options[origin_repo]} - repo_curl ${options[origin_repo]} api_json $repo_api/pulls/${options[origin_pr]} > $TMPDIR/origin-pr.json - jq --raw-output .head.ref < $TMPDIR/origin-pr.json +function upsert_pr() { + url=$(pr_url destination) + state=$(pr_state destination) + if test "$url" != "null" -a "$state" = "open"; then + log_info "an open PR already exists $url" + return + fi + local title=$(pr_destination_title) + repo_curl ${options[destination_repo]} api_json --data-raw '{"title":"'"$title"'","base":"'${options[destination_base]}'","head":"'${options[destination_head]}'"}' ${options[destination_api]}/pulls > $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-raw '{"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 upsert_clone() { @@ -106,10 +160,12 @@ function update() { } 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]}) options[origin_clone]=${options[origin_scheme]}://any:${options[origin_token]}@${options[origin_host_port]}/${options[origin_repo]} - options[origin_head]=$(origin_pr_head) + 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]}) options[destination_clone]=${options[destination_scheme]}://any:${options[destination_token]}@${options[destination_host_port]}/${options[destination_repo]} @@ -119,12 +175,27 @@ function finalize_options() { } function run() { - repo_login ${options[destination_repo]} - upsert_branch - upsert_pr - repo_login ${options[origin_repo]} - update - wait_destination_ci $(cat $TMPDIR/destination.sha) + 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) + log_info "PR is closed, close the cascade PR and remove the branch" + repo_login ${options[destination_repo]} + close_pr destination + ;; + *) + log_info "state '$state', do nothing" + ;; + esac } function main() { diff --git a/tests/originrepo/.forgejo/workflows/test.yml b/tests/originrepo/.forgejo/workflows/test.yml index 9b709da..ac8665a 100644 --- a/tests/originrepo/.forgejo/workflows/test.yml +++ b/tests/originrepo/.forgejo/workflows/test.yml @@ -1,6 +1,11 @@ # SPDX-License-Identifier: MIT name: test -on: [pull_request] +on: + pull_request: + types: + - opened + - synchronize + - closed jobs: test: runs-on: docker diff --git a/tests/run.sh b/tests/run.sh index cf6c08f..fcaef1a 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -45,6 +45,12 @@ function user_create() { user_login $username } +function close_pull_request() { + forgejo-curl.sh api_json ${options[url]}/api/v1/repos/user1/originrepo/pulls | jq --raw-output '.[] | .number' | while read pr ; do + forgejo-curl.sh api_json -X PATCH --data-raw '{"state":"closed"}' ${options[url]}/api/v1/repos/user1/originrepo/issues/$pr + done +} + function create_pull_request() { forgejo-curl.sh api_json ${options[url]}/api/v1/repos/user1/originrepo/pulls | jq --raw-output '.[] | .number' | while read pr ; do forgejo-curl.sh api_json -X DELETE ${options[url]}/api/v1/repos/user1/originrepo/issues/$pr @@ -93,6 +99,10 @@ function run() { create_pull_request wait_success ${options[url]}/api/v1/repos/user1/originrepo $(cat $TMPDIR/originrepo.sha) + + close_pull_request + + wait_success ${options[url]}/api/v1/repos/user1/originrepo $(cat $TMPDIR/originrepo.sha) } function main() {