forked origin requires a forked destination

Fixes: https://code.forgejo.org/actions/cascading-pr/issues/10
This commit is contained in:
Earl Warren 2023-10-31 22:25:43 +01:00
parent 277569106a
commit dd5427bc63
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
3 changed files with 141 additions and 32 deletions

View file

@ -64,21 +64,6 @@ EOF
log_info "comment added to $(pr_url origin)"
}
function upsert_destination_branch() {
if $(exists_branch destination) ; then
log_info "branch ${options[destination_head]} already exists"
return
fi
cat > $TMPDIR/data <<EOF
{
"new_branch_name":"${options[destination_head]}",
"old_branch_name":"${options[destination_base]}"
}
EOF
repo_curl ${options[destination_repo]} api_json --data @$TMPDIR/data ${options[destination_api]}/branches
log_info "branch ${options[destination_head]} created"
}
function pr_destination_title() {
echo "cascading-pr from ${options[origin_url]}/${options[origin_repo]}/pulls/${options[origin_pr]}"
}
@ -94,13 +79,18 @@ function upsert_destination_pr() {
log_info "an open PR already exists $url"
return
fi
if ${options[destination_is_fork]} ; then
head="$(owner ${options[destination_fork_repo]}):${options[destination_head]}"
else
head=${options[destination_head]}
fi
local title=$(pr_destination_title)
cat > $TMPDIR/data <<EOF
{
"title":"$(pr_destination_title)",
"body":"$(pr_destination_body)",
"base":"${options[destination_base]}",
"head":"${options[destination_head]}"
"head":"$head"
}
EOF
retry repo_curl ${options[destination_repo]} api_json --data @$TMPDIR/data ${options[destination_api]}/pulls > $TMPDIR/destination-pr.json
@ -165,26 +155,55 @@ function pr_from_fork() {
pr $1 | jq --raw-output .head.repo.fork
}
function upsert_clone() {
local direction=$1 ref="$2" clone=$3
function git_clone() {
local direction=$1 url=$2
if ! test -d $TMPDIR/$direction; then
git -c credential.helper="store --file=$TMPDIR/$direction.git-credentials" clone $clone $TMPDIR/$direction
git -c credential.helper="store --file=$TMPDIR/$direction.git-credentials" clone $url $TMPDIR/$direction
fi
(
cd $TMPDIR/$direction
if [[ "$ref" =~ ^refs/ ]] ; then
git fetch origin +$ref:$ref
else
ref=origin/$ref
fi
git checkout -b $direction $ref
git config credential.helper "store --file=$TMPDIR/$direction.git-credentials"
git config user.email cascading-pr@example.com
git config user.name cascading-pr
)
}
function git_checkout() {
local direction=$1 ref="$3"
local remote=origin
(
cd $TMPDIR/$direction
if [[ "$ref" =~ ^refs/ ]] ; then
git fetch ${remote} +$ref:$ref
else
ref=${remote}/$ref
fi
git checkout -b prbranch $ref
)
}
function git_remote() {
local direction=$1 remote=$2 url=$3
(
cd $TMPDIR/$direction
git remote add $remote $url
)
}
function git_reset_branch() {
local direction=$1 remote=$2 branch=$3
(
cd $TMPDIR/$direction
if git ls-remote --exit-code --heads ${remote} $branch ; then
git fetch --quiet ${remote} $branch
git reset --hard ${remote}/$branch
fi
)
}
function sha_pushed() {
local direction=$1
if test -f $TMPDIR/$direction.sha ; then
@ -193,13 +212,13 @@ function sha_pushed() {
}
function push() {
local direction=$1 branch=$2 clone=$3
local direction=$1 remote=$2 branch=$3
(
cd $TMPDIR/$direction
git add .
if git commit -m 'cascading-pr update'; then
git push --force origin $direction:$branch
git push --force ${remote} prbranch:$branch
git rev-parse HEAD > ../$direction.sha
log_info "pushed"
else
@ -214,9 +233,53 @@ function wait_destination_ci() {
wait_success $repo_api $sha
}
function upsert_fork() {
if repo_curl ${options[destination_repo]} api_json ${options[destination_fork_api]} > $TMPDIR/fork.json 2> /dev/null ; then
if test "$(jq --raw-output .fork)" != true ; then
log_error "the destination fork already exists but is not a fork ${options[destination_fork]}"
return 1
fi
local forked_from_repo=$(jq --raw-output .parent.full_name)
if test "$forked_from_repo" != "${options[destination_repo]}" ; then
log_error "${options[destination_fork]} must be a fork of ${options[destination_repo]} but is a fork of $forked_from_repo instead"
return 1
fi
else
local fork_owner=$(owner ${options[destination_fork]})
local data="{}"
if repo_curl ${options[destination_repo]} api_json ${options[destination_url]}/api/v1/orgs/${fork_owner} >& /dev/null ; then
data='{"organization":"'$fork_owner'"}'
fi
repo_curl ${options[destination_repo]} api_json --data "$data" ${options[destination_url]}/api/v1/${options[destination_repo]}/forks
fi
}
function checkout() {
#
# origin
#
git_clone origin ${options[origin_clone]}
git_checkout origin "${options[origin_head]}"
#
# destination
#
git_clone destination ${options[destination_clone]}
git_checkout destination "${options[destination_base]}"
#
# fork
#
local head_remote=origin
if ${options[destination_is_fork]} ; then
upsert_fork
git_remote destination fork ${options[destination_fetch_fork]}
head_remote=fork
fi
git_reset_branch destination $head_remote "${options[destination_head]}"
}
function update() {
upsert_clone origin "${options[origin_head]}" ${options[origin_clone]}
upsert_clone destination "${options[destination_head]}" ${options[destination_clone]}
(
local update=${options[update]}
if ! [[ "$update" =~ ^/ ]] ; then
@ -234,7 +297,11 @@ function update() {
cd $TMPDIR
$update $TMPDIR/destination $TMPDIR/destination-pr.json $TMPDIR/origin $TMPDIR/origin-pr.json
)
push destination ${options[destination_head]} ${options[destination_clone]}
local remote_head=origin
if ${options[destination_is_fork]} ; then
remote_head=fork
fi
push destination $remote_head ${options[destination_head]}
}
function set_clone() {
@ -254,12 +321,22 @@ function set_clone() {
options[${direction}_clone]=${options[${direction}_scheme]}://${options[${direction}_host_port]}/${options[${direction}_repo]}
}
function fork_sanity_check() {
local fork_repo=${options[destination_fork_repo]}
local repo=${options[destination_repo]}
if test "$(repository $fork_repo)" != "$(repository $repo)"; then
echo "$repo and its fork $fork_repo must have the same repository name (see https://codeberg.org/forgejo/forgejo/issues/1707)"
return 1
fi
}
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]=refs/pull/${options[origin_pr]}/head
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]})
@ -267,6 +344,14 @@ function finalize_options() {
options[destination_base]=${options[destination_branch]}
: ${options[prefix]:=${options[origin_repo]}}
options[destination_head]=${options[prefix]}-${options[origin_pr]}
if test "${options[destination_fork_repo]}"; then
fork_sanity_check
options[destination_is_fork]=true
options[destination_fork_api]=${options[destination_url]}/api/v1/repos/${options[destination_fork_repo]}
options[destination_is_fork]=false
fi
: ${options[close_merge]:=false}
}
@ -279,7 +364,7 @@ function run() {
case "$state" in
open)
log_info "PR is open, update or create the cascade branch and PR"
upsert_destination_branch
checkout
update
local sha=$(sha_pushed destination)
if test "$sha" ; then
@ -297,6 +382,7 @@ function run() {
log_info "PR was merged, update the cascade PR"
pr_get origin
pr_get destination
checkout
update
fi
else
@ -351,6 +437,11 @@ function main() {
options[destination_repo]=$1
shift
;;
--destination-fork-repo)
shift
options[destination_fork_repo]=$1
shift
;;
--destination-token)
shift
options[destination_token]=$1