mirror of
https://code.forgejo.org/actions/cascading-pr.git
synced 2025-04-21 08:28:44 +00: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
|
The `update` script is expected to be found in the origin repository
|
||||||
running the PR. It is given four arguments:
|
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
|
on the base branch
|
||||||
* A file with the JSON describing the pull request in the
|
* A file with the JSON describing the pull request in the
|
||||||
destination repository
|
destination repository
|
||||||
|
@ -36,6 +36,9 @@ description: |
|
||||||
When the PR is from a forked repository, the `update` script is checked out from
|
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.
|
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:
|
inputs:
|
||||||
origin-url:
|
origin-url:
|
||||||
description: 'URL of the Forgejo instance where the PR that triggers the action is located (e.g. https://code.forgejo.org)'
|
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:
|
destination-repo:
|
||||||
description: 'the repository in which the cascading PR is created or updated'
|
description: 'the repository in which the cascading PR is created or updated'
|
||||||
required: true
|
required: true
|
||||||
|
destination-fork-repo:
|
||||||
|
description: 'the fork of {desitnation-repo} in which the {destination-branch} will be created or updated'
|
||||||
destination-branch:
|
destination-branch:
|
||||||
description: 'the base branch of the destination repository for the cascading PR'
|
description: 'the base branch of the destination repository for the cascading PR'
|
||||||
required: true
|
required: true
|
||||||
|
@ -102,6 +107,7 @@ runs:
|
||||||
--origin-pr "${{ inputs.origin-pr }}" \
|
--origin-pr "${{ inputs.origin-pr }}" \
|
||||||
--destination-url "${{ inputs.destination-url }}" \
|
--destination-url "${{ inputs.destination-url }}" \
|
||||||
--destination-repo "${{ inputs.destination-repo }}" \
|
--destination-repo "${{ inputs.destination-repo }}" \
|
||||||
|
--destination-fork-repo "${{ inputs.destination-fork-repo }}" \
|
||||||
--destination-token "@$destination_token" \
|
--destination-token "@$destination_token" \
|
||||||
--destination-branch "${{ inputs.destination-branch }}" \
|
--destination-branch "${{ inputs.destination-branch }}" \
|
||||||
--update "${{ inputs.update }}" \
|
--update "${{ inputs.update }}" \
|
||||||
|
|
|
@ -115,6 +115,18 @@ function scheme() {
|
||||||
echo "${url%%://*}"
|
echo "${url%%://*}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function owner() {
|
||||||
|
local repo="$1"
|
||||||
|
|
||||||
|
echo "${repo%%/*}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function repository() {
|
||||||
|
local repo="$1"
|
||||||
|
|
||||||
|
echo "${repo##*/}"
|
||||||
|
}
|
||||||
|
|
||||||
function get_status() {
|
function get_status() {
|
||||||
local api="$1"
|
local api="$1"
|
||||||
local sha="$2"
|
local sha="$2"
|
||||||
|
|
153
cascading-pr.sh
153
cascading-pr.sh
|
@ -64,21 +64,6 @@ EOF
|
||||||
log_info "comment added to $(pr_url origin)"
|
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() {
|
function pr_destination_title() {
|
||||||
echo "cascading-pr from ${options[origin_url]}/${options[origin_repo]}/pulls/${options[origin_pr]}"
|
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"
|
log_info "an open PR already exists $url"
|
||||||
return
|
return
|
||||||
fi
|
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)
|
local title=$(pr_destination_title)
|
||||||
cat > $TMPDIR/data <<EOF
|
cat > $TMPDIR/data <<EOF
|
||||||
{
|
{
|
||||||
"title":"$(pr_destination_title)",
|
"title":"$(pr_destination_title)",
|
||||||
"body":"$(pr_destination_body)",
|
"body":"$(pr_destination_body)",
|
||||||
"base":"${options[destination_base]}",
|
"base":"${options[destination_base]}",
|
||||||
"head":"${options[destination_head]}"
|
"head":"$head"
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
retry repo_curl ${options[destination_repo]} api_json --data @$TMPDIR/data ${options[destination_api]}/pulls > $TMPDIR/destination-pr.json
|
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
|
pr $1 | jq --raw-output .head.repo.fork
|
||||||
}
|
}
|
||||||
|
|
||||||
function upsert_clone() {
|
function git_clone() {
|
||||||
local direction=$1 ref="$2" clone=$3
|
local direction=$1 url=$2
|
||||||
|
|
||||||
if ! test -d $TMPDIR/$direction; then
|
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
|
fi
|
||||||
(
|
(
|
||||||
cd $TMPDIR/$direction
|
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 credential.helper "store --file=$TMPDIR/$direction.git-credentials"
|
||||||
git config user.email cascading-pr@example.com
|
git config user.email cascading-pr@example.com
|
||||||
git config user.name cascading-pr
|
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() {
|
function sha_pushed() {
|
||||||
local direction=$1
|
local direction=$1
|
||||||
if test -f $TMPDIR/$direction.sha ; then
|
if test -f $TMPDIR/$direction.sha ; then
|
||||||
|
@ -193,13 +212,13 @@ function sha_pushed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function push() {
|
function push() {
|
||||||
local direction=$1 branch=$2 clone=$3
|
local direction=$1 remote=$2 branch=$3
|
||||||
|
|
||||||
(
|
(
|
||||||
cd $TMPDIR/$direction
|
cd $TMPDIR/$direction
|
||||||
git add .
|
git add .
|
||||||
if git commit -m 'cascading-pr update'; then
|
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
|
git rev-parse HEAD > ../$direction.sha
|
||||||
log_info "pushed"
|
log_info "pushed"
|
||||||
else
|
else
|
||||||
|
@ -214,9 +233,53 @@ function wait_destination_ci() {
|
||||||
wait_success $repo_api $sha
|
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() {
|
function update() {
|
||||||
upsert_clone origin "${options[origin_head]}" ${options[origin_clone]}
|
|
||||||
upsert_clone destination "${options[destination_head]}" ${options[destination_clone]}
|
|
||||||
(
|
(
|
||||||
local update=${options[update]}
|
local update=${options[update]}
|
||||||
if ! [[ "$update" =~ ^/ ]] ; then
|
if ! [[ "$update" =~ ^/ ]] ; then
|
||||||
|
@ -234,7 +297,11 @@ function update() {
|
||||||
cd $TMPDIR
|
cd $TMPDIR
|
||||||
$update $TMPDIR/destination $TMPDIR/destination-pr.json $TMPDIR/origin $TMPDIR/origin-pr.json
|
$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() {
|
function set_clone() {
|
||||||
|
@ -254,12 +321,22 @@ function set_clone() {
|
||||||
options[${direction}_clone]=${options[${direction}_scheme]}://${options[${direction}_host_port]}/${options[${direction}_repo]}
|
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() {
|
function finalize_options() {
|
||||||
options[origin_api]=${options[origin_url]}/api/v1/repos/${options[origin_repo]}
|
options[origin_api]=${options[origin_url]}/api/v1/repos/${options[origin_repo]}
|
||||||
options[origin_scheme]=$(scheme ${options[origin_url]})
|
options[origin_scheme]=$(scheme ${options[origin_url]})
|
||||||
options[origin_host_port]=$(host_port ${options[origin_url]})
|
options[origin_host_port]=$(host_port ${options[origin_url]})
|
||||||
set_clone origin
|
set_clone origin
|
||||||
options[origin_head]=refs/pull/${options[origin_pr]}/head
|
options[origin_head]=refs/pull/${options[origin_pr]}/head
|
||||||
|
|
||||||
options[destination_api]=${options[destination_url]}/api/v1/repos/${options[destination_repo]}
|
options[destination_api]=${options[destination_url]}/api/v1/repos/${options[destination_repo]}
|
||||||
options[destination_scheme]=$(scheme ${options[destination_url]})
|
options[destination_scheme]=$(scheme ${options[destination_url]})
|
||||||
options[destination_host_port]=$(host_port ${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[destination_base]=${options[destination_branch]}
|
||||||
: ${options[prefix]:=${options[origin_repo]}}
|
: ${options[prefix]:=${options[origin_repo]}}
|
||||||
options[destination_head]=${options[prefix]}-${options[origin_pr]}
|
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}
|
: ${options[close_merge]:=false}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +364,7 @@ function run() {
|
||||||
case "$state" in
|
case "$state" in
|
||||||
open)
|
open)
|
||||||
log_info "PR is open, update or create the cascade branch and PR"
|
log_info "PR is open, update or create the cascade branch and PR"
|
||||||
upsert_destination_branch
|
checkout
|
||||||
update
|
update
|
||||||
local sha=$(sha_pushed destination)
|
local sha=$(sha_pushed destination)
|
||||||
if test "$sha" ; then
|
if test "$sha" ; then
|
||||||
|
@ -297,6 +382,7 @@ function run() {
|
||||||
log_info "PR was merged, update the cascade PR"
|
log_info "PR was merged, update the cascade PR"
|
||||||
pr_get origin
|
pr_get origin
|
||||||
pr_get destination
|
pr_get destination
|
||||||
|
checkout
|
||||||
update
|
update
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
@ -351,6 +437,11 @@ function main() {
|
||||||
options[destination_repo]=$1
|
options[destination_repo]=$1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
--destination-fork-repo)
|
||||||
|
shift
|
||||||
|
options[destination_fork_repo]=$1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--destination-token)
|
--destination-token)
|
||||||
shift
|
shift
|
||||||
options[destination_token]=$1
|
options[destination_token]=$1
|
||||||
|
|
Loading…
Add table
Reference in a new issue