mirror of
https://code.forgejo.org/actions/cascading-pr
synced 2025-03-14 22:36:58 +01:00
forked origin requires a forked destination
Fixes: https://code.forgejo.org/actions/cascading-pr/issues/10
This commit is contained in:
parent
277569106a
commit
dd5427bc63
3 changed files with 141 additions and 32 deletions
|
@ -21,7 +21,7 @@ description: |
|
|||
The `update` script is expected to be found in the origin repository
|
||||
running the PR. It is given four arguments:
|
||||
|
||||
* A directory in which the destination repository is checked-out
|
||||
* A directory in which the destination repository (or a fork) is checked-out
|
||||
on the base branch
|
||||
* A file with the JSON describing the pull request in the
|
||||
destination repository
|
||||
|
@ -36,6 +36,9 @@ description: |
|
|||
When the PR is from a forked repository, the `update` script is checked out from
|
||||
the default branch instead of the head branch of the fork.
|
||||
|
||||
If the fork of the destination repository is specified and it does
|
||||
not exist, it is created.
|
||||
|
||||
inputs:
|
||||
origin-url:
|
||||
description: 'URL of the Forgejo instance where the PR that triggers the action is located (e.g. https://code.forgejo.org)'
|
||||
|
@ -55,6 +58,8 @@ inputs:
|
|||
destination-repo:
|
||||
description: 'the repository in which the cascading PR is created or updated'
|
||||
required: true
|
||||
destination-fork-repo:
|
||||
description: 'the fork of {desitnation-repo} in which the {destination-branch} will be created or updated'
|
||||
destination-branch:
|
||||
description: 'the base branch of the destination repository for the cascading PR'
|
||||
required: true
|
||||
|
@ -102,6 +107,7 @@ runs:
|
|||
--origin-pr "${{ inputs.origin-pr }}" \
|
||||
--destination-url "${{ inputs.destination-url }}" \
|
||||
--destination-repo "${{ inputs.destination-repo }}" \
|
||||
--destination-fork-repo "${{ inputs.destination-fork-repo }}" \
|
||||
--destination-token "@$destination_token" \
|
||||
--destination-branch "${{ inputs.destination-branch }}" \
|
||||
--update "${{ inputs.update }}" \
|
||||
|
|
|
@ -115,6 +115,18 @@ function scheme() {
|
|||
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"
|
||||
|
|
153
cascading-pr.sh
153
cascading-pr.sh
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue