From 2b01e2dc4f910e3e5c51de61ed2a22542d705849 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 20 Jul 2025 10:02:23 +0200 Subject: [PATCH 1/7] chore: test instructions with Forgejo v11.0.3 --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5ca5928..1267e85 100644 --- a/README.md +++ b/README.md @@ -179,12 +179,13 @@ The test environment consists of the following (all users password is admin1234) git clone https://code.forgejo.org/actions/setup-forgejo export PATH=$(pwd)/setup-forgejo:$PATH git clone https://code.forgejo.org/actions/cascading-pr +export PATH=$(pwd)/cascading-pr:$PATH cd cascading-pr export DIR=/tmp/forgejo-for-cascading-pr forgejo-curl.sh logout forgejo-runner.sh teardown forgejo-binary.sh teardown -forgejo-binary.sh setup root admin1234 https://codeberg.org/forgejo/forgejo/releases/download/v1.21.3-0/forgejo-1.21.3-0-linux-amd64 +forgejo-binary.sh setup root admin1234 https://codeberg.org/forgejo/forgejo/releases/download/v11.0.3/forgejo-11.0.3-linux-amd64 FORGEJO_RUNNER_CONFIG=$(pwd)/tests/runner-config.yaml forgejo-runner.sh setup url=$(cat $DIR/forgejo-url) firefox $url @@ -207,7 +208,7 @@ The test for a successful run of the cascading-pr action consists of: Following the steps below recreate the same environment as the integration workflow locally. It is helpful for forensic analysis when -something does not run as expected and the error displayed are unclear. +something does not run as expected and the errors displayed are unclear. To help with the development loop all steps are idempotent and running `tests/run.sh --debug` multiple times must succeed. From d7e60d3ba3bf8a1a6ffad34bf06e158d1796be7a Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 20 Jul 2025 10:24:40 +0200 Subject: [PATCH 2/7] chore: upgrade forgejo-curl.sh --- .forgejo/workflows/integration.yml | 1 + cascading-pr.sh | 4 +- forgejo-curl.sh | 241 ++++++++++++++--------------- tests/run.sh | 4 +- 4 files changed, 125 insertions(+), 125 deletions(-) diff --git a/.forgejo/workflows/integration.yml b/.forgejo/workflows/integration.yml index a91ef4e..471dddd 100644 --- a/.forgejo/workflows/integration.yml +++ b/.forgejo/workflows/integration.yml @@ -27,6 +27,7 @@ jobs: runner_config=/tmp/runner-config.yaml sed -e 's|file: .runner|file: ${{ steps.forgejo.outputs.runner-file }}|' < tests/runner-config.yaml > $runner_config FORGEJO_RUNNER_CONFIG=$runner_config forgejo-runner.sh reload + export PATH=$(pwd):$PATH tests/run.sh --verbose --host_port ${{ steps.forgejo.outputs.host-port }} --url ${{ steps.forgejo.outputs.url }} --token ${{ steps.forgejo.outputs.token }} - name: full logs diff --git a/cascading-pr.sh b/cascading-pr.sh index 8d3e227..7dd7180 100755 --- a/cascading-pr.sh +++ b/cascading-pr.sh @@ -14,7 +14,7 @@ function repo_login() { local direction="$1" local repo=${options[${direction}_repo]} ( - export DOT=$TMPDIR/$repo + export DOT_FORGEJO_CURL=$TMPDIR/$repo forgejo-curl.sh logout forgejo-curl.sh --token "${options[${direction}_token]}" login "${options[${direction}_url]}" ) @@ -23,7 +23,7 @@ function repo_login() { function repo_curl() { local repo=$1 shift - DOT=$TMPDIR/$repo forgejo-curl.sh "$@" + DOT_FORGEJO_CURL=$TMPDIR/$repo forgejo-curl.sh "$@" } function default_branch() { diff --git a/forgejo-curl.sh b/forgejo-curl.sh index ef3d996..ee61723 100755 --- a/forgejo-curl.sh +++ b/forgejo-curl.sh @@ -1,13 +1,14 @@ #!/bin/bash # SPDX-License-Identifier: MIT -VERSION=1.0.0 -SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VERSION=1.0.1 +SELF_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" VERBOSE=false DEBUG=false : ${EXIT_ON_ERROR:=true} : ${TOKEN_NAME:=forgejo-curl} -: ${DOT:=$HOME/.forgejo-curl} +: ${DOT_FORGEJO_CURL:=$HOME/.forgejo-curl} +: ${DOT:=$DOT_FORGEJO_CURL} function debug() { DEBUG=true @@ -28,8 +29,8 @@ function log_error() { } function log_verbose() { - if $VERBOSE; then - log "$@" + if $VERBOSE ; then + log "$@" fi } @@ -39,20 +40,20 @@ function log_info() { function fatal_error() { log_error "$@" - if $EXIT_ON_ERROR; then - exit 1 + if $EXIT_ON_ERROR ; then + exit 1 else - return 1 + return 1 fi } function dot_ensure() { - mkdir -p $DOT + mkdir -p $DOT_FORGEJO_CURL } HEADER_JSON='-H Content-Type:application/json' -HEADER_TOKEN="-H @$DOT/header-token" -HEADER_CSRF="-H @$DOT/header-csrf" +HEADER_TOKEN="-H @$DOT_FORGEJO_CURL/header-token" +HEADER_CSRF="-H @$DOT_FORGEJO_CURL/header-csrf" function api() { client $HEADER_TOKEN "$@" @@ -66,44 +67,41 @@ function login_api() { local user="$1" password="$2" token="$3" scopes="${4:-[\"all\"]}" url="$5" dot_ensure - if test -s $DOT/token; then - log_info "already logged in, ignored" - return + if test -s $DOT_FORGEJO_CURL/token ; then + log_info "already logged in, ignored" + return fi - if test -z "$token"; then - log_verbose curl -sS -X DELETE --user "${user}:${password}" "${url}/api/v1/users/$user/tokens/${TOKEN_NAME}" -o /dev/null -w "%{http_code}" - local basic="${user:-unknown}:${password:-unknown}" - local status=$(curl -sS -X DELETE --user "${basic}" "${url}/api/v1/users/$user/tokens/${TOKEN_NAME}" -o /dev/null -w "%{http_code}") - if test "${status}" != 404 -a "${status}" != 204; then - fatal_error permission denied, the user or password are probably incorrect, try again with --verbose - return 1 - fi - token=$(client $HEADER_JSON --user "${basic}" --data-raw '{"name":"'${TOKEN_NAME}'","scopes":'${scopes}'}' "${url}/api/v1/users/${user}/tokens" | jq --raw-output .sha1) + if test -z "$token" ; then + log_verbose curl -sS -X DELETE --user "${user}:${password}" "${url}/api/v1/users/$user/tokens/${TOKEN_NAME}" -o /dev/null -w "%{http_code}" + local basic="${user:-unknown}:${password:-unknown}" + local status=$(curl -sS -X DELETE --user "${basic}" "${url}/api/v1/users/$user/tokens/${TOKEN_NAME}" -o /dev/null -w "%{http_code}") + if test "${status}" != 404 -a "${status}" != 204 ; then + fatal_error permission denied, the user or password are probably incorrect, try again with --verbose + return 1 + fi + token=$(client $HEADER_JSON --user "${basic}" --data-raw '{"name":"'${TOKEN_NAME}'","scopes":'${scopes}'}' "${url}/api/v1/users/${user}/tokens" | jq --raw-output .sha1) fi - if [[ "$token" =~ ^@ ]]; then - cp "${token##@}" $DOT/token + if [[ "$token" =~ ^@ ]] ; then + cp "${token##@}" $DOT_FORGEJO_CURL/token else - echo "$token" >$DOT/token + echo "$token" > $DOT_FORGEJO_CURL/token fi - ( - echo -n "Authorization: token " - cat $DOT/token - ) >$DOT/header-token + ( echo -n "Authorization: token " ; cat $DOT_FORGEJO_CURL/token ) > $DOT_FORGEJO_CURL/header-token # - # Verify it works + # Verify the token works # local status=$(api -w "%{http_code}" -o /dev/null "${url}/api/v1/user") - if test "${status}" != 200; then - fatal_error "${url}/api/v1/user returns status code '${status}', the token is invalid, $0 logout and login again" - return 1 + if test "${status}" != 200 ; then + fatal_error "${url}/api/v1/user returns status code '${status}', the token is invalid, $0 logout and login again" + return 1 fi } function client() { - log_verbose curl --cookie $DOT/cookies -f -sS "$@" - if ! curl --cookie $DOT/cookies -f -sS "$@"; then - fatal_error + log_verbose curl --cookie $DOT_FORGEJO_CURL/cookies -f -sS "$@" + if ! curl --cookie $DOT_FORGEJO_CURL/cookies -f -sS "$@" ; then + fatal_error fi } @@ -112,19 +110,19 @@ function web() { } function client_update_cookies() { - log_verbose curl --cookie-jar $DOT/cookies --cookie $DOT/cookies -w "%{http_code}" -f -sS "$@" - local status=$(curl --cookie-jar $DOT/cookies --cookie $DOT/cookies -w "%{http_code}" -f -sS "$@") - if ! test "${status}" = 200 -o "${status}" = 303; then - fatal_error + log_verbose curl --cookie-jar $DOT_FORGEJO_CURL/cookies --cookie $DOT_FORGEJO_CURL/cookies -w "%{http_code}" -f -sS "$@" + local status=$(curl --cookie-jar $DOT_FORGEJO_CURL/cookies --cookie $DOT_FORGEJO_CURL/cookies -w "%{http_code}" -f -sS "$@") + if ! test "${status}" = 200 -o "${status}" = 303 ; then + fatal_error fi } function login_client() { local user="$1" password="$2" url="$3" - if test -z "$password"; then - log_verbose "no password, web will not be authenticated" - return + if test -z "$password" ; then + log_verbose "no password, web will not be authenticated" + return fi dot_ensure @@ -135,23 +133,23 @@ function login_client() { # # The login stores a cookie # - client_update_cookies -X POST --data "user_name=${user}" --data "password=${password}" "${url}/user/login" -o $DOT/login.html + client_update_cookies -X POST --data "user_name=${user}" --data "password=${password}" "${url}/user/login" -o $DOT_FORGEJO_CURL/login.html # # Get the CSRF for reuse by other requests # client_update_cookies -o /dev/null "${url}/user/login" - local csrf=$(sed -n -e '/csrf/s/.*csrf\t//p' $DOT/cookies) - echo "X-Csrf-Token: $csrf" >$DOT/header-csrf + local csrf=$(sed -n -e '/csrf/s/.*csrf\t//p' $DOT_FORGEJO_CURL/cookies) + echo "X-Csrf-Token: $csrf" > $DOT_FORGEJO_CURL/header-csrf # # Verify it works # local status=$(web -o /dev/null -w "%{http_code}" "${url}/user/settings") - if test "${status}" != 200; then - grep -C 1 flash-error $DOT/login.html - if ${DEBUG}; then - cat $DOT/login.html - fi - fatal_error login failed, the user or password are probably incorrect, try again with --verbose + if test "${status}" != 200 ; then + grep -C 1 flash-error $DOT_FORGEJO_CURL/login.html + if ${DEBUG} ; then + cat $DOT_FORGEJO_CURL/login.html + fi + fatal_error login failed, the user or password are probably incorrect, try again with --verbose fi } @@ -162,12 +160,13 @@ function login() { } function logout() { - rm -f $DOT/* - if test -d $DOT; then - rmdir $DOT + rm -f $DOT_FORGEJO_CURL/* + if test -d $DOT_FORGEJO_CURL ; then + rmdir $DOT_FORGEJO_CURL fi } + function usage() { cat >&2 < Date: Sun, 20 Jul 2025 10:24:53 +0200 Subject: [PATCH 3/7] chore(tests): fail if token cannot be obtained --- tests/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run.sh b/tests/run.sh index 3097cd9..80dd169 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -33,7 +33,7 @@ function user_curl() { function user_token() { local username=$1 name=$2 - curl -sS -f -H Content-Type:application/json --user "$username:${options[password]}" --data '{"name":"'$name'","scopes":["write:repository","write:issue","read:organization","read:user"]}' ${options[url]}/api/v1/users/$username/tokens | jq --raw-output .sha1 | tee $TMPDIR/$username/repo-token + curl --fail -sS -f -H Content-Type:application/json --user "$username:${options[password]}" --data '{"name":"'$name'","scopes":["write:repository","write:issue","read:organization","read:user"]}' ${options[url]}/api/v1/users/$username/tokens | jq --raw-output .sha1 | tee $TMPDIR/$username/repo-token } function user_secret() { From 029ff0f8584a11568210d56c0d56b9d219dc2ca4 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 20 Jul 2025 10:42:12 +0200 Subject: [PATCH 4/7] chore(ci): run tests against v11 --- .forgejo/workflows/integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/integration.yml b/.forgejo/workflows/integration.yml index 471dddd..4a951ab 100644 --- a/.forgejo/workflows/integration.yml +++ b/.forgejo/workflows/integration.yml @@ -19,7 +19,7 @@ jobs: user: root password: admin1234 image: codeberg.org/forgejo/forgejo - image-version: 1.21 + image-version: 11 lxc-ip-prefix: 10.1.15 - name: tests cascading-pr From c13c83cba241f919b6878c9a78a0d723a0ce7a09 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 20 Jul 2025 10:53:32 +0200 Subject: [PATCH 5/7] chore(upgrade): setup-forgejo@v1.0.1 to setup-forgejo@v3.0.1 --- .forgejo/workflows/integration.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.forgejo/workflows/integration.yml b/.forgejo/workflows/integration.yml index 4a951ab..36fa2d0 100644 --- a/.forgejo/workflows/integration.yml +++ b/.forgejo/workflows/integration.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 - id: forgejo - uses: https://code.forgejo.org/actions/setup-forgejo@v1.0.1 + uses: https://code.forgejo.org/actions/setup-forgejo@v3.0.1 with: user: root password: admin1234 @@ -24,10 +24,12 @@ jobs: - name: tests cascading-pr run: | + set -x + export PATH=$(pwd):$PATH runner_config=/tmp/runner-config.yaml sed -e 's|file: .runner|file: ${{ steps.forgejo.outputs.runner-file }}|' < tests/runner-config.yaml > $runner_config - FORGEJO_RUNNER_CONFIG=$runner_config forgejo-runner.sh reload - export PATH=$(pwd):$PATH + DIR=$(dirname ${{ steps.forgejo.outputs.runner-file }}) + DIR=$DIR FORGEJO_RUNNER_CONFIG=$runner_config forgejo-runner.sh reload tests/run.sh --verbose --host_port ${{ steps.forgejo.outputs.host-port }} --url ${{ steps.forgejo.outputs.url }} --token ${{ steps.forgejo.outputs.token }} - name: full logs @@ -35,4 +37,4 @@ jobs: run: | sed -e 's/^/[RUNNER LOGS] /' ${{ steps.forgejo.outputs.runner-logs }} docker logs forgejo | sed -e 's/^/[FORGEJO LOGS]/' - sleep 5 # hack to avoid mixing outputs in Forgejo v1.21 + sleep 5 # hack to avoid mixing outputs in Forgejo v12.0 From 80ba22a9a46cb2b543159aa9480d70f535f300e0 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 20 Jul 2025 11:19:17 +0200 Subject: [PATCH 6/7] fix(ci): do not push with basic auth --- tests/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run.sh b/tests/run.sh index 80dd169..87a8988 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -12,7 +12,7 @@ source $SELF_DIR/../cascading-pr-lib.sh function push_self() { log_verbose "push cascading-pr action to user1/cascading-pr" - forgejo-test-helper.sh push_self_action http://user1:admin1234@${options[host_port]} user1 cascading-pr vTest + forgejo-test-helper.sh push_self_action http://placeholder:${options[token]}@${options[host_port]} user1 cascading-pr vTest } function user_login() { From 3726ed27c986652b86112e4b6d534d012010ab17 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 20 Jul 2025 07:12:05 +0200 Subject: [PATCH 7/7] fix: loop over the open pull requests to get an exact title match The q= argument of the search may not return matches that are exactly the title depending on how the issue search is defined. https://code.forgejo.org/forgejo/end-to-end/actions/runs/3505/jobs/4 is an example of a failed run because q= did not match the right pull request. --- cascading-pr.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/cascading-pr.sh b/cascading-pr.sh index 7dd7180..99f3c81 100755 --- a/cascading-pr.sh +++ b/cascading-pr.sh @@ -132,7 +132,19 @@ function pr_get_origin() { 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 + local page=1 + touch $TMPDIR/destination-pr.json + while true; do + repo_curl ${options[destination_repo]} api --get --data page=$page --data state=open --data type=pulls ${options[destination_api]}/issues >$TMPDIR/destination-prs.json + if test "$(jq length <$TMPDIR/destination-prs.json)" = 0; then + break + fi + jq --argjson title "\"$title\"" '.[] | select(.title == $title)' <$TMPDIR/destination-prs.json >$TMPDIR/destination-pr.json + if test -s $TMPDIR/destination-pr.json; then + break + fi + page=$(expr $page + 1) + done } function pr_get() {