diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..669640d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +tab_width = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.forgejo/cascading-docs b/.forgejo/cascading-docs new file mode 100755 index 0000000..63a5bd9 --- /dev/null +++ b/.forgejo/cascading-docs @@ -0,0 +1,23 @@ +#!/bin/bash + +set -ex + +docs=$1 +docs_pr=$2 +end_to_end=$3 +end_to_end_ref=$4 + +if ! test -d /srv/contexts ; then + echo no contexts in /srv/contexts, do nothing + exit 0 +fi + +cd $docs/docs/user + +if test "$FORCE_VERSION" = "$VERSION" || ! test -f actions-contexts/version.txt || test "$VERSION" != $(cat actions-contexts/version.txt) ; then + rm -fr actions-contexts + mkdir actions-contexts + echo "$VERSION" > actions-contexts/version.txt + # populated by actions/run.sh + rsync -av /srv/contexts/ actions-contexts/ +fi diff --git a/.forgejo/prepare-end-to-end/action.yml b/.forgejo/prepare-end-to-end/action.yml new file mode 100644 index 0000000..7bc2ed0 --- /dev/null +++ b/.forgejo/prepare-end-to-end/action.yml @@ -0,0 +1,30 @@ +runs: + using: "composite" + steps: + - name: cache S3 binaries + id: S3 + uses: https://code.forgejo.org/actions/cache@v4 + with: + path: | + /usr/local/bin/minio + /usr/local/bin/mc + /usr/local/bin/garage + key: S3 + + - uses: https://code.forgejo.org/actions/setup-forgejo@v2.0.7 + with: + install-only: true + - run: forgejo-binary.sh ensure_user forgejo + - uses: actions/download-artifact@v3 + with: + name: forgejo-dev + path: /srv/forgejo-binaries + - name: chown/chmod /srv/forgejo-binaries + run: | + mkdir -p /srv/forgejo-binaries + chown -R forgejo /srv/forgejo-binaries + chmod -R +x /srv/forgejo-binaries + - run: | + script=$(pwd)/end-to-end.sh + $script run dependencies + $script clobber diff --git a/.forgejo/workflows/actions.yml b/.forgejo/workflows/actions.yml deleted file mode 100644 index 383eed3..0000000 --- a/.forgejo/workflows/actions.yml +++ /dev/null @@ -1,40 +0,0 @@ -on: - pull_request: - push: - branches: - - 'main' - -jobs: - actions: - runs-on: self-hosted - strategy: - matrix: - info: - - binary: https://codeberg.org/forgejo-experimental/forgejo/releases/download/v1.22.0-test/forgejo-1.22.0-test-linux-amd64 - version: v1_22 - tests: ${{ vars.V1_22_TESTS }} - - binary: https://codeberg.org/forgejo/forgejo/releases/download/v1.21.3-0/forgejo-1.21.3-0-linux-amd64 - version: v1_21 - tests: ${{ vars.V1_21_TESTS }} - - binary: https://codeberg.org/forgejo/forgejo/releases/download/v1.20.6-1/forgejo-1.20.6-1-linux-amd64 - version: v1_20 - tests: ${{ vars.V1_20_TESTS }} - steps: - - uses: actions/checkout@v4 - - - uses: https://code.forgejo.org/actions/setup-forgejo@v2 - with: - install-only: true - - - if: matrix.info.tests != 'none' - shell: bash - run: | - set -x - forgejo-binary.sh ensure_user forgejo - test "${{ matrix.info.binary }}" - test "${{ matrix.info.version }}" - - export DIR=$(mktemp -d) - chown forgejo $DIR /srv - - su -c "actions/run.sh ${{ matrix.info.binary }} ${{ matrix.info.version }} ${{ matrix.info.tests }}" forgejo diff --git a/.forgejo/workflows/end-to-end.yml b/.forgejo/workflows/end-to-end.yml new file mode 100644 index 0000000..35b27fc --- /dev/null +++ b/.forgejo/workflows/end-to-end.yml @@ -0,0 +1,163 @@ +# +# +# https://code.forgejo.org/forgejo/end-to-end/settings/actions +# +# secrets.CASCADE_DOCS_ORIGIN_TOKEN +# https://code.forgejo.org/forgejo-ci scope write:issue, read:repository, read:user +# vars.CASCADE_DOCS_DESTINATION_DOER +# forgejo-cascading-pr (https://codeberg.org/forgejo-cascading-pr) +# secrets.CASCADE_DOCS_DESTINATION_TOKEN +# https://codeberg.org/forgejo-cascading-pr scope write:issue, write:repository, read:user +# vars.CASCADE_DOCS_FORCE_VERSION +# replace the generated documentation for a given version even if it has already +# been generated (e.g. v7.0.0-test) +# + +on: + pull_request: + push: + branches: + - 'main' + +jobs: + build: + runs-on: docker + container: + image: 'code.forgejo.org/oci/node:20-bookworm' + steps: + - uses: https://code.forgejo.org/actions/checkout@v4 + - uses: https://code.forgejo.org/actions/setup-go@v5 + with: + go-version: "1.22" + - name: lib/build.sh + run: | + mkdir $d /tmp/forgejo-upload + touch /tmp/forgejo-upload/PLACEHOLDER + + if ! test -f forgejo/build-from-sources; then + echo forgejo/build-from-sources is not present, do not build any version from source + exit 0 + fi + + set -x + # + # SQLite needs gcc to be compiled + # + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq + apt-get -q install -y -qq build-essential + + d=/tmp/forgejo-binaries + + for version in $(cat forgejo/build-from-sources) ; do + lib/build.sh $version $d + forgejo=$d/forgejo-$version-dev + $forgejo --version + mv $forgejo /tmp/forgejo-upload/forgejo-$version + done + - uses: actions/upload-artifact@v3 + with: + name: forgejo-dev + path: /tmp/forgejo-upload + + packages: + needs: [build] + runs-on: lxc-bookworm + steps: + - uses: https://code.forgejo.org/actions/checkout@v4 + - uses: ./.forgejo/prepare-end-to-end + - run: su forgejo -c "./end-to-end.sh test_packages" + - name: full logs + if: always() + run: su forgejo -c "./end-to-end.sh show_logs" + + actions: + needs: [build] + runs-on: lxc-bookworm + steps: + - uses: https://code.forgejo.org/actions/checkout@v4 + - uses: ./.forgejo/prepare-end-to-end + - run: ./end-to-end.sh prepare_dockerd + - run: su forgejo -c "./end-to-end.sh test_actions" + - name: full logs + if: always() + run: su forgejo -c "./end-to-end.sh show_logs" + + federation: + needs: [build] + runs-on: lxc-bookworm + steps: + - uses: https://code.forgejo.org/actions/checkout@v4 + - uses: ./.forgejo/prepare-end-to-end + - name: install zstd + run: | + export DEBIAN_FRONTEND=noninteractive + apt-get -q install -y -qq zstd + - name: cache GitLab OCI image + uses: https://code.forgejo.org/actions/cache@v4 + with: + path: | + /srv/forgejo-binaries/gitlab + key: gitlab + - run: | + su forgejo -c "./end-to-end.sh test_federation" + - name: full logs + if: always() + run: su forgejo -c "./end-to-end.sh show_logs" + + actions-docs: + needs: [build] + runs-on: lxc-bookworm + if: github.ref == 'refs/heads/main' + strategy: + matrix: + info: + - version: "11.0" + branch: next + forgejo: https://codeberg.org + owner: forgejo-experimental + - version: "10.0" + forgejo: https://codeberg.org + owner: forgejo + steps: + - uses: https://code.forgejo.org/actions/checkout@v4 + - uses: ./.forgejo/prepare-end-to-end + - name: set full-version + id: full-version + shell: bash + run: | + set -x + full_version=$(./end-to-end.sh full_version ${{ matrix.info.version }} ${{ matrix.info.owner }}) + echo value="$full_version" >> $GITHUB_OUTPUT + - run: ./end-to-end.sh prepare_dockerd + - run: su forgejo -c "./end-to-end.sh test_actions ${{ matrix.info.version }}" + - name: update documentation + uses: https://code.forgejo.org/actions/cascading-pr@v2.1 + with: + origin-url: ${{ env.GITHUB_SERVER_URL }} + origin-repo: ${{ github.repository }} + origin-token: ${{ secrets.CASCADE_DOCS_ORIGIN_TOKEN }} + origin-ref: refs/heads/main + destination-url: https://codeberg.org + destination-fork-repo: ${{ vars.CASCADE_DOCS_DESTINATION_DOER }}/docs + destination-repo: forgejo/docs + destination-branch: ${{ matrix.info.branch || format('v{0}', matrix.info.version) }} + destination-token: ${{ secrets.CASCADE_DOCS_DESTINATION_TOKEN }} + prefix: ${{ env.GITHUB_REPOSITORY }}-${{ matrix.info.version }} + update: .forgejo/cascading-docs + env: + FORCE_VERSION: "${{ vars.CASCADE_DOCS_FORCE_VERSION }}" + VERSION: "${{ steps.full-version.outputs.value }}" + + upgrade: + name: upgrade and storage + needs: [build] + runs-on: lxc-bookworm + steps: + - uses: https://code.forgejo.org/actions/checkout@v4 + - uses: ./.forgejo/prepare-end-to-end + - run: su forgejo -c "./end-to-end.sh test_upgrades" + - run: su forgejo -c "./end-to-end.sh test_storage" + - name: full logs + if: always() + run: su forgejo -c "./end-to-end.sh show_logs" diff --git a/.forgejo/workflows/upgrade.yml b/.forgejo/workflows/upgrade.yml deleted file mode 100644 index 06a8843..0000000 --- a/.forgejo/workflows/upgrade.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: upgrade - -on: - pull_request: - push: - branches: - - 'main' - -jobs: - upgrade: - runs-on: docker - container: - image: 'docker.io/node:20-bookworm' - steps: - - name: cache S3 binaries - id: S3 - uses: https://code.forgejo.org/actions/cache@v3 - with: - path: | - /usr/local/bin/minio - /usr/local/bin/mc - /usr/local/bin/garage - key: S3 - - - name: skip if S3 cache hit - if: steps.S3.outputs.cache-hit != 'true' - run: echo no hit - - - uses: https://code.forgejo.org/actions/checkout@v4 - - uses: https://code.forgejo.org/actions/setup-go@v4 - with: - go-version: "1.21" - - run: | - git config --add safe.directory '*' - adduser --quiet --comment forgejo --disabled-password forgejo - chown -R forgejo:forgejo . - - run: | - script=$(pwd)/forgejo/upgrades/test-upgrade.sh - $script run dependencies - $script clobber - su forgejo -c "$script test_upgrades" diff --git a/.gitignore b/.gitignore index 5c2e2ef..ff9fe83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,9 @@ *~ -forgejo-api -forgejo-header forgejo-ip forgejo-runner-home forgejo-runner-pid forgejo-runner-token forgejo-runner.clientpid forgejo-runner.log -forgejo-token .runner #* diff --git a/README.md b/README.md index f85ed11..4e7ed5b 100644 --- a/README.md +++ b/README.md @@ -2,60 +2,93 @@ A series of tests scenarios and assertions covering [Forgejo](https://codeberg.org/forgejo/forgejo) and the [Forgejo -runner](https://code.forgejo.org/forgejo/runner). They partially rely -on [Forgejo actions](https://code.forgejo.org/actions) developped -specifically for testing such as -[setup-forgejo](https://code.forgejo.org/actions/setup-forgejo). +runner](https://code.forgejo.org/forgejo/runner). They are designed to run using Forgejo releases and development versions compiled from designated repositories. -## Hacking +# Removing legacy tests -To run and debug workflows from `actions/example-*`, from the root of -the source directory, with docker and forgejo-curl.sh installed, mimic -what `.forgejo/workflows/actions.yml` does. There may be some manual -tweaking (such as creating temporary directories) because the tests -run as root in the context of Forgejo Actions and assume they have -admin permissions. But they do not need to run as root and must work -fine when run as a regular user. +End-to-end tests cover the supported range of releases and when one of +them is EOL, it must be removed as well as the tests that target it +specifically. Otherwise the test suite would grow indefinitely. -### Prepare the Forgejo instance and the runner +When a release is EOL, a branch is cut with a name following the +pattern `legacy/vX.Y-vA.B`. For instance when `v8.0` is published and +`v1.21` is EOL, the branch `legacy/v8.0-v1.21` is cut. + +# Hacking + +docker and sudo must be installed with insecure registries allowed in +/etc/docker/daemon.json for the IP that will be used for forgejo such +as: + +```json +{ + "insecure-registries": [ "10.0.0.0/8" ] +} +``` + +Use setup-forgejo from source. + +The [setup-forgejo](https://code.forgejo.org/actions/setup-forgejo) +repository is a [Forgejo +Action](https://forgejo.org/docs/v7.0/user/actions/) which is meant +to be used in workflows. However, it is implemented as shell scripts that +can also be used to create Forgejo instances and runners locally. This +is convenient for testing and the reason why it needs to be added to the PATH. +For instance, it is a dependency of the `end-to-end.sh` script. ```sh git clone https://code.forgejo.org/actions/setup-forgejo export PATH=$(pwd)/setup-forgejo:$PATH git clone https://code.forgejo.org/forgejo/end-to-end cd end-to-end -export DIR=/tmp/end-to-end ``` -Run one example +## Running from locally built binary ```sh -actions/run.sh https://codeberg.org/forgejo-experimental/forgejo/releases/download/v1.22.0-test/forgejo-1.22.0-test-linux-amd64 v1_22 cron # runs actions/example-cron +make TAGS='bindata sqlite sqlite_unlock_notify' generate forgejo +cp -a forgejo /srv/forgejo-binaries/forgejo-10.0 ``` -Cleanup +It will be used whenever the version `10.0` is specified in a test. + +## Running actions locally + +To run and debug workflows from `actions/example-*`, from the root of +the source directory, with docker and forgejo-curl.sh installed, mimic +what `.forgejo/workflows/end-to-end.yml` does. There may be some manual +tweaking (such as creating temporary directories) because the tests +run as root in the context of Forgejo Actions and assume they have +admin permissions. But they do not need to run as root and must work +fine when run as a regular user. ```sh -actions/run.sh https://codeberg.org/forgejo-experimental/forgejo/releases/download/v1.22.0-test/forgejo-1.22.0-test-linux-amd64 v1_22 none +./end-to-end.sh run dependencies +./end-to-end.sh actions_setup 10.0 +firefox 0.0.0.0:3000 # user root / admin1234 +./end-to-end.sh actions_verify_example echo +./end-to-end.sh actions_teardown ``` -Run all examples for v1_22 +Note that `with-docker-tcp` requires the docker daemon listens to +tcp://127.0.0.1:2375. See `actions/actions.sh` for how to do that. + +## Running other tests locally + +To run and debug tests, from the root of the source directory. + +Run one test. When the test fails the instance can be inspected at http://0.0.0.0:3000 ```sh -actions/run.sh https://codeberg.org/forgejo-experimental/forgejo/releases/download/v1.22.0-test/forgejo-1.22.0-test-linux-amd64 v1_22 +./end-to-end.sh test_packages_alpine +./end-to-end.sh test_storage_stable_s3 minio ``` -### Remote testing +Cleanup. It will teardown the Forgejo instance. -To reduce the runtime the following variables can be set to control -the number of cases run by the -[actions](.forgejo/workflows/actions.yml) tests. If set to -**none** they are not run at all for that version of Forgejo. If -it does not exist, all tests are run. - -* `V1_22_TESTS` -* `V1_21_TESTS` -* `V1_20_TESTS` +```sh +./end-to-end.sh stop +``` diff --git a/actions/actions.sh b/actions/actions.sh new file mode 100755 index 0000000..e62debf --- /dev/null +++ b/actions/actions.sh @@ -0,0 +1,172 @@ +# Copyright 2024 The Forgejo Authors +# SPDX-License-Identifier: MIT + +ACTIONS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +function actions_export_variables() { + export HOST_PORT + export url=http://${FORGEJO_USER}:${FORGEJO_PASSWORD}@${HOST_PORT} + export token=$(cat $DOT_FORGEJO_CURL/token) +} + +function actions_verify_feature() { + local feature=$1 + + actions_export_variables + + export FEATURE_DIR=$ACTIONS_DIR/feature-$feature + + echo "============================ RUN feature-$feature ===================" + bash -ex $FEATURE_DIR/run.sh || return 1 +} + +function actions_verify_example() { + local example=$1 + + actions_export_variables + actions_cleanup_example_volume + + export example + export EXAMPLE_DIR=$ACTIONS_DIR/example-$example + + if test -f $EXAMPLE_DIR/setup.sh; then + echo "============================ SETUP example-$example ===================" + bash -ex $EXAMPLE_DIR/setup.sh || return 1 + fi + + if test -f $EXAMPLE_DIR/run.sh; then + echo "============================ RUN example-$example ===================" + bash -ex $EXAMPLE_DIR/run.sh || return 1 + else + forgejo-test-helper.sh run_workflow actions/example-$example $url root example-$example $example $token || return 1 + fi + + if test -f $EXAMPLE_DIR/teardown.sh; then + echo "============================ TEARDOWN example-$example ===================" + bash -ex $EXAMPLE_DIR/teardown.sh || return 1 + fi + + actions_save_contexts $example +} + +function actions_save_contexts() { + local example="$1" + + if test -d /srv/example/$example/contexts; then + mkdir -p /srv/contexts + rsync -av /srv/example/$example/contexts/ /srv/contexts/$example/ + fi +} + +function actions_cleanup_example_volume() { + if test $(id -u) != 0; then + $SUDO chown $(id -u) /srv + fi + + if ! test -d /srv/example; then + mkdir -p /srv/example + return + fi + + $SUDO rm -fr /srv/example/* +} + +function actions_setup() { + local version=$1 + actions_teardown + + reset_forgejo $ACTIONS_DIR/default-app.ini + start_forgejo $version + + export FORGEJO_RUNNER_LOGS=$DIR/forgejo-runner.log + + actions_cleanup_example_volume + export FORGEJO_RUNNER_CONFIG=$ACTIONS_DIR/runner-config.yaml + forgejo-runner.sh setup '' '' http://${HOST_PORT} +} + +function actions_teardown() { + forgejo-curl.sh logout + forgejo-runner.sh teardown + stop_forgejo +} + +function actions_runner_version() { + local runner_version=$($DIR/forgejo-runner --version | sed -n -e 's/forgejo-runner version v//p') + if test -z "$runner_version"; then + $DIR/forgejo-runner --version + echo failed to parse version + false + fi + echo $runner_version +} + +function prepare_dockerd() { + mkdir -p /etc/systemd/system/docker.service.d + cat >/etc/systemd/system/docker.service.d/override.conf <<'EOF' +[Service] +ExecStart= +ExecStart=/usr/sbin/dockerd -H unix:///var/run/docker.sock -H tcp://127.0.0.1:2375 --containerd=/run/containerd/containerd.sock $DOCKER_OPTS +EOF + systemctl daemon-reload + if ! systemctl restart docker.service; then + journalctl --no-pager --unit docker.service + return 1 + fi + netstat -lntp | grep 127.0.0.1:2375 +} + +function test_actions() { + local versions="${1:-$RELEASE_NUMBERS}" + + for version in $versions; do + + actions_setup $version + local runner_version=$(actions_runner_version) + + log_info "Testing actions with Forgejo $version & Forgejo runner $runner_version" + + run actions_verify_example with-docker-tcp + if dpkg --compare-versions $runner_version gt 5.0.2; then + for example in with-docker-host with-docker-socket without-docker-socket; do + run actions_verify_example $example + done + fi + + if dpkg --compare-versions $runner_version gt 6.0.1; then + run actions_verify_example force-rebuild + fi + + if dpkg --compare-versions $version ge 7.0 && dpkg --compare-versions $runner_version gt 3.3.0; then + for example in artifacts-v4; do + run actions_verify_example $example + done + fi + + for example in echo config-options cache checkout service container expression local-action docker-action if if-fail push tag push-cancel artifacts pull-request context; do + run actions_verify_example $example + done + + if dpkg --compare-versions $version lt 7.1; then + for example in cron; do + run actions_verify_example $example + done + fi + + if dpkg --compare-versions $version ge 7.1; then + for example in automerge post-7-0-schedule; do + run actions_verify_example $example + done + fi + + if dpkg --compare-versions $version ge 8.0; then + for example in workflow-dispatch; do + run actions_verify_example $example + done + fi + + if dpkg --compare-versions $version ge 9.0; then + run actions_verify_example schedule-noncancel + fi + done +} diff --git a/forgejo/upgrades/relative-app.ini b/actions/default-app.ini similarity index 53% rename from forgejo/upgrades/relative-app.ini rename to actions/default-app.ini index d53c291..e4577a5 100644 --- a/forgejo/upgrades/relative-app.ini +++ b/actions/default-app.ini @@ -1,18 +1,23 @@ -RUN_MODE = prod -WORK_PATH = ${WORK_PATH} +RUN_MODE = dev +WORK_PATH = forgejo-work-path [server] APP_DATA_PATH = ${WORK_PATH}/data +DOMAIN = ${IP} HTTP_PORT = 3000 SSH_LISTEN_PORT = 2222 LFS_START_SERVER = true +[queue] +TYPE = immediate + [database] DB_TYPE = sqlite3 +PATH = ${WORK_PATH}/forgejo.db [log] MODE = file -LEVEL = debug +LEVEL = trace ROUTER = file [log.file] @@ -24,21 +29,7 @@ INSTALL_LOCK = true [repository] ENABLE_PUSH_CREATE_USER = true DEFAULT_PUSH_CREATE_PRIVATE = false +DEFAULT_REPO_UNITS = repo.code,repo.releases,repo.issues,repo.pulls,repo.wiki,repo.projects,repo.packages,repo.actions -[attachment] -PATH = relative-attachments - -[lfs] -PATH = relative-lfs - -[avatar] -PATH = relative-avatars - -[repo-avatar] -PATH = relative-repo-avatars - -[repo-archive] -PATH = relative-repo-archive - -[packages] -PATH = relative-packages +[actions] +ENABLED = true diff --git a/actions/example-artifacts-v4/.forgejo/workflows/test.yml b/actions/example-artifacts-v4/.forgejo/workflows/test.yml new file mode 100644 index 0000000..d215354 --- /dev/null +++ b/actions/example-artifacts-v4/.forgejo/workflows/test.yml @@ -0,0 +1,47 @@ +on: [push] +jobs: + upload-many: + runs-on: docker + steps: + - run: mkdir -p artifacts + + - run: touch artifacts/ONE artifacts/TWO + + - uses: https://code.forgejo.org/forgejo/upload-artifact@v4 + with: + name: many-artifacts + path: artifacts/ + + download-many: + needs: [upload-many] + runs-on: docker + steps: + - uses: https://code.forgejo.org/forgejo/download-artifact@v4 + + - run: | + test -f many-artifacts/ONE + test -f many-artifacts/TWO + + upload-one: + runs-on: docker + steps: + - run: mkdir -p path/to/artifact + + - run: echo hello > path/to/artifact/world.txt + + - uses: https://code.forgejo.org/forgejo/upload-artifact@v4 + with: + name: my-artifact + path: path/to/artifact/world.txt + + download-one: + needs: [upload-one] + runs-on: docker + steps: + - run: "! test -f world.txt" + + - uses: https://code.forgejo.org/forgejo/download-artifact@v4 + with: + name: my-artifact + + - run: "test -f world.txt" diff --git a/actions/example-automerge/.forgejo/workflows/test.yml b/actions/example-automerge/.forgejo/workflows/test.yml new file mode 100644 index 0000000..3ce5da0 --- /dev/null +++ b/actions/example-automerge/.forgejo/workflows/test.yml @@ -0,0 +1,13 @@ +on: + pull_request: + +jobs: + test: + runs-on: docker + container: + image: code.forgejo.org/oci/node:20-bookworm + options: "--volume /srv/example:/srv/example" + + steps: + - run: | + ${{ vars.SCRIPT }} diff --git a/actions/example-automerge/run.sh b/actions/example-automerge/run.sh new file mode 100755 index 0000000..1d48860 --- /dev/null +++ b/actions/example-automerge/run.sh @@ -0,0 +1,108 @@ +TMPDIR=$(mktemp -d) + +trap "rm -fr $TMPDIR" EXIT + +source $EXAMPLE_DIR/../../lib/lib.sh + +api=$url/api/v1 +repo=root/example-automerge +export d=/srv/example/automerge + +function reset_automerge_pr() { + # + # repository with a pull_request event workflow that always succeeds + # + mkdir -p $d + + forgejo-curl.sh api_json -X DELETE $api/repos/$repo >&/dev/null || true + forgejo-test-helper.sh push_workflow actions/example-$example $url root example-$example setup-forgejo $token + + forgejo-curl.sh api_json -X DELETE $api/repos/$repo/actions/variables/SCRIPT >&/dev/null || true + forgejo-curl.sh api_json -X POST --data-raw '{"value":"true"}' $api/repos/$repo/actions/variables/SCRIPT + + ( + cd $d + rm -fr example-automerge + git clone $url/$repo + cd example-automerge + git checkout -b other + git config user.email root@example.com + git config user.name username + touch file-unique-to-the-pr-branch + echo other >>README + git add . + git commit -m 'other change' + git push --force -u origin other + ) + + # + # make sure the runner won't race with the sequence that follows + # + forgejo-runner.sh teardown + # + # create a PR and schedule it for automerge when the workflow succeeds + # + api_pr_delete_all $api $repo + forgejo-curl.sh api_json --data-raw '{"title":"PR title","base":"main","head":"other"}' $api/repos/$repo/pulls >$TMPDIR/pr.json + local pr=$(jq -r .number <$TMPDIR/pr.json) + forgejo-curl.sh api_json --data-raw '{"Do":"merge","merge_when_checks_succeed":true}' $api/repos/$repo/pulls/$pr/merge + if api_pr_is_merged $api $repo $pr; then + echo pull request already merged although it should not be + return 1 + fi +} + +function verify_automerge_on_status_success() { + reset_automerge_pr + local pr=$(jq -r .number <$TMPDIR/pr.json) + # + # run the workflow + # + forgejo-runner.sh run + local sha=$(api_branch_tip $api $repo other) + api_pr_wait_success $api $repo $sha + # + # verify the PR was automerged + # + if ! retry api_pr_is_merged $api $repo $pr; then + echo pull request is not automerged as expected + return 1 + fi +} + +function verify_automerge_on_reviewer_approval() { + reset_automerge_pr + local pr=$(jq -r .number <$TMPDIR/pr.json) + # + # require at least one review for a PR to be merged + # + api_branch_protect $api $repo main + # + # run the workflow + # + forgejo-runner.sh run + local sha=$(api_branch_tip $api $repo other) + api_pr_wait_success $api $repo $sha + # + # approve the PR + # + local username=user1 + api_user_create $api $username $username@example.com + api_user_make_admin $api $username + user_login $username + DOT=$API_TMPDIR/$username api_pr_approve $api $repo $pr + # + # verify the PR was automerged + # + if ! retry api_pr_is_merged $api $repo $pr; then + echo pull request is not automerged as expected + return 1 + fi +} + +function main() { + verify_automerge_on_status_success + verify_automerge_on_reviewer_approval +} + +main diff --git a/actions/example-automerge/setup.sh b/actions/example-automerge/setup.sh new file mode 100755 index 0000000..9476a67 --- /dev/null +++ b/actions/example-automerge/setup.sh @@ -0,0 +1 @@ +mkdir -p /srv/example/automerge diff --git a/actions/example-cache/.forgejo/workflows/test.yml b/actions/example-cache/.forgejo/workflows/test.yml new file mode 100644 index 0000000..ab37b75 --- /dev/null +++ b/actions/example-cache/.forgejo/workflows/test.yml @@ -0,0 +1,51 @@ +on: [push] + +jobs: + build: + runs-on: docker + container: + image: code.forgejo.org/oci/node:20-bookworm + steps: + - name: cache restore + id: cachestep1 + uses: https://code.forgejo.org/actions/cache/restore@v4 + with: + path: | + /usr/local/bin/something + key: cachekey + + - name: cache hit + run: | + set -x + test "${{ steps.cachestep1.outputs.cache-hit }}" != true + + - name: create something + run: echo SOMETHING > /usr/local/bin/something + + - name: cache save + uses: https://code.forgejo.org/actions/cache/save@v4 + with: + path: | + /usr/local/bin/something + key: ${{ steps.cachestep1.outputs.cache-primary-key }} + + - name: remove something + run: rm /usr/local/bin/something + + - name: cache restore + id: cachestep2 + uses: https://code.forgejo.org/actions/cache/restore@v4 + with: + path: | + /usr/local/bin/something + key: cachekey + + - name: verify something + run: | + set -x + test SOMETHING = $(cat /usr/local/bin/something) + + - name: cache hit + run: | + set -x + test "${{ steps.cachestep2.outputs.cache-hit }}" = true diff --git a/actions/example-cache/runner-config.yaml b/actions/example-cache/runner-config.yaml new file mode 100644 index 0000000..11818cc --- /dev/null +++ b/actions/example-cache/runner-config.yaml @@ -0,0 +1,30 @@ + +log: + level: debug + +runner: + file: .runner + capacity: 1 + env_file: .env + timeout: 3h + insecure: false + fetch_timeout: 5s + fetch_interval: 2s + labels: ["docker:docker://code.forgejo.org/oci/node:20-bookworm"] + +cache: + enabled: true + dir: "/srv/example/cache" + host: "" + port: 0 + +container: + network: "bridge" + privileged: false + options: + workdir_parent: + valid_volumes: ["/srv/example"] + docker_host: "" + +host: + workdir_parent: diff --git a/actions/example-cache/setup.sh b/actions/example-cache/setup.sh new file mode 100755 index 0000000..9c1f5ad --- /dev/null +++ b/actions/example-cache/setup.sh @@ -0,0 +1 @@ +FORGEJO_RUNNER_CONFIG=$EXAMPLE_DIR/runner-config.yaml forgejo-runner.sh reload diff --git a/actions/example-cron/teardown.sh b/actions/example-cache/teardown.sh similarity index 100% rename from actions/example-cron/teardown.sh rename to actions/example-cache/teardown.sh diff --git a/actions/example-config-options/.forgejo/workflows/test.yml b/actions/example-config-options/.forgejo/workflows/test.yml new file mode 100644 index 0000000..d9c0324 --- /dev/null +++ b/actions/example-config-options/.forgejo/workflows/test.yml @@ -0,0 +1,16 @@ +on: [push] + +jobs: + test: + runs-on: docker + container: + options: "--hostname customname" + steps: + - run: | + test -f /srv/example-config-options-volume-valid + - run: | + ! test -f /srv/example-config-options-volume-invalid + - run: | + test "$FROB" = "NITZ" + - run: | + test "$(cat /etc/hostname)" = customname diff --git a/actions/example-config-options/runner-config.yaml b/actions/example-config-options/runner-config.yaml new file mode 100644 index 0000000..dff5356 --- /dev/null +++ b/actions/example-config-options/runner-config.yaml @@ -0,0 +1,31 @@ + +log: + level: debug + job_level: debug + +runner: + file: .runner + capacity: 1 + env_file: .env + timeout: 3h + insecure: false + fetch_timeout: 5s + fetch_interval: 2s + labels: ["docker:docker://code.forgejo.org/oci/node:20-bookworm"] + +cache: + enabled: false + dir: "" + host: "" + port: 0 + +container: + network: "" + privileged: false + options: "--volume /srv/example-config-options-volume-valid:/srv/example-config-options-volume-valid --volume /srv/example-config-options-volume-invalid:/srv/example-config-options-volume-invalid --env FROB=NITZ" + workdir_parent: + valid_volumes: ["/srv/example-config-options-volume-valid"] + docker_host: "" + +host: + workdir_parent: diff --git a/actions/example-config-options/setup.sh b/actions/example-config-options/setup.sh new file mode 100755 index 0000000..d9f6ea9 --- /dev/null +++ b/actions/example-config-options/setup.sh @@ -0,0 +1,3 @@ +>/srv/example-config-options-volume-valid +>/srv/example-config-options-volume-invalid +FORGEJO_RUNNER_CONFIG=$EXAMPLE_DIR/runner-config.yaml forgejo-runner.sh reload diff --git a/actions/example-pull-request/teardown.sh b/actions/example-config-options/teardown.sh similarity index 100% rename from actions/example-pull-request/teardown.sh rename to actions/example-config-options/teardown.sh diff --git a/actions/example-container/.forgejo/workflows/test.yml b/actions/example-container/.forgejo/workflows/test.yml index 6385e42..ded75cd 100644 --- a/actions/example-container/.forgejo/workflows/test.yml +++ b/actions/example-container/.forgejo/workflows/test.yml @@ -3,6 +3,6 @@ jobs: test: runs-on: docker container: - image: alpine:3.18 + image: code.forgejo.org/oci/alpine:3.21 steps: - run: grep Alpine /etc/os-release diff --git a/actions/example-context/.forgejo/fileone.txt b/actions/example-context/.forgejo/fileone.txt deleted file mode 100644 index a2628c1..0000000 --- a/actions/example-context/.forgejo/fileone.txt +++ /dev/null @@ -1 +0,0 @@ -ONE diff --git a/actions/example-context/.forgejo/workflows/test.yml b/actions/example-context/.forgejo/workflows/test.yml new file mode 100644 index 0000000..a894bfd --- /dev/null +++ b/actions/example-context/.forgejo/workflows/test.yml @@ -0,0 +1,206 @@ +on: [push] + +jobs: + test: + runs-on: docker + container: + image: code.forgejo.org/oci/node:20-bookworm + volumes: + - /srv/example:/srv/example + steps: + + - name: env.CI + run: | + set -x + test "$CI" = true + test "$CI" = "${{ env.CI }}" + + - name: GITHUB_ACTION + run: | + set -x + echo "$GITHUB_ACTION" | grep -E '^[0-9]+$' + test "$GITHUB_ACTION" = "${{ env.GITHUB_ACTION }}" + test "$GITHUB_ACTION" = "${{ github.ACTION }}" + + # See also actions/example-local-action/.forgejo/local-action/action.yml + - name: GITHUB_ACTION_PATH + run: | + set -x + test -z "$GITHUB_ACTION_PATH" + test "$GITHUB_ACTION_PATH" = "${{ env.GITHUB_ACTION_PATH }}" + test "$GITHUB_ACTION_PATH" = "${{ github.ACTION_PATH }}" + + - name: when running an action + if: ${{ env.GITHUB_ACTIONS }} + uses: SELF@main + with: + input-one: "otherone" + + - name: GITHUB_ACTION_REPOSITORY + run: test -f /srv/example/example-context/GITHUB_ACTION_REPOSITORY + + - name: GITHUB_ACTION_PATH + run: test -f /srv/example/example-context/GITHUB_ACTION_PATH + + - name: GITHUB_ACTIONS + run: | + set -x + test "$GITHUB_ACTIONS" = true + test "$GITHUB_ACTIONS" = "${{ env.GITHUB_ACTIONS }}" + + - name: GITHUB_ACTOR + run: | + set -x + test "$GITHUB_ACTOR" + test "$GITHUB_ACTOR" = "${{ env.GITHUB_ACTOR }}" + test "$GITHUB_ACTOR" = "${{ github.ACTOR }}" + + - name: GITHUB_API_URL + shell: bash + run: | + set -x + [[ "$GITHUB_API_URL" =~ /api/v1$ ]] + test "$GITHUB_API_URL" = "${{ env.GITHUB_API_URL }}" + test "$GITHUB_API_URL" = "${{ github.API_URL }}" + + # See also actions/example-pull-request/.forgejo/workflows/test.yml + - name: GITHUB_BASE_REF + run: | + set -x + test -z "$GITHUB_BASE_REF" + test "$GITHUB_BASE_REF" = "${{ env.GITHUB_BASE_REF }}" + test "$GITHUB_BASE_REF" = "${{ github.BASE_REF }}" + + # See also actions/example-pull-request/.forgejo/workflows/test.yml + - name: GITHUB_HEAD_REF + run: | + set -x + test -z "$GITHUB_HEAD_REF" + test "$GITHUB_HEAD_REF" = "${{ env.GITHUB_HEAD_REF }}" + test "$GITHUB_HEAD_REF" = "${{ github.HEAD_REF }}" + + - name: GITHUB_ENV + run: | + set -x + test -f "$GITHUB_ENV" + test "$GITHUB_ENV" = "${{ env.GITHUB_ENV }}" + + - name: GITHUB_EVENT_NAME + run: | + set -x + test "$GITHUB_EVENT_NAME" = push + test "$GITHUB_EVENT_NAME" = "${{ env.GITHUB_EVENT_NAME }}" + test "$GITHUB_EVENT_NAME" = "${{ github.EVENT_NAME }}" + + - name: GITHUB_JOB + run: | + set -x + test "$GITHUB_JOB" = test + test "$GITHUB_JOB" = "${{ env.GITHUB_JOB }}" + test "$GITHUB_JOB" = "${{ github.JOB }}" + + - name: GITHUB_OUTPUT + run: | + set -x + test -f "$GITHUB_OUTPUT" + test "$GITHUB_OUTPUT" = "${{ env.GITHUB_OUTPUT }}" + + - name: GITHUB_PATH + run: | + set -x + test -f "$GITHUB_PATH" + test "$GITHUB_PATH" = "${{ env.GITHUB_PATH }}" + + - name: GITHUB_REF + shell: bash + run: | + set -x + [[ "$GITHUB_REF" =~ ^refs/ ]] + test "$GITHUB_REF" = "${{ env.GITHUB_REF }}" + test "$GITHUB_REF" = "${{ github.REF }}" + + - name: GITHUB_REF_NAME + shell: bash + run: | + set -x + ! [[ "$GITHUB_REF_NAME" =~ ^refs/ ]] + test "$GITHUB_REF_NAME" = "${{ env.GITHUB_REF_NAME }}" + test "$GITHUB_REF_NAME" = "${{ github.REF_NAME }}" + + - name: GITHUB_REPOSITORY + run: | + set -x + test "$GITHUB_REPOSITORY" = root/example-context + test "$GITHUB_REPOSITORY" = "${{ env.GITHUB_REPOSITORY }}" + test "$GITHUB_REPOSITORY" = "${{ github.REPOSITORY }}" + + - name: GITHUB_REPOSITORY_OWNER + run: | + set -x + test "$GITHUB_REPOSITORY_OWNER" = root + test "$GITHUB_REPOSITORY_OWNER" = "${{ env.GITHUB_REPOSITORY_OWNER }}" + test "$GITHUB_REPOSITORY_OWNER" = "${{ github.REPOSITORY_OWNER }}" + + - name: GITHUB_RUN_NUMBER + run: | + set -x + echo "$GITHUB_RUN_NUMBER" | grep -E '^[0-9]+$' + test "$GITHUB_RUN_NUMBER" = "${{ env.GITHUB_RUN_NUMBER }}" + test "$GITHUB_RUN_NUMBER" = "${{ github.RUN_NUMBER }}" + + - name: GITHUB_SERVER_URL + shell: bash + run: | + set -x + [[ "$GITHUB_SERVER_URL" =~ ^http ]] + test "$GITHUB_SERVER_URL" = "${{ env.GITHUB_SERVER_URL }}" + test "$GITHUB_SERVER_URL" = "${{ github.SERVER_URL }}" + + - name: GITHUB_SHA + run: | + set -x + test "$GITHUB_SHA" + test "$GITHUB_SHA" = "${{ env.GITHUB_SHA }}" + test "$GITHUB_SHA" = "${{ github.SHA }}" + + - name: GITHUB_STEP_SUMMARY + run: | + set -x + test -f "$GITHUB_STEP_SUMMARY" + test "$GITHUB_STEP_SUMMARY" = "${{ env.GITHUB_STEP_SUMMARY }}" + + # See also actions/example-pull-request/.forgejo/workflows/test.yml + - name: GITHUB_TOKEN + run: | + set -x + test "$GITHUB_TOKEN" + test "$GITHUB_TOKEN" = "${{ env.GITHUB_TOKEN }}" + test "$GITHUB_TOKEN" = "${{ github.TOKEN }}" + + - name: GITHUB_WORKSPACE + run: | + set -x + test -d "$GITHUB_WORKSPACE" + test "$GITHUB_WORKSPACE" = "${{ env.GITHUB_WORKSPACE }}" + test "$GITHUB_WORKSPACE" = "${{ github.WORKSPACE }}" + + - name: RUNNER_ARCH + run: | + set -x + test "$RUNNER_ARCH" = X64 + + - name: RUNNER_OS + run: | + set -x + test "$RUNNER_OS" = Linux + + # runner 3.3.0 $RUNNER_TOOL_CACHE is not an existing directory + # - name: RUNNER_TOOL_CACHE + # run: | + # set -x + # test -d "$RUNNER_TOOL_CACHE" + + - name: RUNNER_TEMP + run: | + set -x + test -d "$RUNNER_TEMP" diff --git a/actions/example-context/action-for-context/action.yml b/actions/example-context/action-for-context/action.yml new file mode 100644 index 0000000..dcb8f2b --- /dev/null +++ b/actions/example-context/action-for-context/action.yml @@ -0,0 +1,23 @@ +inputs: + input-one: + default: 'one' + description: 'description one' + +runs: + using: "composite" + steps: + - name: GITHUB_ACTION_REPOSITORY + run: | + set -x + test "$GITHUB_ACTION_REPOSITORY" = root/action-for-context + test "$GITHUB_ACTION_REPOSITORY" = "${{ env.GITHUB_ACTION_REPOSITORY }}" + test "$GITHUB_ACTION_REPOSITORY" = "${{ github.ACTION_REPOSITORY }}" + touch /srv/example/example-context/GITHUB_ACTION_REPOSITORY + + - name: GITHUB_ACTION_PATH + shell: bash + run: | + set -x + [[ "$GITHUB_ACTION_PATH" =~ action-for-context@main$ ]] + test "$GITHUB_ACTION_PATH" = "${{ github.ACTION_PATH }}" + touch /srv/example/example-context/GITHUB_ACTION_PATH diff --git a/actions/example-context/run.sh b/actions/example-context/run.sh new file mode 100644 index 0000000..d9d08b8 --- /dev/null +++ b/actions/example-context/run.sh @@ -0,0 +1 @@ +forgejo-test-helper.sh run_workflow actions/example-$example $url root example-$example action-for-context $token diff --git a/actions/example-context/setup.sh b/actions/example-context/setup.sh new file mode 100644 index 0000000..c7759f4 --- /dev/null +++ b/actions/example-context/setup.sh @@ -0,0 +1,3 @@ +mkdir -p /srv/example/example-context + +forgejo-test-helper.sh push actions/example-$example/action-for-context $url root action-for-context diff --git a/actions/example-cron/.forgejo/workflows/test.yml b/actions/example-cron/.forgejo/workflows/test.yml index c81b017..e2748f8 100644 --- a/actions/example-cron/.forgejo/workflows/test.yml +++ b/actions/example-cron/.forgejo/workflows/test.yml @@ -7,8 +7,16 @@ jobs: runs-on: docker container: image: code.forgejo.org/oci/debian:bookworm - options: "--volume /srv/example-cron-volume:/srv/example-cron-volume" + options: "--volume /srv/example:/srv/example" steps: + - name: save context + run: | + d=/srv/example/cron/contexts/$GITHUB_EVENT_NAME + mkdir -p $d + cat > $d/github <<'EOF' + ${{ toJSON(github) }} + EOF + - run: | - touch /srv/example-cron-volume/DONE + touch /srv/example/cron-volume/DONE diff --git a/actions/example-cron/run.sh b/actions/example-cron/run.sh index 3ced64c..bf19e98 100755 --- a/actions/example-cron/run.sh +++ b/actions/example-cron/run.sh @@ -7,7 +7,7 @@ forgejo-test-helper.sh push_workflow actions/example-$example $url root example- # See https://codeberg.org/forgejo/forgejo/pulls/1941 for more information # function verify_ref() { - local ref=$(sqlite3 $DIR/forgejo-work-path/forgejo.db 'select ref from action_schedule') + local ref=$(sqlite3 $DIR/forgejo-work-path/forgejo.db 'select ref from action_schedule') test "${ref##*/}" = "main" } verify_ref @@ -16,7 +16,12 @@ forgejo-curl.sh api_json --data '{"new_branch_name":"zzzz"}' $api/repos/root/exa verify_ref # cron runs once per minute, give it three minutes max before declaring failure -if ! RETRY_DELAYS="30 30 30 30 30 30" forgejo.sh retry test -f /srv/example-cron-volume/DONE ; then +if ! RETRY_DELAYS="30 30 30 30 30 30" forgejo.sh retry test -f /srv/example/cron-volume/DONE; then cat $FORGEJO_RUNNER_LOGS false fi + +c=/srv/example/cron/contexts/schedule/github +cat $c +test "schedule" = "$(jq -r .event_name <$c)" +test "schedule" = "$(jq -r .event.action <$c)" diff --git a/actions/example-cron/setup.sh b/actions/example-cron/setup.sh index ee70b92..ed70dec 100755 --- a/actions/example-cron/setup.sh +++ b/actions/example-cron/setup.sh @@ -1,3 +1 @@ -rm -fr /srv/example-cron-volume -mkdir -p /srv/example-cron-volume -FORGEJO_RUNNER_CONFIG=$EXAMPLE_DIR/runner-config.yaml forgejo-runner.sh reload +mkdir -p /srv/example/cron-volume diff --git a/actions/example-force-rebuild/.forgejo/local-docker-action/Dockerfile b/actions/example-force-rebuild/.forgejo/local-docker-action/Dockerfile new file mode 100644 index 0000000..2339a39 --- /dev/null +++ b/actions/example-force-rebuild/.forgejo/local-docker-action/Dockerfile @@ -0,0 +1,5 @@ +FROM code.forgejo.org/oci/debian:bookworm +COPY entrypoint.sh /run/entrypoint.sh +# if we rebuild, we should notice this file change +COPY input.txt /run/input.txt +ENTRYPOINT [ "/run/entrypoint.sh" ] diff --git a/actions/example-force-rebuild/.forgejo/local-docker-action/action.yml b/actions/example-force-rebuild/.forgejo/local-docker-action/action.yml new file mode 100644 index 0000000..e53d627 --- /dev/null +++ b/actions/example-force-rebuild/.forgejo/local-docker-action/action.yml @@ -0,0 +1,6 @@ +name: local docker action +description: local docker action, build depends on "input.txt" + +runs: + using: 'docker' + image: 'Dockerfile' diff --git a/actions/example-force-rebuild/.forgejo/local-docker-action/entrypoint.sh b/actions/example-force-rebuild/.forgejo/local-docker-action/entrypoint.sh new file mode 100755 index 0000000..e2ee36a --- /dev/null +++ b/actions/example-force-rebuild/.forgejo/local-docker-action/entrypoint.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +set -x +exit "$(< /run/input.txt)" \ No newline at end of file diff --git a/actions/example-force-rebuild/.forgejo/local-docker-action/input.txt b/actions/example-force-rebuild/.forgejo/local-docker-action/input.txt new file mode 100644 index 0000000..5a94479 --- /dev/null +++ b/actions/example-force-rebuild/.forgejo/local-docker-action/input.txt @@ -0,0 +1 @@ +this file will be filled by the test \ No newline at end of file diff --git a/actions/example-force-rebuild/.forgejo/workflows/test.yml b/actions/example-force-rebuild/.forgejo/workflows/test.yml new file mode 100644 index 0000000..4151065 --- /dev/null +++ b/actions/example-force-rebuild/.forgejo/workflows/test.yml @@ -0,0 +1,10 @@ +--- +on: + push: +jobs: + test: + runs-on: docker + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - uses: ./.forgejo/local-docker-action diff --git a/actions/example-force-rebuild/run.sh b/actions/example-force-rebuild/run.sh new file mode 100755 index 0000000..d82890a --- /dev/null +++ b/actions/example-force-rebuild/run.sh @@ -0,0 +1,42 @@ +TMPDIR=$(mktemp -d) + +trap "rm -fr $TMPDIR" EXIT + +function setup_with_rebuild() { + FORGEJO_RUNNER_CONFIG=$EXAMPLE_DIR/runner-config-with-rebuild.yml forgejo-runner.sh reload +} + +function setup_without_rebuild() { + FORGEJO_RUNNER_CONFIG=$EXAMPLE_DIR/runner-config-without-rebuild.yml forgejo-runner.sh reload +} + +function run() { + local dir="$1" + local expected="$2" + local repo=root/example-$example + forgejo-test-helper.sh push_workflow $dir $url root example-$example setup-forgejo $token + sha=$(forgejo-test-helper.sh branch_tip $url $repo main) + forgejo-test-helper.sh wait_$expected $url $repo $sha +} + +function main() { + local dir=$TMPDIR/repository + cp -a $EXAMPLE_DIR $dir + + # set up passing docker action + echo "0" >$dir/.forgejo/local-docker-action/input.txt + setup_with_rebuild + run $dir success + + # change docker action to fail + echo "1" >$dir/.forgejo/local-docker-action/input.txt + # ... but without a rebuild, it should still pass + setup_without_rebuild + run $dir success + + # now the action should fail + setup_with_rebuild + run $dir failure +} + +main diff --git a/actions/example-cron/runner-config.yaml b/actions/example-force-rebuild/runner-config-with-rebuild.yml similarity index 64% rename from actions/example-cron/runner-config.yaml rename to actions/example-force-rebuild/runner-config-with-rebuild.yml index 119cde4..c99a230 100644 --- a/actions/example-cron/runner-config.yaml +++ b/actions/example-force-rebuild/runner-config-with-rebuild.yml @@ -1,6 +1,5 @@ - log: - level: info + level: debug runner: file: .runner @@ -10,7 +9,7 @@ runner: insecure: false fetch_timeout: 5s fetch_interval: 2s - labels: ["docker:docker://code.forgejo.org/oci/node:16-bullseye"] + labels: ["docker:docker://code.forgejo.org/oci/node:20-bookworm"] cache: enabled: false @@ -19,12 +18,13 @@ cache: port: 0 container: - network: "" + network: "bridge" privileged: false options: workdir_parent: - valid_volumes: ["/srv/example-cron-volume"] + valid_volumes: ["/srv/example"] docker_host: "" + force_rebuild: true host: workdir_parent: diff --git a/actions/example-force-rebuild/runner-config-without-rebuild.yml b/actions/example-force-rebuild/runner-config-without-rebuild.yml new file mode 100644 index 0000000..793fbcd --- /dev/null +++ b/actions/example-force-rebuild/runner-config-without-rebuild.yml @@ -0,0 +1,30 @@ +log: + level: debug + +runner: + file: .runner + capacity: 1 + env_file: .env + timeout: 3h + insecure: false + fetch_timeout: 5s + fetch_interval: 2s + labels: ["docker:docker://code.forgejo.org/oci/node:20-bookworm"] + +cache: + enabled: false + dir: "" + host: "" + port: 0 + +container: + network: "bridge" + privileged: false + options: + workdir_parent: + valid_volumes: ["/srv/example"] + docker_host: "" + force_rebuild: false + +host: + workdir_parent: diff --git a/actions/example-force-rebuild/teardown.sh b/actions/example-force-rebuild/teardown.sh new file mode 100755 index 0000000..b410c51 --- /dev/null +++ b/actions/example-force-rebuild/teardown.sh @@ -0,0 +1 @@ +forgejo-runner.sh reload diff --git a/actions/example-if-cancel/.forgejo/workflows/test.yml b/actions/example-if-cancel/.forgejo/workflows/test.yml index 5a92198..d680017 100644 --- a/actions/example-if-cancel/.forgejo/workflows/test.yml +++ b/actions/example-if-cancel/.forgejo/workflows/test.yml @@ -1,9 +1,3 @@ -# -# As of Forgejo v1.20 running this example would require using the web -# endpoints because there is no API to do the same. -# -# It was manually tested to **not work** with Forgejo v1.21 & runner 3.0.1 -# on: [push] jobs: diff --git a/actions/example-local-action/.forgejo/local-action/action.yml b/actions/example-local-action/.forgejo/local-action/action.yml index d83d33c..7963757 100644 --- a/actions/example-local-action/.forgejo/local-action/action.yml +++ b/actions/example-local-action/.forgejo/local-action/action.yml @@ -13,4 +13,10 @@ outputs: runs: using: "composite" steps: + - name: GITHUB_ACTION_PATH + run: | + set -x + test "$(basename $GITHUB_ACTION_PATH)" = local-action + test "$GITHUB_ACTION_PATH" = "${{ env.GITHUB_ACTION_PATH }}" + - run: echo key=${{ inputs.input-two-required }} >> $GITHUB_OUTPUT diff --git a/actions/example-post-7-0-schedule/.forgejo/workflows/test.yml b/actions/example-post-7-0-schedule/.forgejo/workflows/test.yml new file mode 100644 index 0000000..135364a --- /dev/null +++ b/actions/example-post-7-0-schedule/.forgejo/workflows/test.yml @@ -0,0 +1,23 @@ +on: + schedule: + - cron: '* * * * *' + +jobs: + test: + runs-on: ${{ vars.TEST_SCHEDULE_RUNSON }} + container: + image: code.forgejo.org/oci/debian:bookworm + options: "--volume /srv/example:/srv/example" + + steps: + - name: save context + run: | + d=/srv/example/post-7-0-schedule/contexts/$GITHUB_EVENT_NAME + mkdir -p $d + cat > $d/github <<'EOF' + ${{ toJSON(github) }} + EOF + + - run: | + echo "TEST_SCHEDULE_RUNSON=${{ vars.TEST_SCHEDULE_RUNSON }}" + touch /srv/example/post-7-0-schedule-volume/DONE diff --git a/actions/example-post-7-0-schedule/run.sh b/actions/example-post-7-0-schedule/run.sh new file mode 100755 index 0000000..29617c1 --- /dev/null +++ b/actions/example-post-7-0-schedule/run.sh @@ -0,0 +1,30 @@ +forgejo-test-helper.sh push_workflow actions/example-$example $url root example-$example setup-forgejo $token + +forgejo-curl.sh web -X POST http://${HOST_PORT}/admin/actions/variables/1/delete || true +forgejo-curl.sh web --form name=TEST_SCHEDULE_RUNSON --form data=docker http://${HOST_PORT}/admin/actions/variables/new + +# +# Verify that creating a new branch with the same SHA as the default branch +# does not change the ref associated with the schedule +# +# See https://codeberg.org/forgejo/forgejo/pulls/1941 for more information +# +function verify_ref() { + local ref=$(sqlite3 $DIR/forgejo-work-path/forgejo.db 'select ref from action_schedule') + test "${ref##*/}" = "main" +} +verify_ref +api=$url/api/v1 +forgejo-curl.sh api_json --data '{"new_branch_name":"zzzz"}' $api/repos/root/example-post-7-0-schedule/branches +verify_ref + +# runs once per minute, give it three minutes max before declaring failure +if ! RETRY_DELAYS="30 30 30 30 30 30" forgejo.sh retry test -f /srv/example/post-7-0-schedule-volume/DONE; then + cat $FORGEJO_RUNNER_LOGS + false +fi + +c=/srv/example/post-7-0-schedule/contexts/schedule/github +cat $c +test "schedule" = "$(jq -r .event_name <$c)" +test "schedule" = "$(jq -r .event.action <$c)" diff --git a/actions/example-post-7-0-schedule/setup.sh b/actions/example-post-7-0-schedule/setup.sh new file mode 100755 index 0000000..5592dbe --- /dev/null +++ b/actions/example-post-7-0-schedule/setup.sh @@ -0,0 +1 @@ +mkdir -p /srv/example/post-7-0-schedule-volume diff --git a/actions/example-pull-request/.forgejo/workflows/test.yml b/actions/example-pull-request/.forgejo/workflows/test.yml index 90e9fb4..772a38f 100644 --- a/actions/example-pull-request/.forgejo/workflows/test.yml +++ b/actions/example-pull-request/.forgejo/workflows/test.yml @@ -10,7 +10,7 @@ jobs: runs-on: docker container: image: code.forgejo.org/oci/node:20-bookworm - options: "--volume /srv/example-pull-request:/srv/example-pull-request" + options: "--volume /srv/example:/srv/example" steps: - name: setup @@ -33,6 +33,24 @@ jobs: echo value=true >> $GITHUB_OUTPUT fi + # See also actions/example-context/.forgejo/workflows/test.yml + - name: env.GITHUB_BASE_REF + run: | + set -x + test "$GITHUB_BASE_REF" = main + test "$GITHUB_BASE_REF" = "${{ env.GITHUB_BASE_REF }}" + + # See also actions/example-context/.forgejo/workflows/test.yml + - name: env.GITHUB_HEAD_REF + run: | + set -x + if ${{ steps.forked.outputs.value }} ; then + test "$GITHUB_HEAD_REF" = main + else + test "$GITHUB_HEAD_REF" = other + fi + test "$GITHUB_HEAD_REF" = "${{ env.GITHUB_HEAD_REF }}" + - name: secrets shell: bash run: | @@ -115,10 +133,10 @@ jobs: - name: save event run: | - d=/srv/example-pull-request/${{ github.event.pull_request.head.repo.owner.username }}/$GITHUB_EVENT_NAME/${{ github.event.action }} + d=/srv/example/pull-request/contexts/${{ github.event.pull_request.head.repo.owner.username }}/$GITHUB_EVENT_NAME mkdir -p $d - cat > $d/event <<'EOF' - ${{ toJSON(github.event) }} + cat > $d/github <<'EOF' + ${{ toJSON(github) }} EOF - uses: https://code.forgejo.org/actions/checkout@v4 diff --git a/actions/example-pull-request/assert-contexts.sh b/actions/example-pull-request/assert-contexts.sh new file mode 100755 index 0000000..83cd660 --- /dev/null +++ b/actions/example-pull-request/assert-contexts.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -ex + +c=$d/contexts + +test opened = "$(jq -r .event.action <$c/fork-org/pull_request/github)" +test opened = "$(jq -r .event.action <$c/fork-org/pull_request_target/github)" + +test opened = "$(jq -r .event.action <$c/root/pull_request/github)" +test opened = "$(jq -r .event.action <$c/root/pull_request_target/github)" diff --git a/actions/example-pull-request/assert-token.sh b/actions/example-pull-request/assert-token.sh deleted file mode 100755 index daa47d3..0000000 --- a/actions/example-pull-request/assert-token.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -set -ex - -test -d $d/fork-org/pull_request/opened -test -d $d/fork-org/pull_request_target/opened -test -d $d/root/pull_request/opened -test -d $d/root/pull_request_target/opened diff --git a/actions/example-pull-request/run.sh b/actions/example-pull-request/run.sh index 2a88afd..de54e37 100755 --- a/actions/example-pull-request/run.sh +++ b/actions/example-pull-request/run.sh @@ -1,59 +1,56 @@ api=$url/api/v1 -export d=/srv/example-pull-request +export d=/srv/example/pull-request PROOF='some proof' -function setup() { +function main() { + mkdir -p $d + forgejo-test-helper.sh push_workflow actions/example-$example $url root example-$example setup-forgejo $token forgejo-curl.sh api_json --data-raw '{"username":"fork-org"}' $api/orgs forgejo-curl.sh api_json --data-raw '{"organization":"fork-org"}' $api/repos/root/example-pull-request/forks forgejo-curl.sh api_json -X PUT --data-raw '{"data":"AAAA"}' $api/repos/root/example-pull-request/actions/secrets/SECRET - ( - cd $d - git clone $url/fork-org/example-pull-request fork - cd fork - git config user.email root@example.com - git config user.name username - echo fork $PROOF >> README - touch file-unique-to-the-pr-branch - git add . - git commit -m 'fork change' - git push + cd $d + git clone $url/fork-org/example-pull-request fork + cd fork + git config user.email root@example.com + git config user.name username + echo fork $PROOF >>README + touch file-unique-to-the-pr-branch + git add . + git commit -m 'fork change' + git push ) forgejo.sh retry forgejo-curl.sh api_json --data-raw '{"title":"PR from fork","base":"main","head":"fork-org:main"}' $api/repos/root/example-pull-request/pulls ( - cd $d - git clone $url/root/example-pull-request - cd example-pull-request - git checkout -b other - git config user.email root@example.com - git config user.name username - touch file-unique-to-the-pr-branch - echo other $PROOF >> README - git add . - git commit -m 'other change' - git push --force -u origin other + cd $d + git clone $url/root/example-pull-request + cd example-pull-request + git checkout -b other + git config user.email root@example.com + git config user.name username + touch file-unique-to-the-pr-branch + echo other $PROOF >>README + git add . + git commit -m 'other change' + git push --force -u origin other ) forgejo.sh retry forgejo-curl.sh api_json --data-raw '{"title":"PR same repo","base":"main","head":"other"}' $api/repos/root/example-pull-request/pulls export RETRY_DELAYS="60 60 60 60 60 60 60" - for assert in $EXAMPLE_DIR/assert-*.sh ; do - if ! forgejo.sh retry $assert ; then - find $d - cat $FORGEJO_RUNNER_LOGS - false - fi + for assert in $EXAMPLE_DIR/assert-*.sh; do + if ! forgejo.sh retry $assert; then + find $d + sed -e 's/^/[RUNNER LOGS]/' <$FORGEJO_RUNNER_LOGS + false + fi done } -function main() { - setup -} - main diff --git a/actions/example-pull-request/setup.sh b/actions/example-pull-request/setup.sh index e93087e..5f46962 100755 --- a/actions/example-pull-request/setup.sh +++ b/actions/example-pull-request/setup.sh @@ -1,8 +1 @@ -if test $(id -u) != 0 ; then - SUDO=sudo -fi - -$SUDO rm -fr /srv/example-pull-request/{root,fork-org} -rm -fr /srv/example-pull-request -mkdir -p /srv/example-pull-request -FORGEJO_RUNNER_CONFIG=$EXAMPLE_DIR/runner-config.yaml forgejo-runner.sh reload +mkdir -p /srv/example/pull-request diff --git a/actions/example-push-cancel/run.sh b/actions/example-push-cancel/run.sh index 37ed9d3..891798e 100755 --- a/actions/example-push-cancel/run.sh +++ b/actions/example-push-cancel/run.sh @@ -11,8 +11,8 @@ forgejo-test-helper.sh wait_running $url $repo $sha # # push to the same branch # -forgejo-test-helper.sh push_workflow actions/example-$example $url root example-$example setup-forgejo $token +forgejo-test-helper.sh push_workflow actions/example-echo $url root example-$example setup-forgejo $token # -# wait for the workflow to be canceld as a result of the previous push +# wait for the workflow to be canceled as a result of the previous push # forgejo-test-helper.sh wait_failure $url $repo $sha 'Has been cancelled' diff --git a/actions/example-push/.forgejo/workflows/test.yml b/actions/example-push/.forgejo/workflows/test.yml new file mode 100644 index 0000000..89763f2 --- /dev/null +++ b/actions/example-push/.forgejo/workflows/test.yml @@ -0,0 +1,23 @@ +on: + push: + branches: + - 'mai*' + paths: + - '**/test.yml' + +jobs: + test: + runs-on: docker + container: + image: code.forgejo.org/oci/node:20-bookworm + volumes: + - /srv/example:/srv/example + steps: + + - name: save event + run: | + d=/srv/example/push/contexts/$GITHUB_EVENT_NAME + mkdir -p $d + cat > $d/github <<'EOF' + ${{ toJSON(github) }} + EOF diff --git a/actions/example-schedule-noncancel/.forgejo/workflows/schedule_continue.yml b/actions/example-schedule-noncancel/.forgejo/workflows/schedule_continue.yml new file mode 100644 index 0000000..58156ea --- /dev/null +++ b/actions/example-schedule-noncancel/.forgejo/workflows/schedule_continue.yml @@ -0,0 +1,15 @@ +on: + schedule: + - cron: "* * * * *" +jobs: + test: + runs-on: docker + container: + image: code.forgejo.org/oci/debian:bookworm + volumes: + - /srv/example:/srv/example + steps: + - run: | + while ! [ -f /srv/example/schedule-noncancel/PUSHED ]; do + sleep 3 + done diff --git a/actions/example-schedule-noncancel/run.sh b/actions/example-schedule-noncancel/run.sh new file mode 100755 index 0000000..b3756bf --- /dev/null +++ b/actions/example-schedule-noncancel/run.sh @@ -0,0 +1,99 @@ +repo=root/example-$example +# +# delete the repository +# +api=$url/api/v1 +if forgejo-curl.sh api_json -X GET $api/repos/root/example-$example; then + forgejo-curl.sh api_json -X DELETE $api/repos/root/example-$example +fi + +# +# push the repository +# +forgejo-test-helper.sh push_workflow actions/example-$example $url root example-$example setup-forgejo $token + +# +# get the run id of the workflow that just started +# +getScheduleRun() { + rm -f $DIR/forgejo-work-path/forgejo.copy.db + cp $DIR/forgejo-work-path/forgejo.db $DIR/forgejo-work-path/forgejo.copy.db + sqlite3 $DIR/forgejo-work-path/forgejo.copy.db \ + "pragma busy_timeout = 5000; \ + select action_run.id \ + from action_run \ + inner join action_schedule on action_run.schedule_id = action_schedule.id \ + inner join repository on action_schedule.repo_id = repository.id \ + where repository.name = 'example-schedule-noncancel' \ + order by action_run.created desc limit 1" | sed '2q;d' +} + +run_id=$(getScheduleRun) +while [ -z $run_id ]; do + echo waiting 5... + sleep 5 + run_id=$(getScheduleRun) +done + +echo Schedule run id: $run_id + +# +# Wait for it to be started +# +checkStarted() { + rm -f $DIR/forgejo-work-path/forgejo.copy.db + cp $DIR/forgejo-work-path/forgejo.db $DIR/forgejo-work-path/forgejo.copy.db + sqlite3 $DIR/forgejo-work-path/forgejo.copy.db \ + "pragma busy_timeout = 5000; \ + select id \ + from action_run \ + where id = $run_id \ + and started is not null" | sed '2q;d' +} + +started_check=$(checkStarted) +while [ -z $started_check ]; do + echo waiting 2... + sleep 2 + started_check=$(checkStarted) +done + +echo Run has started +echo Push to repo again + +# +# Push to the repo again +# +forgejo-test-helper.sh push_workflow actions/example-echo $url root example-$example setup-forgejo $token + +echo Signal to the workflow that the push has happened +mkdir -p /srv/example/schedule-noncancel +touch /srv/example/schedule-noncancel/PUSHED + +# +# Wait for the workflow to finish anyway +# +echo Wait for workflow to finish + +checkFinished() { + rm -f $DIR/forgejo-work-path/forgejo.copy.db + cp $DIR/forgejo-work-path/forgejo.db $DIR/forgejo-work-path/forgejo.copy.db + sqlite3 $DIR/forgejo-work-path/forgejo.copy.db \ + "pragma busy_timeout = 5000; \ + select status \ + from action_run \ + where id = $run_id \ + and (status != 6 and status != 5)" | sed '2q;d' +} + +finished_status=$(checkFinished) +while [ -z $finished_status ]; do + echo waiting 5... + sleep 5 + finished_status=$(checkFinished) +done + +echo Workflow finished. +rm -f $DIR/forgejo-work-path/forgejo.copy.db + +test $finished_status = 1 diff --git a/actions/example-schedule-noncancel/teardown.sh b/actions/example-schedule-noncancel/teardown.sh new file mode 100644 index 0000000..ae9987a --- /dev/null +++ b/actions/example-schedule-noncancel/teardown.sh @@ -0,0 +1,4 @@ +# +# this will effectively discard any linger workflow so they do not interfere with other tests +# +forgejo-runner.sh reload diff --git a/actions/example-service/.forgejo/workflows/test.yml b/actions/example-service/.forgejo/workflows/test.yml index 438203c..014653a 100644 --- a/actions/example-service/.forgejo/workflows/test.yml +++ b/actions/example-service/.forgejo/workflows/test.yml @@ -2,7 +2,23 @@ on: [push] jobs: # - # No volume involved + # No volume involved & the container is implicit + # + simple-no-container: + runs-on: docker + services: + pgsql: + image: code.forgejo.org/oci/postgres:15 + env: + POSTGRES_DB: test + POSTGRES_PASSWORD: postgres + steps: + - run: | + apt-get update -qq + apt-get install -y -qq postgresql-client + PGPASSWORD=postgres psql -h pgsql -U postgres -c '\dt' test + # + # No volume involved & the container is explicit # simple: runs-on: docker diff --git a/actions/example-service/runner-config.yaml b/actions/example-service/runner-config.yaml index 4606852..c931a24 100644 --- a/actions/example-service/runner-config.yaml +++ b/actions/example-service/runner-config.yaml @@ -1,6 +1,6 @@ log: - level: info + level: debug runner: file: .runner @@ -10,7 +10,7 @@ runner: insecure: false fetch_timeout: 5s fetch_interval: 2s - labels: ["docker:docker://code.forgejo.org/oci/node:16-bullseye"] + labels: ["docker:docker://code.forgejo.org/oci/node:20-bookworm"] cache: enabled: false diff --git a/actions/example-service/setup.sh b/actions/example-service/setup.sh index 3a9674e..7bc1672 100755 --- a/actions/example-service/setup.sh +++ b/actions/example-service/setup.sh @@ -1,3 +1,3 @@ -> /srv/example-service-volume-valid -> /srv/example-service-volume-invalid +>/srv/example-service-volume-valid +>/srv/example-service-volume-invalid FORGEJO_RUNNER_CONFIG=$EXAMPLE_DIR/runner-config.yaml forgejo-runner.sh reload diff --git a/actions/example-tag/.forgejo/workflows/test.yml b/actions/example-tag/.forgejo/workflows/test.yml new file mode 100644 index 0000000..c7e1357 --- /dev/null +++ b/actions/example-tag/.forgejo/workflows/test.yml @@ -0,0 +1,21 @@ +on: + push: + tags: + - 'v*' + +jobs: + test: + runs-on: docker + container: + image: code.forgejo.org/oci/node:20-bookworm + volumes: + - /srv/example:/srv/example + steps: + + - name: save event + run: | + d=/srv/example/tag/contexts/$GITHUB_EVENT_NAME + mkdir -p $d + cat > $d/github <<'EOF' + ${{ toJSON(github) }} + EOF diff --git a/actions/example-tag/run.sh b/actions/example-tag/run.sh new file mode 100755 index 0000000..001c303 --- /dev/null +++ b/actions/example-tag/run.sh @@ -0,0 +1,19 @@ +export d=/srv/example/tag + +function main() { + mkdir -p $d + + local repo=root/example-$example + + forgejo-test-helper.sh push_workflow actions/example-$example $url root example-$example setup-forgejo $token + local sha=$(forgejo-test-helper.sh branch_tip $url $repo main) + + local api=$url/api/v1 + forgejo-curl.sh api_json --data-raw '{"tag_name":"v1.1","target":"'$sha'"}' $api/repos/$repo/tags + + forgejo-test-helper.sh wait_success $url $repo $sha + + test -f /srv/example/tag/contexts/push/github +} + +main diff --git a/actions/example-with-docker-host/.forgejo/workflows/test.yml b/actions/example-with-docker-host/.forgejo/workflows/test.yml new file mode 100644 index 0000000..6089092 --- /dev/null +++ b/actions/example-with-docker-host/.forgejo/workflows/test.yml @@ -0,0 +1,10 @@ +on: [push] + +jobs: + build: + runs-on: docker + container: + image: code.forgejo.org/oci/docker:cli + steps: + - run: ls -l /var/run/docker.sock + - run: docker ps diff --git a/actions/example-with-docker-host/runner-config.yaml b/actions/example-with-docker-host/runner-config.yaml new file mode 100644 index 0000000..839de20 --- /dev/null +++ b/actions/example-with-docker-host/runner-config.yaml @@ -0,0 +1,30 @@ + +log: + level: debug + +runner: + file: .runner + capacity: 1 + env_file: .env + timeout: 3h + insecure: false + fetch_timeout: 5s + fetch_interval: 2s + labels: ["docker:docker://code.forgejo.org/oci/node:20-bookworm"] + +cache: + enabled: true + dir: "/srv/example/cache" + host: "" + port: 0 + +container: + network: "bridge" + privileged: false + options: + workdir_parent: + valid_volumes: ["/srv/example"] + docker_host: "unix:///var/run/docker.sock" + +host: + workdir_parent: diff --git a/actions/example-with-docker-host/setup.sh b/actions/example-with-docker-host/setup.sh new file mode 100755 index 0000000..9c1f5ad --- /dev/null +++ b/actions/example-with-docker-host/setup.sh @@ -0,0 +1 @@ +FORGEJO_RUNNER_CONFIG=$EXAMPLE_DIR/runner-config.yaml forgejo-runner.sh reload diff --git a/actions/example-with-docker-host/teardown.sh b/actions/example-with-docker-host/teardown.sh new file mode 100755 index 0000000..b410c51 --- /dev/null +++ b/actions/example-with-docker-host/teardown.sh @@ -0,0 +1 @@ +forgejo-runner.sh reload diff --git a/actions/example-with-docker-socket/.forgejo/workflows/test.yml b/actions/example-with-docker-socket/.forgejo/workflows/test.yml new file mode 100644 index 0000000..6089092 --- /dev/null +++ b/actions/example-with-docker-socket/.forgejo/workflows/test.yml @@ -0,0 +1,10 @@ +on: [push] + +jobs: + build: + runs-on: docker + container: + image: code.forgejo.org/oci/docker:cli + steps: + - run: ls -l /var/run/docker.sock + - run: docker ps diff --git a/actions/example-with-docker-socket/runner-config.yaml b/actions/example-with-docker-socket/runner-config.yaml new file mode 100644 index 0000000..e09edaa --- /dev/null +++ b/actions/example-with-docker-socket/runner-config.yaml @@ -0,0 +1,30 @@ + +log: + level: debug + +runner: + file: .runner + capacity: 1 + env_file: .env + timeout: 3h + insecure: false + fetch_timeout: 5s + fetch_interval: 2s + labels: ["docker:docker://code.forgejo.org/oci/node:20-bookworm"] + +cache: + enabled: true + dir: "/srv/example/cache" + host: "" + port: 0 + +container: + network: "bridge" + privileged: false + options: + workdir_parent: + valid_volumes: ["/srv/example"] + docker_host: "automount" + +host: + workdir_parent: diff --git a/actions/example-with-docker-socket/setup.sh b/actions/example-with-docker-socket/setup.sh new file mode 100755 index 0000000..9c1f5ad --- /dev/null +++ b/actions/example-with-docker-socket/setup.sh @@ -0,0 +1 @@ +FORGEJO_RUNNER_CONFIG=$EXAMPLE_DIR/runner-config.yaml forgejo-runner.sh reload diff --git a/actions/example-with-docker-socket/teardown.sh b/actions/example-with-docker-socket/teardown.sh new file mode 100755 index 0000000..b410c51 --- /dev/null +++ b/actions/example-with-docker-socket/teardown.sh @@ -0,0 +1 @@ +forgejo-runner.sh reload diff --git a/actions/example-with-docker-tcp/.forgejo/workflows/test.yml b/actions/example-with-docker-tcp/.forgejo/workflows/test.yml new file mode 100644 index 0000000..c33c2e7 --- /dev/null +++ b/actions/example-with-docker-tcp/.forgejo/workflows/test.yml @@ -0,0 +1,11 @@ +on: [push] + +jobs: + build: + runs-on: docker + container: + image: code.forgejo.org/oci/node:20-bookworm + steps: + - run: | + ! test -e /var/run/docker.sock + ! env | grep DOCKER_HOST diff --git a/actions/example-with-docker-tcp/runner-config.yaml b/actions/example-with-docker-tcp/runner-config.yaml new file mode 100644 index 0000000..730d9c8 --- /dev/null +++ b/actions/example-with-docker-tcp/runner-config.yaml @@ -0,0 +1,30 @@ + +log: + level: debug + +runner: + file: .runner + capacity: 1 + env_file: .env + timeout: 3h + insecure: false + fetch_timeout: 5s + fetch_interval: 2s + labels: ["docker:docker://code.forgejo.org/oci/node:20-bookworm"] + +cache: + enabled: true + dir: "/srv/example/cache" + host: "" + port: 0 + +container: + network: "bridge" + privileged: false + options: + workdir_parent: + valid_volumes: ["/srv/example"] + docker_host: "tcp://127.0.0.1:2375" + +host: + workdir_parent: diff --git a/actions/example-with-docker-tcp/setup.sh b/actions/example-with-docker-tcp/setup.sh new file mode 100755 index 0000000..9c1f5ad --- /dev/null +++ b/actions/example-with-docker-tcp/setup.sh @@ -0,0 +1 @@ +FORGEJO_RUNNER_CONFIG=$EXAMPLE_DIR/runner-config.yaml forgejo-runner.sh reload diff --git a/actions/example-with-docker-tcp/teardown.sh b/actions/example-with-docker-tcp/teardown.sh new file mode 100755 index 0000000..b410c51 --- /dev/null +++ b/actions/example-with-docker-tcp/teardown.sh @@ -0,0 +1 @@ +forgejo-runner.sh reload diff --git a/actions/example-without-docker-socket/.forgejo/workflows/test.yml b/actions/example-without-docker-socket/.forgejo/workflows/test.yml new file mode 100644 index 0000000..8d5c837 --- /dev/null +++ b/actions/example-without-docker-socket/.forgejo/workflows/test.yml @@ -0,0 +1,6 @@ +on: [push] +jobs: + test: + runs-on: docker + steps: + - run: "! test -e /var/run/docker.sock" diff --git a/actions/example-workflow-dispatch/.forgejo/workflows/test.yml b/actions/example-workflow-dispatch/.forgejo/workflows/test.yml new file mode 100644 index 0000000..186fe56 --- /dev/null +++ b/actions/example-workflow-dispatch/.forgejo/workflows/test.yml @@ -0,0 +1,57 @@ +on: + workflow_dispatch: + inputs: + logLevel: + description: 'Log Level' + required: true + default: 'warning' + type: choice + options: + - info + - warning + - debug + tags: + description: 'Test scenario tags' + required: false + type: boolean + boolean_default_true: + description: 'Test scenario tags' + required: true + type: boolean + default: true + boolean_default_false: + description: 'Test scenario tags' + required: false + type: boolean + default: false + number1_default: + description: 'Number w. default' + default: '100' + type: number + number2: + description: 'Number w/o. default' + type: number + string1_default: + description: 'String w. default' + default: 'Hello world' + type: string + string2: + description: 'String w/o. default' + required: true + type: string + +jobs: + test: + runs-on: docker + container: + image: code.forgejo.org/oci/debian:bookworm + options: "--volume /srv/example:/srv/example" + + steps: + - name: save and display context + run: | + d=/srv/example/workflow-dispatch/contexts/$GITHUB_EVENT_NAME + mkdir -p $d + tee $d/github <<'EOF' + ${{ toJSON(github) }} + EOF diff --git a/actions/example-workflow-dispatch/run.sh b/actions/example-workflow-dispatch/run.sh new file mode 100755 index 0000000..8a49365 --- /dev/null +++ b/actions/example-workflow-dispatch/run.sh @@ -0,0 +1,62 @@ +TMPDIR=$(mktemp -d) + +trap "rm -fr $TMPDIR" EXIT + +source $EXAMPLE_DIR/../../lib/lib.sh + +export d=/srv/example/tag +context=/srv/example/workflow-dispatch/contexts/workflow_dispatch/github +api=$url/api/v1 +repo=root/example-$example + +function context_wait() { + if ! forgejo.sh retry test -f $context; then + cat "$FORGEJO_RUNNER_LOGS" + false + fi +} + +function verify_required() { + local actual=$(forgejo-curl.sh api_json -w '%{http_code}' --data '{"ref":"main","inputs":{}}' $api/repos/$repo/actions/workflows/test.yml/dispatches) + local expected=400 + if test "$actual" != $expected; then + log_info "dispatch is expected to fail with status $expected because of string2 is a required value but got status $actual instead" + return 1 + fi +} + +function verify_inputs() { + local inputs='{"string2":"value2"}' + forgejo-curl.sh api_json --data '{"ref":"main","inputs":'$inputs'}' $api/repos/$repo/actions/workflows/test.yml/dispatches + cat >$TMPDIR/expected <<'EOF' + { + "boolean_default_false": "false", + "boolean_default_true": "true", + "logLevel": "warning", + "number1_default": "100", + "string1_default": "Hello world", + "string2": "value2" + } +EOF +} + +function run_tests() { + verify_required + + npm --silent install json-diff + verify_inputs + context_wait + node_modules/.bin/json-diff <(jq .event.inputs <$context) $TMPDIR/expected +} + +function main() { + mkdir -p $d + + forgejo-test-helper.sh push_workflow actions/example-$example $url root example-$example setup-forgejo $token + + run_tests + + test "workflow_dispatch" = "$(jq -r .event_name <$context)" +} + +main diff --git a/actions/example-workflow-dispatch/setup.sh b/actions/example-workflow-dispatch/setup.sh new file mode 100755 index 0000000..402b08c --- /dev/null +++ b/actions/example-workflow-dispatch/setup.sh @@ -0,0 +1 @@ +mkdir -p /srv/example/workflow-dispatch diff --git a/actions/run.sh b/actions/run.sh deleted file mode 100755 index 717682d..0000000 --- a/actions/run.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/bash - -set -e - -function run() { - local example=$1 - - export example - export EXAMPLE_DIR=$(pwd)/actions/example-$example - - if test -f $EXAMPLE_DIR/setup.sh ; then - echo "============================ SETUP example-$example ===================" - bash -ex $EXAMPLE_DIR/setup.sh || return 1 - fi - - if test -f $EXAMPLE_DIR/run.sh ; then - echo "============================ RUN example-$example ===================" - bash -ex $EXAMPLE_DIR/run.sh || return 1 - else - forgejo-test-helper.sh run_workflow actions/example-$example $url root example-$example setup-forgejo $token || return 1 - fi - - if test -f $EXAMPLE_DIR/teardown.sh ; then - echo "============================ TEARDOWN example-$example ===================" - bash -ex $EXAMPLE_DIR/teardown.sh || return 1 - fi -} - -function examples_v1_20() { - echo 'echo checkout service container expression local-action docker-action if if-fail' -} - -function examples_v1_21() { - # keep "cron" last otherwise it will linger and pollute the following runs - echo 'echo push-cancel artifacts service checkout pull-request container expression local-action docker-action if if-fail cron' -} - -function examples_v1_22() { - examples_v1_21 -} - -function setup() { - local binary=$1 - forgejo-binary.sh setup root admin1234 $binary - forgejo-runner.sh setup -} - -function teardown() { - forgejo-curl.sh logout - forgejo-runner.sh teardown - forgejo-binary.sh teardown -} - -function main() { - local binary="$1" - shift - export version="$1" - shift - - export DOT=$DIR/forgejo-curl - - teardown - - if test "$#" = 0 ; then - examples=$(examples_$version) - else - examples="$@" - fi - - if test "$examples" = "none" ; then - exit 0 - fi - - setup $binary - - if ! test -f "$DIR/forgejo-auth-url" ; then - echo "DIR=$DIR must be a directory with a forgejo-auth-url file" - fi - - export FORGEJO_RUNNER_LOGS=$DIR/forgejo-runner.log - export url=$(cat $DIR/forgejo-auth-url) - export token=$(cat $DIR/forgejo-token) - - for example in $examples ; do - echo "======================== BEGIN example-$example ===================" - if ! time run $example >& /tmp/run.out ; then - cat /tmp/run.out - echo "======================== FAIL example-$example ===================" - sleep 5 # hack for Forgejo v1.21 to workaround a bug by which the last lines of the output are moved to the next step - false - fi - echo "======================== END example-$example ===================" - done -} - -main "$@" diff --git a/actions/example-pull-request/runner-config.yaml b/actions/runner-config.yaml similarity index 86% rename from actions/example-pull-request/runner-config.yaml rename to actions/runner-config.yaml index 5c3a6b3..7d9e764 100644 --- a/actions/example-pull-request/runner-config.yaml +++ b/actions/runner-config.yaml @@ -1,6 +1,5 @@ - log: - level: info + level: debug runner: file: .runner @@ -23,7 +22,7 @@ container: privileged: false options: workdir_parent: - valid_volumes: ["/srv/example-pull-request"] + valid_volumes: ["/srv/example"] docker_host: "" host: diff --git a/end-to-end.sh b/end-to-end.sh new file mode 100755 index 0000000..3b3a702 --- /dev/null +++ b/end-to-end.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT + +SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SELF="${BASH_SOURCE[0]}" +source $SELF_DIR/lib/lib.sh + +source $SELF_DIR/federation/federation.sh +source $SELF_DIR/actions/actions.sh +source $SELF_DIR/forgejo/fixtures.sh +source $SELF_DIR/storage/storage.sh +source $SELF_DIR/upgrade/upgrade.sh +source $SELF_DIR/packages/packages.sh + +"$@" diff --git a/federation/ONE-app.ini b/federation/ONE-app.ini new file mode 100644 index 0000000..4dcdacf --- /dev/null +++ b/federation/ONE-app.ini @@ -0,0 +1,33 @@ +RUN_MODE = prod +WORK_PATH = forgejo-ONE + +[server] +APP_DATA_PATH = ${WORK_PATH}/data +DOMAIN = ${IP} +HTTP_PORT = 3001 +SSH_LISTEN_PORT = 2201 + +[queue] +TYPE = immediate + +[database] +DB_TYPE = sqlite3 +PATH = ${WORK_PATH}/forgejo.db + +[log] +MODE = file +LEVEL = trace +ROUTER = file + +[log.file] +FILE_NAME = forgejo.log + +[security] +INSTALL_LOCK = true + +[repository] +ENABLE_PUSH_CREATE_USER = true +DEFAULT_PUSH_CREATE_PRIVATE = false + +[federation] +ENABLED = true diff --git a/federation/TWO-app.ini b/federation/TWO-app.ini new file mode 100644 index 0000000..656588a --- /dev/null +++ b/federation/TWO-app.ini @@ -0,0 +1,33 @@ +RUN_MODE = prod +WORK_PATH = forgejo-TWO + +[server] +APP_DATA_PATH = ${WORK_PATH}/data +DOMAIN = ${IP} +HTTP_PORT = 3002 +SSH_LISTEN_PORT = 2202 + +[queue] +TYPE = immediate + +[database] +DB_TYPE = sqlite3 +PATH = ${WORK_PATH}/forgejo.db + +[log] +MODE = file +LEVEL = trace +ROUTER = file + +[log.file] +FILE_NAME = forgejo.log + +[security] +INSTALL_LOCK = true + +[repository] +ENABLE_PUSH_CREATE_USER = true +DEFAULT_PUSH_CREATE_PRIVATE = false + +[federation] +ENABLED = true diff --git a/federation/federation.sh b/federation/federation.sh new file mode 100755 index 0000000..e3e7ab7 --- /dev/null +++ b/federation/federation.sh @@ -0,0 +1,86 @@ +# Copyright 2024 The Forgejo Authors +# SPDX-License-Identifier: MIT + +FEDERATION_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +export FEDERATION_INSTANCES="ONE TWO" +export FEDERATION_CONFIGS + +function federation_setup_variables() { + if test "$FEDERATION_CONFIGS"; then + return + fi + for instance in $FEDERATION_INSTANCES; do + local config=$FEDERATION_DIR/$instance-app.ini + FEDERATION_CONFIGS="$FEDERATION_CONFIGS $config" + local base=$(work_path_base $config) + local work_path=$DIR/$base + local host_port=$(get_host_port $config) + + eval export ${instance}_CONFIG=$config + eval export ${instance}_CURL=$work_path/forgejo-curl.sh + eval export ${instance}_HOST_PORT=$host_port + done +} + +function federation_verify_scenario() { + local scenario=$1 + + export scenario + export SCENARIO_DIR=$FEDERATION_DIR/scenario-$scenario + + if test -f $SCENARIO_DIR/setup.sh; then + echo "============================ SETUP scenario-$scenario ===================" + bash -ex $SCENARIO_DIR/setup.sh || return 1 + fi + + echo "============================ RUN scenario-$scenario ===================" + bash -ex $SCENARIO_DIR/run.sh || return 1 + + if test -f $SCENARIO_DIR/teardown.sh; then + echo "============================ TEARDOWN scenario-$scenario ===================" + bash -ex $SCENARIO_DIR/teardown.sh || return 1 + fi +} + +function federation_setup() { + federation_setup_variables + + local version=$1 + federation_teardown + + local config + for config in $FEDERATION_CONFIGS; do + reset_forgejo $config + start_forgejo $version $config + done +} + +function federation_teardown() { + federation_setup_variables + + local config + for config in $FEDERATION_CONFIGS; do + stop_forgejo $config + done +} + +function test_federation() { + # start_gitlab octobus/heptapod:1.5.3 + federation_setup_variables + + local versions="${1:-$RELEASE_NUMBERS}" + + for version in $versions; do + + if dpkg --compare-versions $version lt 7.1; then + continue + fi + + federation_setup $version + + for scenario in star; do + run federation_verify_scenario $scenario + done + done +} diff --git a/federation/scenario-star/run.sh b/federation/scenario-star/run.sh new file mode 100644 index 0000000..ad340d2 --- /dev/null +++ b/federation/scenario-star/run.sh @@ -0,0 +1,47 @@ +TMPDIR=$(mktemp -d) + +trap "rm -fr $TMPDIR" EXIT + +source $SCENARIO_DIR/../../lib/lib.sh + +function star_count() { + local curl=$1 + local host_port=$2 + local count=$3 + + $curl api_json http://$host_port/api/v1/repos/root/test >$TMPDIR/count.json + if test $count != $(jq -r .stars_count <$TMPDIR/count.json); then + jq . <$TMPDIR/count.json + return 1 + fi +} + +# +# create a repo on each instance +# +$ONE_CURL api_json --data '{"name":"test","auto_init":true}' $ONE_HOST_PORT/api/v1/user/repos >$TMPDIR/one-repo.json +one_repo_id=$(jq -r .id <$TMPDIR/one-repo.json) +$TWO_CURL api_json --data '{"name":"test","auto_init":true}' $TWO_HOST_PORT/api/v1/user/repos >$TMPDIR/two-repo.json +two_repo_id=$(jq -r .id <$TMPDIR/two-repo.json) + +# +# the repo in instance two is federated with the repo in instance one +# +$ONE_CURL web --form action=federation --form following_repos=http://$TWO_HOST_PORT/api/v1/activitypub/repository-id/$two_repo_id http://$ONE_HOST_PORT/root/test/settings + +# +# check that both repo have 0 star +# +star_count $ONE_CURL $ONE_HOST_PORT 0 +star_count $TWO_CURL $TWO_HOST_PORT 0 + +# +# star the repo on instance one and expect the star to show on instance two +# +$ONE_CURL api_json -X PUT $ONE_HOST_PORT/api/v1/user/starred/root/test + +# +# check that both repo have 1 star +# +star_count $ONE_CURL $ONE_HOST_PORT 1 +retry star_count $TWO_CURL $TWO_HOST_PORT 1 diff --git a/federation/scenario-star/setup.sh b/federation/scenario-star/setup.sh new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/federation/scenario-star/setup.sh @@ -0,0 +1 @@ + diff --git a/federation/scenario-star/teardown.sh b/federation/scenario-star/teardown.sh new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/federation/scenario-star/teardown.sh @@ -0,0 +1 @@ + diff --git a/forgejo/build.sh b/forgejo/build.sh deleted file mode 100755 index c78bbdd..0000000 --- a/forgejo/build.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -set -ex - -SELF_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -version=$1 -DIR=$2 - -v=$(echo $version | sed -E -e 's/([0-9]+\.[0-9]+).*/\1/') -read url ref semver < $SELF_DIR/sources/$v - -rm -fr $DIR/src -if [[ "$ref" =~ ^refs/ ]] ; then - git clone --depth 1 $url $DIR/src - cd $DIR/src - git fetch origin +$ref:$ref - git checkout -b $v $ref -else - git clone --depth 1 -b $ref $url $DIR/src - cd $DIR/src -fi -export TAGS="bindata sqlite sqlite_unlock_notify" -make deps-backend backend -# -# use the gitea target here so that branches that do not contain the commit that adds -# the `forgejo` target to the Makefile can build successfully -# -make VERSION=v$version GITEA_VERSION=v$version FORGEJO_VERSION=$semver generate gitea -mv gitea $DIR/forgejo-$version diff --git a/forgejo/fixtures.sh b/forgejo/fixtures.sh new file mode 100644 index 0000000..b49abf5 --- /dev/null +++ b/forgejo/fixtures.sh @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: MIT + +FIXTURES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +source $FIXTURES_DIR/fixtures/storage.sh +source $FIXTURES_DIR/fixtures/doctor.sh diff --git a/forgejo/fixtures/doctor.sh b/forgejo/fixtures/doctor.sh new file mode 100644 index 0000000..c0d8109 --- /dev/null +++ b/forgejo/fixtures/doctor.sh @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: MIT + +function doctor_run() { + local config=$1 + local base=$(work_path_base $config) + local work_path=$DIR/$base + + $work_path/forgejocli doctor check --all # --log-file - +} diff --git a/forgejo/upgrades/fixtures.sh b/forgejo/fixtures/storage.sh similarity index 66% rename from forgejo/upgrades/fixtures.sh rename to forgejo/fixtures/storage.sh index 1b8cb44..80b7c70 100644 --- a/forgejo/upgrades/fixtures.sh +++ b/forgejo/fixtures/storage.sh @@ -1,4 +1,3 @@ -#!/bin/bash # SPDX-License-Identifier: MIT #ONEPIXEL="iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==" @@ -8,20 +7,44 @@ # ONEPIXEL="iVBORw0KGgoAAAANSUhEUgAAASIAAAEiCAYAAABdvt+2AAADrElEQVR4nOzUMRHAMADEsL9eeQd6AsOLhMCT/7udAYS+OgDAiICcEQE5IwJyRgTkjAjIGRGQMyIgZ0RAzoiAnBEBOSMCckYE5IwIyBkRkDMiIGdEQM6IgJwRATkjAnJGBOSMCMgZEZAzIiBnREDOiICcEQE5IwJyRgTkjAjIGRGQMyIgZ0RAzoiAnBEBOSMCckYE5IwIyBkRkDMiIGdEQM6IgJwRATkjAnJGBOSMCMgZEZAzIiBnREDOiICcEQE5IwJyRgTkjAjIGRGQMyIgZ0RAzoiAnBEBOSMCckYE5IwIyBkRkDMiIGdEQM6IgJwRATkjAnJGBOSMCMgZEZAzIiBnREDOiICcEQE5IwJyRgTkjAjIGRGQMyIgZ0RAzoiAnBEBOSMCckYE5IwIyBkRkDMiIGdEQM6IgJwRATkjAnJGBOSMCMgZEZAzIiBnREDOiICcEQE5IwJyRgTkjAjIGRGQMyIgZ0RAzoiAnBEBOSMCckYE5IwIyBkRkDMiIGdEQM6IgJwRATkjAnJGBOSMCMgZEZAzIiBnREDOiICcEQE5IwJyRgTkjAjIGRGQMyIgZ0RAzoiAnBEBOSMCckYE5IwIyBkRkDMiIGdEQM6IgJwRATkjAnJGBOSMCMgZEZAzIiBnREDOiICcEQE5IwJyRgTkjAjIGRGQMyIgZ0RAzoiAnBEBOSMCckYE5IwIyBkRkDMiIGdEQM6IgJwRATkjAnJGBOSMCMgZEZAzIiBnREDOiICcEQE5IwJyRgTkjAjIGRGQMyIgZ0RAzoiAnBEBOSMCckYE5IwIyBkRkDMiIGdEQM6IgJwRATkjAnJGBOSMCMgZEZAzIiBnREDOiICcEQE5IwJyRgTkjAjIGRGQMyIgZ0RAzoiAnBEBOSMCckYE5IwIyBkRkDMiIGdEQM6IgJwRATkjAnJGBOSMCMgZEZAzIiBnREDOiICcEQE5IwJyRgTkjAjIGRGQMyIgZ0RAzoiAnBEBOSMCckYE5IwIyBkRkDMiIGdEQM6IgJwRATkjAnJGBOSMCMgZEZAzIiBnREDOiICcEQE5IwJyRgTkjAjIGRGQMyIgZ0RAzoiAnBEBOSMCckYE5IwIyBkRkDMiIGdEQM6IgJwRATkjAnJGBOSMCMgZEZAzIiBnREDOiICcEQE5IwJyRgTkjAjIGRGQMyIgZ0RAzoiAnBEBOSMCckYE5IwIyBkRkDMiIGdEQM6IgJwRATkjAnJGBOSMCMgZEZAzIiBnREDuBQAA//+4jAPFe1H1tgAAAABJRU5ErkJggg==" +STORAGE_FUN="attachments avatars lfs packages repo_archive repo_avatars" +: ${FORGEJO_REPO:=fixture} + function fixture_get_paths_s3() { local path=$1 ( - echo -n $path/ - mc ls --quiet --recursive testS3/$path | sed -e 's/.* //' - ) > $DIR/path + mc ls --quiet --recursive testS3/$path | sed -e "s|.* |$path/|" + ) >$DIR/path +} + +function fixture_content_search_s3() { + local path="$1" + local expected="$2" + + fixture_get_paths_s3 $path + if test $(wc -l <$DIR/path) -lt 1; then + echo expected at least one but got "'$(cat $DIR/path)'" + return 1 + fi + for filename in $(cat $DIR/path); do + local content=$(mc cat testS3/$filename | base64 -w0) + if test "$content" = "$expected"; then + return 0 + fi + done + echo nothing in $path found with the expected content "$expected" + return 1 } function fixture_get_paths_local() { local path=$1 local work_path=$DIR/forgejo-work-path - ( cd $work_path ; find $path -type f) > $DIR/path + ( + cd $work_path + find $path -type f + ) >$DIR/path } function fixture_get_one_path() { @@ -30,7 +53,7 @@ function fixture_get_one_path() { fixture_get_paths_$storage $path - if test $(wc -l < $DIR/path) != 1 ; then + if test $(wc -l <$DIR/path) != 1; then echo expected one path but got cat $DIR/path return 1 @@ -57,13 +80,25 @@ function fixture_lfs_create() { ( cd $DIR/fixture git lfs track "*.txt" - echo CONTENT > file.txt + echo CONTENT >file.txt git add . git commit -m 'lfs files' git push ) } +function fixture_lfs_assert() { + local d=$(mktemp -d) + ( + git clone http://${FORGEJO_USER}:${FORGEJO_PASSWORD}@${HOST_PORT}/${FORGEJO_USER}/${FORGEJO_REPO} $d/${FORGEJO_REPO} + cd $d/${FORGEJO_REPO} + rm file.txt + git-lfs checkout file.txt + test -f file.txt + ) + rm -fr $d +} + function fixture_lfs_assert_s3() { local content=$(mc cat testS3/forgejo/lfs/d6/1e/5fa787e50330288923bd0c9866b44643925965144262288447cf52f9f9b7) test "$content" = CONTENT @@ -78,9 +113,9 @@ function fixture_lfs_assert_local() { } function fixture_packages_create() { - echo PACKAGE_CONTENT > $DIR/fixture/package - $work_path/forgejo-api -X DELETE http://${HOST_PORT}/api/packages/${FORGEJO_USER}/generic/test_package/1.0.0/file.txt || true - $work_path/forgejo-api --upload-file $DIR/fixture/package http://${HOST_PORT}/api/packages/${FORGEJO_USER}/generic/test_package/1.0.0/file.txt + echo PACKAGE_CONTENT >$DIR/fixture/package + forgejo-curl.sh api_json -X DELETE http://${HOST_PORT}/api/packages/${FORGEJO_USER}/generic/test_package/1.0.0/file.txt || true + forgejo-curl.sh api_json --upload-file $DIR/fixture/package http://${HOST_PORT}/api/packages/${FORGEJO_USER}/generic/test_package/1.0.0/file.txt } function fixture_packages_assert_s3() { @@ -96,14 +131,12 @@ function fixture_packages_assert_local() { } function fixture_avatars_create() { - echo -n $ONEPIXEL | base64 --decode > $DIR/avatar.png - $work_path/forgejo-client --form avatar=@$DIR/avatar.png http://${HOST_PORT}/user/settings/avatar + echo -n $ONEPIXEL | base64 --decode >$DIR/avatar.png + forgejo-curl.sh web --form avatar=@$DIR/avatar.png http://${HOST_PORT}/user/settings/avatar } function fixture_avatars_assert_s3() { - local filename=$(fixture_get_one_path s3 forgejo/avatars) - local content=$(mc cat testS3/$filename | base64 -w0) - test "$content" = "$ONEPIXEL" + fixture_content_search_s3 forgejo/avatars "$ONEPIXEL" } function fixture_avatars_assert_local() { @@ -115,10 +148,8 @@ function fixture_avatars_assert_local() { } function fixture_repo_avatars_create() { - echo -n $ONEPIXEL | base64 --decode > $DIR/repo-avatar.png - $work_path/forgejo-client --form avatar=@$DIR/repo-avatar.png http://${HOST_PORT}/${FORGEJO_USER}/${FORGEJO_REPO}/settings/avatar - # v1.21 only - #$work_path/forgejo-api -X POST --data-raw '{"body":"'$avatar'"}' http://${HOST_PORT}/api/v1/repos/${FORGEJO_USER}/${FORGEJO_REPO}/avatar + echo -n $ONEPIXEL | base64 --decode >$DIR/repo-avatar.png + forgejo-curl.sh web --form avatar=@$DIR/repo-avatar.png http://${HOST_PORT}/${FORGEJO_USER}/${FORGEJO_REPO}/settings/avatar } function fixture_repo_avatars_assert_s3() { @@ -136,18 +167,18 @@ function fixture_repo_avatars_assert_local() { } function fixture_attachments_create_1_18() { - echo -n $ONEPIXEL | base64 --decode > $DIR/attachment.png - $work_path/forgejo-client --trace-ascii - --form file=@$DIR/attachment.png http://${HOST_PORT}/${FORGEJO_USER}/${FORGEJO_REPO}/issues/attachments + echo -n $ONEPIXEL | base64 --decode >$DIR/attachment.png + forgejo-curl.sh web --trace-ascii - --form file=@$DIR/attachment.png http://${HOST_PORT}/${FORGEJO_USER}/${FORGEJO_REPO}/issues/attachments } function fixture_attachments_create() { - if $work_path/forgejo-api http://${HOST_PORT}/api/v1/version | grep --quiet --fixed-strings 1.18. ; then + if forgejo-curl.sh api_json http://${HOST_PORT}/api/v1/version | grep --quiet --fixed-strings 1.18.; then fixture_attachments_create_1_18 return fi - id=$($work_path/forgejo-api --data-raw '{"title":"TITLE"}' http://${HOST_PORT}/api/v1/repos/${FORGEJO_USER}/${FORGEJO_REPO}/issues | jq .id) - echo -n $ONEPIXEL | base64 --decode > $DIR/attachment.png - $work_path/forgejo-client -H @$DIR/forgejo-work-path/forgejo-header --form name=attachment.png --form attachment=@$DIR/attachment.png http://${HOST_PORT}/api/v1/repos/${FORGEJO_USER}/${FORGEJO_REPO}/issues/$id/assets + id=$(forgejo-curl.sh api_json --data-raw '{"title":"TITLE"}' http://${HOST_PORT}/api/v1/repos/${FORGEJO_USER}/${FORGEJO_REPO}/issues | jq .id) + echo -n $ONEPIXEL | base64 --decode >$DIR/attachment.png + forgejo-curl.sh api --form name=attachment.png --form attachment=@$DIR/attachment.png http://${HOST_PORT}/api/v1/repos/${FORGEJO_USER}/${FORGEJO_REPO}/issues/$id/assets } function fixture_attachments_assert_s3() { @@ -176,12 +207,18 @@ function fixture_create() { git remote add origin http://${FORGEJO_USER}:${FORGEJO_PASSWORD}@${HOST_PORT}/${FORGEJO_USER}/${FORGEJO_REPO} git config user.email root@example.com git config user.name username - echo SOMETHING > README + echo SOMETHING >README git add README git commit -m 'initial commit' git push --set-upstream --force origin main ) - for fun in ${STORAGE_FUN} ; do + for fun in ${STORAGE_FUN}; do fixture_${fun}_create done } + +function fixture_assert() { + for fun in lfs; do + fixture_${fun}_assert + done +} diff --git a/forgejo/sources/1.20 b/forgejo/sources/1.20 deleted file mode 100644 index 3539e49..0000000 --- a/forgejo/sources/1.20 +++ /dev/null @@ -1 +0,0 @@ -https://codeberg.org/forgejo/forgejo v1.20/forgejo 5.0.0+0-gitea-1.20.0 diff --git a/forgejo/sources/1.21 b/forgejo/sources/1.21 deleted file mode 100644 index fc1869a..0000000 --- a/forgejo/sources/1.21 +++ /dev/null @@ -1 +0,0 @@ -https://codeberg.org/forgejo/forgejo v1.21/forgejo 6.0.0+0-gitea-1.21.0 diff --git a/forgejo/sources/1.22 b/forgejo/sources/1.22 deleted file mode 100644 index 985576e..0000000 --- a/forgejo/sources/1.22 +++ /dev/null @@ -1 +0,0 @@ -https://codeberg.org/forgejo/forgejo forgejo 7.0.0+0-gitea-1.22.0 diff --git a/forgejo/sources/10.0 b/forgejo/sources/10.0 new file mode 100644 index 0000000..7c1c013 --- /dev/null +++ b/forgejo/sources/10.0 @@ -0,0 +1 @@ +https://codeberg.org/forgejo/forgejo forgejo 10.0.0+gitea-1.22 diff --git a/forgejo/sources/11.0 b/forgejo/sources/11.0 new file mode 100644 index 0000000..0286dca --- /dev/null +++ b/forgejo/sources/11.0 @@ -0,0 +1 @@ +https://codeberg.org/forgejo/forgejo forgejo 11.0.0 diff --git a/forgejo/sources/7.0 b/forgejo/sources/7.0 new file mode 100644 index 0000000..0e2cc26 --- /dev/null +++ b/forgejo/sources/7.0 @@ -0,0 +1 @@ +https://codeberg.org/forgejo/forgejo v7.0/forgejo 7.0.0+gitea-1.21.0 diff --git a/forgejo/sources/8.0 b/forgejo/sources/8.0 new file mode 100644 index 0000000..42ce80b --- /dev/null +++ b/forgejo/sources/8.0 @@ -0,0 +1 @@ +https://codeberg.org/forgejo/forgejo v8.0/forgejo 8.0.0+gitea-1.22.0 diff --git a/forgejo/sources/9.0 b/forgejo/sources/9.0 new file mode 100644 index 0000000..bd6c2d0 --- /dev/null +++ b/forgejo/sources/9.0 @@ -0,0 +1 @@ +https://codeberg.org/forgejo/forgejo forgejo 9.0.0+gitea-1.22.0 diff --git a/forgejo/upgrades/misplace-app.ini b/forgejo/upgrades/misplace-app.ini deleted file mode 100644 index 0aeff45..0000000 --- a/forgejo/upgrades/misplace-app.ini +++ /dev/null @@ -1,59 +0,0 @@ -RUN_MODE = prod -WORK_PATH = ${WORK_PATH} - -[server] -APP_DATA_PATH = ${WORK_PATH}/elsewhere -HTTP_PORT = 3000 -SSH_LISTEN_PORT = 2222 -LFS_START_SERVER = true - -[database] -DB_TYPE = sqlite3 - -[log] -MODE = file -LEVEL = debug -ROUTER = file - -[log.file] -FILE_NAME = forgejo.log - -[security] -INSTALL_LOCK = true - -[repository] -ENABLE_PUSH_CREATE_USER = true -DEFAULT_PUSH_CREATE_PRIVATE = false - -[actions] -ENABLED = true - -[attachment] - -[storage.attachments] -PATH = ${WORK_PATH}/data/attachments - -[lfs] - -[storage.lfs] -PATH = ${WORK_PATH}/data/lfs - -[avatar] - -[storage.avatars] -PATH = ${WORK_PATH}/data/avatars - -[repo-avatar] - -[storage.repo-avatars] -PATH = ${WORK_PATH}/data/repo-avatars - -[repo-archive] - -[storage.repo-archive] -PATH = ${WORK_PATH}/data/repo-archive - -[packages] - -[storage.packages] -PATH = ${WORK_PATH}/data/packages diff --git a/forgejo/upgrades/misplace-s3-app.ini b/forgejo/upgrades/misplace-s3-app.ini deleted file mode 100644 index d9243dd..0000000 --- a/forgejo/upgrades/misplace-s3-app.ini +++ /dev/null @@ -1,89 +0,0 @@ -RUN_MODE = prod -WORK_PATH = ${WORK_PATH} - -[server] -APP_DATA_PATH = ${WORK_PATH}/elsewhere -HTTP_PORT = 3000 -SSH_LISTEN_PORT = 2222 -LFS_START_SERVER = true - -[database] -DB_TYPE = sqlite3 - -[log] -MODE = file -LEVEL = debug -ROUTER = file - -[log.file] -FILE_NAME = forgejo.log - -[security] -INSTALL_LOCK = true - -[repository] -ENABLE_PUSH_CREATE_USER = true -DEFAULT_PUSH_CREATE_PRIVATE = false - -[actions] -ENABLED = true - -[attachment] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false - -[lfs] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false - -[repo-avatar] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false - -[avatar] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false - -[repo-archive] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false - -[packages] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false diff --git a/forgejo/upgrades/misplace-s3-two-app.ini b/forgejo/upgrades/misplace-s3-two-app.ini deleted file mode 100644 index 00445ea..0000000 --- a/forgejo/upgrades/misplace-s3-two-app.ini +++ /dev/null @@ -1,113 +0,0 @@ -RUN_MODE = prod -WORK_PATH = ${WORK_PATH} - -[server] -APP_DATA_PATH = ${WORK_PATH}/elsewhere -HTTP_PORT = 3000 -SSH_LISTEN_PORT = 2222 -LFS_START_SERVER = true - -[database] -DB_TYPE = sqlite3 - -[log] -MODE = file -LEVEL = debug -ROUTER = file - -[log.file] -FILE_NAME = forgejo.log - -[security] -INSTALL_LOCK = true - -[repository] -ENABLE_PUSH_CREATE_USER = true -DEFAULT_PUSH_CREATE_PRIVATE = false - -[actions] -ENABLED = true - -[storage.attachments] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false - -[storage.lfs] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false - -[picture] -AVATAR_STORAGE_TYPE = minio -REPOSITORY_AVATAR_STORAGE_TYPE = minio - -[storage.repo-avatars] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false - -[storage.minio] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false - -[storage] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false - -[storage.avatars] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false - -[storage.repo-archive] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false - -[storage.packages] -STORAGE_TYPE = minio -SERVE_DIRECT = false -MINIO_ENDPOINT = 127.0.0.1:9000 -MINIO_ACCESS_KEY_ID = 123456 -MINIO_SECRET_ACCESS_KEY = 12345678 -MINIO_BUCKET = forgejo -MINIO_LOCATION = us-east-1 -MINIO_USE_SSL = false diff --git a/forgejo/upgrades/storage-relative-app.ini b/forgejo/upgrades/storage-relative-app.ini deleted file mode 100644 index eba5232..0000000 --- a/forgejo/upgrades/storage-relative-app.ini +++ /dev/null @@ -1,44 +0,0 @@ -RUN_MODE = prod -WORK_PATH = ${WORK_PATH} - -[server] -APP_DATA_PATH = ${WORK_PATH}/data -HTTP_PORT = 3000 -SSH_LISTEN_PORT = 2222 -LFS_START_SERVER = true - -[database] -DB_TYPE = sqlite3 - -[log] -MODE = file -LEVEL = debug -ROUTER = file - -[log.file] -FILE_NAME = forgejo.log - -[security] -INSTALL_LOCK = true - -[repository] -ENABLE_PUSH_CREATE_USER = true -DEFAULT_PUSH_CREATE_PRIVATE = false - -[storage.attachments] -PATH = relative-attachments - -[storage.lfs] -PATH = relative-lfs - -[storage.avatars] -PATH = relative-avatars - -[storage.repo-avatars] -PATH = relative-repo-avatars - -[storage.repo-archive] -PATH = relative-repo-archive - -[storage.packages] -PATH = relative-packages diff --git a/forgejo/upgrades/test-upgrade.sh b/forgejo/upgrades/test-upgrade.sh deleted file mode 100755 index cfbb946..0000000 --- a/forgejo/upgrades/test-upgrade.sh +++ /dev/null @@ -1,620 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: MIT - -# -# Debug loop from the source tree: -# -# ./.forgejo/upgrades/test-upgrade.sh dependencies -# ./.forgejo/upgrades/test-upgrade.sh build_all -# VERBOSE=true ./.forgejo/upgrades/test-upgrade.sh test_downgrade_1.20.2_fails -# -# Everything happens in /tmp/forgejo-upgrades -# - -PREFIX=============== -HOST_PORT=0.0.0.0:3000 -STORAGE_PATHS="attachments avatars lfs packages repo-archive repo-avatars" -STORAGE_FUN="attachments avatars lfs packages repo_archive repo_avatars" -DIR=/tmp/forgejo-upgrades -if ${VERBOSE:-false} ; then - set -ex - PS4='${BASH_SOURCE[0]}:$LINENO: ${FUNCNAME[0]}: ' -else - set -e -fi -SELF_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -: ${FORGEJO_USER:=root} -: ${FORGEJO_REPO:=fixture} -: ${FORGEJO_PASSWORD:=admin1234} - -source $SELF_DIR/fixtures.sh - -function maybe_sudo() { - if test $(id -u) != 0 ; then - SUDO=sudo - fi -} - -function log_info() { - echo "$PREFIX $@" -} - -function dependencies() { - maybe_sudo - if ! which curl daemon jq git-lfs > /dev/null ; then - export DEBIAN_FRONTEND=noninteractive - $SUDO apt-get update -qq - $SUDO apt-get install -y -qq curl daemon git-lfs jq sqlite3 gettext-base - fi - - if ! test -f /usr/local/bin/mc || ! test -f /usr/local/bin/minio > /dev/null ; then - $SUDO curl --fail -sS https://dl.min.io/client/mc/release/linux-amd64/mc -o /usr/local/bin/mc - $SUDO curl --fail -sS https://dl.min.io/server/minio/release/linux-amd64/minio -o /usr/local/bin/minio - fi - if ! test -x /usr/local/bin/mc || ! test -x /usr/local/bin/minio > /dev/null ; then - $SUDO chmod +x /usr/local/bin/mc - $SUDO chmod +x /usr/local/bin/minio - fi - - if ! test -f /usr/local/bin/garage > /dev/null ; then - $SUDO curl --fail -sS https://garagehq.deuxfleurs.fr/_releases/v0.8.2/x86_64-unknown-linux-musl/garage -o /usr/local/bin/garage - fi - if ! test -x /usr/local/bin/garage > /dev/null ; then - $SUDO chmod +x /usr/local/bin/garage - fi -} - -function build_all() { - log_info 1.22.0-dev - $SELF_DIR/../build.sh 1.22.0-dev $DIR -} - -function retry() { - rm -f $DIR/wait-for.out - success=false - for delay in 1 1 5 5 15 ; do - if "$@" >> $DIR/wait-for.out 2>&1 ; then - success=true - break - fi - cat $DIR/wait-for.out - echo waiting $delay - sleep $delay - done - if test $success = false ; then - cat $DIR/wait-for.out - return 1 - fi -} - -function download() { - local version=$1 - - if ! test -f $DIR/forgejo-$version ; then - mkdir -p $DIR - for owner in forgejo forgejo-experimental forgejo-integration ; do - if wget -O $DIR/forgejo-$version --quiet https://codeberg.org/$owner/forgejo/releases/download/v$version/forgejo-$version-linux-amd64 ; then - break - fi - done - chmod +x $DIR/forgejo-$version - fi -} - -function cleanup_logs() { - local work_path=$DIR/forgejo-work-path - - rm -f $DIR/*.log - rm -f $work_path/log/*.log -} - -function clobber() { - rm -fr /tmp/forgejo-upgrades -} - -function start_forgejo() { - local version=$1 - - download $version - local work_path=$DIR/forgejo-work-path - daemon --chdir=$DIR --unsafe --env="TERM=$TERM" --env="HOME=$HOME" --env="PATH=$PATH" --pidfile=$DIR/forgejo-pid --errlog=$DIR/forgejo-err.log --output=$DIR/forgejo-out.log -- $DIR/forgejo-$version --config $work_path/app.ini --work-path $work_path - if ! retry grep 'Starting server on' $work_path/log/forgejo.log ; then - cat $DIR/*.log - cat $work_path/log/*.log - return 1 - fi - create_user $version -} - -function start_minio() { - mkdir -p $DIR/minio - daemon --chdir=$DIR --unsafe \ - --env="PATH=$PATH" \ - --env=MINIO_ROOT_USER=123456 \ - --env=MINIO_ROOT_PASSWORD=12345678 \ - --env=MINIO_VOLUMES=$DIR/minio \ - --pidfile=$DIR/minio-pid --errlog=$DIR/minio-err.log --output=$DIR/minio-out.log -- /usr/local/bin/minio server - retry mc alias set testS3 http://127.0.0.1:9000 123456 12345678 -} - -function start_garage() { - mkdir -p $DIR/garage/{data,meta} - cat > $DIR/garage/garage.toml < $work_path/forgejo-token - ( echo -n 'Authorization: token ' ; cat $work_path/forgejo-token ) > $work_path/forgejo-header - ( echo "#!/bin/sh" ; echo 'curl -f -sS -H "Content-Type: application/json" -H @'$work_path/forgejo-header' "$@"' ) > $work_path/forgejo-api && chmod +x $work_path/forgejo-api - $work_path/forgejo-api http://${HOST_PORT}/api/v1/version - - # - # forgejo-client is to use with web endpoints - # - # - # login and obtain a CSRF, all stored in the cookie file - # - ( echo "#!/bin/sh" ; echo 'curl --cookie-jar '$DIR/cookies' --cookie '$DIR/cookies' -f -sS "$@"' ) > $work_path/forgejo-client-update-cookies && chmod +x $work_path/forgejo-client-update-cookies - $work_path/forgejo-client-update-cookies http://${HOST_PORT}/user/login -o /dev/null - $work_path/forgejo-client-update-cookies --verbose -X POST --data user_name=${FORGEJO_USER} --data password=${FORGEJO_PASSWORD} http://${HOST_PORT}/user/login >& $DIR/login.html - $work_path/forgejo-client-update-cookies http://${HOST_PORT}/user/login -o /dev/null - local csrf=$(sed -n -e '/csrf/s/.*csrf\t//p' $DIR/cookies) - # - # use the cookie file but do not modify it - # - ( echo "#!/bin/sh" ; echo 'curl --cookie '$DIR/cookies' -H "X-Csrf-Token: '$csrf'" -f -sS "$@"' ) > $work_path/forgejo-client && chmod +x $work_path/forgejo-client -} - -function stop_daemon() { - local daemon=$1 - - if test -f $DIR/$daemon-pid ; then - local pid=$(cat $DIR/$daemon-pid) - kill -TERM $pid - pidwait $pid || true - for delay in 1 1 2 2 5 5 ; do - if ! test -f $DIR/$daemon-pid ; then - break - fi - sleep $delay - done - ! test -f $DIR/$daemon-pid - fi -} - -function stop() { - stop_daemon forgejo - stop_daemon minio - stop_daemon garage - - cleanup_logs -} - -function reset_forgejo() { - local config=$1 - local work_path=$DIR/forgejo-work-path - rm -fr $work_path - mkdir -p $work_path - WORK_PATH=$work_path envsubst < $SELF_DIR/$config-app.ini > $work_path/app.ini -} - -function reset_minio() { - rm -fr $DIR/minio -} - -function reset_garage() { - rm -fr $DIR/garage -} - -function reset() { - local config=$1 - reset_forgejo $config - reset_minio - reset_garage -} - -function verify_storage() { - local work_path=$DIR/forgejo-work-path - - for path in ${STORAGE_PATHS} ; do - test -d $work_path/data/$path - done -} - -function cleanup_storage() { - local work_path=$DIR/forgejo-work-path - - for path in ${STORAGE_PATHS} ; do - rm -fr $work_path/data/$path - done -} - -function test_downgrade_1.20.2_fails() { - local work_path=$DIR/forgejo-work-path - - log_info "See also https://codeberg.org/forgejo/forgejo/pulls/1225" - - log_info "downgrading from 1.20.3-0 to 1.20.2-0 fails" - stop - reset default - start 1.20.3-0 - stop - download 1.20.2-0 - timeout 60 $DIR/forgejo-1.20.2-0 --config $work_path/app.ini --work-path $work_path || true - if ! grep --fixed-strings --quiet 'use the newer database' $work_path/log/forgejo.log ; then - cat $work_path/log/forgejo.log - return 1 - fi -} - -function test_bug_storage_merged() { - local work_path=$DIR/forgejo-work-path - - log_info "See also https://codeberg.org/forgejo/forgejo/pulls/1225" - - log_info "using < 1.20.3-0 and [storage].PATH merge all storage" - for version in 1.18.5-0 1.19.4-0 1.20.2-0 ; do - stop - reset merged - start $version - for path in ${STORAGE_PATHS} ; do - ! test -d $work_path/data/$path - done - for path in ${STORAGE_PATHS} ; do - ! test -d $work_path/merged/$path - done - test -d $work_path/merged - done - stop - - log_info "upgrading from 1.20.2-0 with [storage].PATH fails" - download 1.20.3-0 - timeout 60 $DIR/forgejo-1.20.3-0 --config $work_path/app.ini --work-path $work_path || true - if ! grep --fixed-strings --quiet '[storage].PATH is set and may create storage issues' $work_path/log/forgejo.log ; then - cat $work_path/log/forgejo.log - return 1 - fi -} - -function test_bug_storage_relative_path() { - local work_path=$DIR/forgejo-work-path - - log_info "using < 1.20.3-0 legacy [server].XXXX and [picture].XXXX are relative to WORK_PATH" - for version in 1.18.5-0 1.19.4-0 1.20.2-0 ; do - stop - reset legagy-relative - start $version - test -d $work_path/relative-lfs - test -d $work_path/relative-avatars - test -d $work_path/relative-repo-avatars - done - - log_info "using >= 1.20.3-0 legacy [server].XXXX and [picture].XXXX are relative to APP_DATA_PATH" - for version in 1.20.3-0 1.21.0-5-rc2 ; do - stop - reset legagy-relative - start $version - test -d $work_path/data/relative-lfs - test -d $work_path/data/relative-avatars - test -d $work_path/data/relative-repo-avatars - done - - log_info "using >= 1.20.3-0 relative [storage.XXXX].PATHS are relative to APP_DATA_PATH" - for version in 1.20.3-0 1.21.0-5-rc2 ; do - stop - reset storage-relative - start $version - for path in ${STORAGE_PATHS} ; do - test -d $work_path/data/relative-$path - done - done - - log_info "using 1.20.[12]-0 relative [storage.XXXX].PATHS are inconsistent" - for version in 1.20.2-0 ; do - stop - reset storage-relative - start $version - test -d $work_path/data/packages - test -d $work_path/relative-repo-archive - test -d $work_path/relative-attachments - test -d $work_path/relative-lfs - test -d $work_path/data/avatars - test -d $work_path/data/repo-avatars - done - - log_info "using < 1.20 relative [storage.XXXX].PATHS are inconsistent" - for version in 1.18.5-0 1.19.4-0 ; do - stop - reset storage-relative - start $version - test -d $work_path/relative-packages - test -d $work_path/relative-repo-archive - test -d $work_path/relative-attachments - test -d $work_path/data/lfs - test -d $work_path/data/avatars - test -d $work_path/data/repo-avatars - done - - log_info "using < 1.20.3-0 relative [XXXX].PATHS are relative to WORK_PATH" - for version in 1.18.5-0 1.19.4-0 1.20.2-0 ; do - stop - reset relative - start $version - for path in ${STORAGE_PATHS} ; do - test -d $work_path/relative-$path - done - done - - log_info "using >= 1.20.3-0 relative [XXXX].PATHS are relative to APP_DATA_PATH" - for version in 1.20.3-0 1.21.0-5-rc2 ; do - stop - reset relative - start $version - for path in ${STORAGE_PATHS} ; do - test -d $work_path/data/relative-$path - done - done - - stop -} - -function test_bug_storage_s3_misplace() { - local work_path=$DIR/forgejo-work-path - local s3_backend=${2:-minio} - - log_info "See also https://codeberg.org/forgejo/forgejo/issues/1338" - - for version in 1.20.2-0 1.20.3-0 ; do - log_info "Forgejo $version & $s3_backend" - stop - reset misplace-s3 - start $version $s3_backend - fixture_create - for fun in ${STORAGE_FUN} ; do - fixture_${fun}_assert_s3 - done - done - - for version in 1.18.5-0 1.19.4-0 ; do - log_info "Forgejo $version & $s3_backend" - stop - reset misplace-s3 - start $version $s3_backend - fixture_create - # - # some storage are in S3 - # - fixture_attachments_assert_s3 - fixture_lfs_assert_s3 - # - # others are in local - # - fixture_repo_archive_assert_local elsewhere/repo-archive - fixture_avatars_assert_local elsewhere/avatars - fixture_packages_assert_local elsewhere/packages - fixture_repo_avatars_assert_local elsewhere/repo-avatars - done -} - -function test_storage_stable_s3() { - local work_path=$DIR/forgejo-work-path - local s3_backend=${1:-minio} - - log_info "See also https://codeberg.org/forgejo/forgejo/issues/1338" - - for version in 1.18.5-0 1.19.4-0 1.20.2-0 1.20.3-0 ; do - log_info "Forgejo $version & $s3_backend" - stop - reset stable-s3 - start $version $s3_backend - fixture_create - for fun in ${STORAGE_FUN} ; do - fixture_${fun}_assert_s3 - done - done -} - -function test_bug_storage_misplace() { - local work_path=$DIR/forgejo-work-path - - log_info "See also https://codeberg.org/forgejo/forgejo/pulls/1225" - - log_info "using < 1.20 and conflicting sections misplace storage" - for version in 1.18.5-0 1.19.4-0 ; do - stop - reset misplace - start $version - # - # some storage are where they should be - # - test -d $work_path/data/packages - test -d $work_path/data/repo-archive - test -d $work_path/data/attachments - # - # others are under APP_DATA_PATH - # - test -d $work_path/elsewhere/lfs - test -d $work_path/elsewhere/avatars - test -d $work_path/elsewhere/repo-avatars - done - - log_info "using < 1.20.[12]-0 and conflicting sections ignores [storage.*]" - for version in 1.20.2-0 ; do - stop - reset misplace - start $version - for path in ${STORAGE_PATHS} ; do - test -d $work_path/elsewhere/$path - done - done - - stop - - log_info "upgrading from 1.20.2-0 with conflicting sections fails" - download 1.20.3-0 - timeout 60 $DIR/forgejo-1.20.3-0 --config $work_path/app.ini --work-path $work_path || true - for path in ${STORAGE_PATHS} ; do - if ! grep --fixed-strings --quiet "[storage.$path] may conflict" $work_path/log/forgejo.log ; then - cat $work_path/log/forgejo.log - return 1 - fi - done -} - -function test_successful_upgrades() { - for config in default specific ; do - log_info "using $config app.ini" - reset $config - - for version in 1.18.5-0 1.19.4-0 1.20.2-0 1.20.3-0 1.21.0-5-rc2 ; do - log_info "run $version" - cleanup_storage - start $version - verify_storage - stop - done - done -} - -function test_forgejo_database_version() { - local expected_version=$1 - local work_path=$DIR/forgejo-work-path - - actual_version=$(sqlite3 $work_path/forgejo.db "select version from forgejo_version") - test "$expected_version" = "$actual_version" -} - -function test_forgejo_database_v3_upgrades_list_table() { - local table=$1 - local work_path=$DIR/forgejo-work-path - - sqlite3 $work_path/forgejo.db ".tables $table" .exit | grep --quiet $table -} - -function test_forgejo_database_v3_upgrades() { - local table=forgejo_auth_token - - stop - - reset default - log_info "run 1.20.4-1" - start 1.20.4-1 - stop - ! test_forgejo_database_v3_upgrades_list_table $table - test_forgejo_database_version 2 - - log_info "run 1.20.5-0" - start 1.20.5-0 - stop - test_forgejo_database_v3_upgrades_list_table $table - test_forgejo_database_version 3 -} - -function run() { - local fun=$1 - shift - - echo Start running $fun - mkdir -p $DIR - > $DIR/$fun.out - tail --follow $DIR/$fun.out | sed --unbuffered -n -e "/^$PREFIX/s/^$PREFIX //p" & - pid=$! - if ! VERBOSE=true ${BASH_SOURCE[0]} $fun "$@" >& $DIR/$fun.out ; then - kill $pid - cat $DIR/$fun.out - echo Failure running $fun - return 1 - fi - kill $pid - echo Success running $fun -} - -function test_upgrades() { - run stop - run dependencies - run build_all - run test_successful_upgrades - run test_bug_storage_misplace - run test_bug_storage_merged - run test_downgrade_1.20.2_fails - run test_bug_storage_s3_misplace - run test_storage_stable_s3 minio - run test_storage_stable_s3 garage - run test_forgejo_database_v3_upgrades -} - -"$@" diff --git a/last-upgrade b/last-upgrade new file mode 100644 index 0000000..0a10e69 --- /dev/null +++ b/last-upgrade @@ -0,0 +1 @@ +Sun Feb 9 00:51:52 UTC 2025 diff --git a/lib/ORGANIZATIONS b/lib/ORGANIZATIONS new file mode 100644 index 0000000..60ddbbd --- /dev/null +++ b/lib/ORGANIZATIONS @@ -0,0 +1 @@ +forgejo-integration forgejo-experimental forgejo diff --git a/lib/api.sh b/lib/api.sh new file mode 100644 index 0000000..cc56f50 --- /dev/null +++ b/lib/api.sh @@ -0,0 +1,148 @@ +#!/bin/bash +# Copyright 2024 The Forgejo Authors +# SPDX-License-Identifier: MIT + +API_TMPDIR=$(mktemp -d) +: ${PASSWORD:=admin1234} + +function api_user_make_admin() { + local api="$1" username="$2" + + forgejo-curl.sh api_json -X PATCH --data '{"admin":true}' $api/admin/users/$username +} + +function api_user_create() { + local api="$1" username="$2" email="$3" + log_info "(re)create user $username" + forgejo-curl.sh api_json -X DELETE $api/admin/users/$username?purge=true >&/dev/null || true + forgejo-curl.sh api_json --data '{"username":"'$username'","email":"'$email'","password":"admin1234","must_change_password":false}' $api/admin/users +} + +function user_login() { + local username=$1 + ( + export DOT=$API_TMPDIR/$username + forgejo-curl.sh logout + forgejo-curl.sh --user $username --password "admin1234" login http://${HOST_PORT} + ) +} + +function api_branch_tip() { + local api="$1" + local repo="$2" + local branch="$3" + + retry forgejo-curl.sh api_json $api/repos/$repo/branches/$branch >&/dev/null + forgejo-curl.sh api_json $api/repos/$repo/branches/$branch | jq --raw-output .commit.id +} + +function api_branch_protect() { + local api="$1" + local repo="$2" + local branch="$3" + + forgejo-curl.sh api_json -X DELETE $api/repos/${repo}/branch_protections/$branch >&/dev/null || true + forgejo-curl.sh api_json --data '{"branch_name":"'$branch'","required_approvals":1}' $api/repos/${repo}/branch_protections +} + +function api_pr_approve() { + local api="$1" + local repo="$2" + local pr="$3" + + forgejo-curl.sh api_json --data '{"event":"APPROVED"}' $api/repos/${repo}/pulls/$pr/reviews +} + +function api_pr_is_merged() { + local api="$1" + local repo="$2" + local pr="$3" + + forgejo-curl.sh api_json $api/repos/$repo/pulls/$pr >$API_TMPDIR/pr.json + $(jq -r .merged <$API_TMPDIR/pr.json) +} + +function api_pr_delete_all() { + local api="$1" + local repo="$2" + + forgejo-curl.sh api_json $api/repos/${repo}/pulls | jq --raw-output '.[] | .number' | while read pr; do + forgejo-curl.sh api_json -X DELETE $api/repos/${repo}/issues/$pr + done +} + +function api_pr_get_status() { + local api="$1" + local repo="$2" + local sha="$3" + + forgejo-curl.sh api_json $api/repos/$repo/commits/$sha/status +} + +function api_pr_check_status() { + local api="$1" + local repo="$2" + local sha="$3" + local expected_status="$4" + local expected_description="$5" + + api_pr_get_status $api $repo $sha >$API_TMPDIR/status.json + local status="$(jq --raw-output .state <$API_TMPDIR/status.json)" + local description="$(jq --raw-output .statuses[0].description <$API_TMPDIR/status.json)" + if test "$status" = "$expected_status" && test -z "$expected_description" -o "$description" = "$expected_description"; then + echo OK + elif test "$status" = "failure" -o "$status" = "success"; then + echo NOK + else + echo RETRY + fi +} + +function api_pr_wait_success() { + api_pr_wait_status success "$@" +} + +function api_pr_wait_failure() { + api_pr_wait_status failure "$@" +} + +function api_pr_wait_running() { + api_pr_wait_status pending "$@" "Has started running" +} + +function api_pr_wait_log() { + local sha="$1" expected_status="$2" expected_description="$3" + local status="$(jq --raw-output .state <$API_TMPDIR/status.json)" + local description="$(jq --raw-output .statuses[0].description <$API_TMPDIR/status.json)" + if test "$expected_description"; then + expected_description=" '$expected_description'" + fi + log_info "$sha status waiting '$expected_status'$expected_description, currently '$status' '$description'" +} + +# default loop delay is 3600 sec (1 hour) +: ${API_LOOPS:=100} +: ${API_LOOP_DELAY:=36} + +function api_pr_wait_status() { + local status="$1" + local api="$2" + local repo="$3" + local sha="$4" + local description="$5" + + for i in $(seq $API_LOOPS); do + if test $(api_pr_check_status "$api" "$repo" "$sha" "$status" "$description") != RETRY; then + break + fi + api_pr_wait_log "$sha" "$status" "$description" + sleep $API_LOOP_DELAY + done + if test $(api_pr_check_status "$api" "$repo" "$sha" "$status" "$description") = "OK"; then + log_info "$sha status OK" + else + api_pr_get_status $api $repo $sha | jq .statuses + log_info "$sha status NOK" + return 1 + fi +} diff --git a/lib/build.sh b/lib/build.sh new file mode 100755 index 0000000..bbef73e --- /dev/null +++ b/lib/build.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Copyright 2024 The Forgejo Authors +# SPDX-License-Identifier: MIT + +set -ex + +SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +version=$1 +dir_binaries=$2 + +v=$(echo $version | sed -E -e 's/^([0-9]+\.[0-9]+).*/\1/') +src=$dir_binaries/src-$v +read url ref semver <$SELF_DIR/../forgejo/sources/$v + +if ! test -d $src; then + mkdir -p $src + cd $src + git init + git remote add origin $url +else + cd $src +fi + +if ! [[ "$ref" =~ ^refs/ ]]; then + ref=refs/heads/$ref +fi +for retry in 1 2 3; do + if timeout 15m git fetch --update-head-ok origin +$ref:$ref; then + break + else + echo "Retry git fetch in 60 seconds" + sleep 60 + fi +done +git fetch --update-head-ok origin +$ref:$ref +git switch --force-create $v $ref + +export TAGS="bindata sqlite sqlite_unlock_notify" FORGEJO_VERSION=$semver +make deps-backend backend +make generate forgejo +cp -a forgejo $dir_binaries/forgejo-$v-dev diff --git a/lib/lib.sh b/lib/lib.sh new file mode 100644 index 0000000..590e082 --- /dev/null +++ b/lib/lib.sh @@ -0,0 +1,461 @@ +#!/bin/bash +# Copyright 2024 The Forgejo Authors +# SPDX-License-Identifier: MIT + +LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +source $LIB_DIR/api.sh + +if ${VERBOSE:-false}; then + set -ex + PS4='${BASH_SOURCE[0]}:$LINENO: ${FUNCNAME[0]}: ' +else + set -e +fi + +set -o pipefail + +export DEBIAN_FRONTEND=noninteractive + +if test $(id -u) != 0; then + SUDO=sudo +fi + +IP=$(hostname -I | cut -f1 -d' ') + +# +# Forgejo releases for which a branch exists (7.0/forgejo etc.) +# +RELEASE_NUMBERS="7.0 10.0 11.0" + +PREFIX=============== +export DIR=/tmp/forgejo-end-to-end +DIR_BINARIES=/srv/forgejo-binaries +export DOT_FORGEJO_CURL=$DIR/forgejo-curl +export DOT=$DOT_FORGEJO_CURL # for backward compatibility with forgejo-curl.sh 1.0.0 +: ${FORGEJO_USER:=root} +: ${FORGEJO_PASSWORD:=admin1234} +ORGANIZATIONS=$(cat $LIB_DIR/ORGANIZATIONS) + +function log_info() { + echo "$PREFIX $@" +} + +function dependencies() { + + if ! test -f /usr/local/bin/forgejo-curl.sh; then + $SUDO curl --fail -sS https://code.forgejo.org/forgejo/forgejo-curl/raw/branch/main/forgejo-curl.sh -o /usr/local/bin/forgejo-curl.sh + $SUDO chmod +x /usr/local/bin/forgejo-curl.sh + fi + + if ! which make curl daemon git-lfs jq sqlite3 skopeo >/dev/null; then + $SUDO apt-get update -qq + $SUDO apt-get install -y -qq make curl daemon git-lfs jq sqlite3 skopeo + fi + + if ! test -f /usr/local/bin/mc || ! test -f /usr/local/bin/minio; then + $SUDO curl --fail -sS https://dl.min.io/client/mc/release/linux-amd64/mc -o /usr/local/bin/mc + $SUDO curl --fail -sS https://dl.min.io/server/minio/release/linux-amd64/minio -o /usr/local/bin/minio + fi + if ! test -x /usr/local/bin/mc || ! test -x /usr/local/bin/minio; then + $SUDO chmod +x /usr/local/bin/mc + $SUDO chmod +x /usr/local/bin/minio + fi + + if ! test -f /usr/local/bin/garage >/dev/null; then + $SUDO curl --fail -sS https://garagehq.deuxfleurs.fr/_releases/v0.8.2/x86_64-unknown-linux-musl/garage -o /usr/local/bin/garage + fi + if ! test -x /usr/local/bin/garage >/dev/null; then + $SUDO chmod +x /usr/local/bin/garage + fi +} + +function retry() { + rm -f $DIR/wait-for.out + success=false + for delay in 1 1 5 5 15 15 15; do + if "$@" >>$DIR/wait-for.out 2>&1; then + success=true + break + fi + cat $DIR/wait-for.out + echo waiting $delay + sleep $delay + done + if test $success = false; then + cat $DIR/wait-for.out + return 1 + fi +} + +function full_version() { + local version=$1 + local owner=$2 + + if [[ $version =~ ^[0-9]+\.[0-9]+$ ]]; then + full_version=$(curl -sS "https://codeberg.org/api/v1/repos/$owner/forgejo/releases?limit=50" | jq -r '.[] | .tag_name | select(startswith("v'$version'"))' | sort --reverse --version-sort | head -1) + echo ${full_version#v} + else + echo $version + fi +} + +function download_forgejo() { + local version=$1 + + if ! test -f $DIR_BINARIES/forgejo-$version; then + mkdir -p $DIR_BINARIES + for owner in $ORGANIZATIONS; do + full_version=$(full_version $version $owner) + if test "$full_version" = ""; then + continue + fi + if wget -O $DIR_BINARIES/forgejo-$version --quiet https://codeberg.org/$owner/forgejo/releases/download/v$full_version/forgejo-$full_version-linux-amd64; then + break + fi + done + if test -s $DIR_BINARIES/forgejo-$version; then + if test "$version" != "$full_version"; then + log_info "downloaded Forgejo $full_version for $version" + fi + else + echo unable to download Forgejo $version + return 1 + fi + chmod +x $DIR_BINARIES/forgejo-$version + fi +} + +function download_gitea() { + local version=$1 + + if ! test -f $DIR_BINARIES/gitea-$version; then + mkdir -p $DIR_BINARIES + if [[ $version =~ ^[0-9]+\.[0-9]+$ ]]; then + full_version=$(curl -sS "https://gitea.com/api/v1/repos/gitea/gitea-mirror/tags" | jq -r '.[] | .name | select(startswith("v'$version'"))' | grep -v -e '-rc' | sort --reverse --version-sort | head -1) + full_version=${full_version#v} + else + full_version=$version + fi + wget -O $DIR_BINARIES/gitea-$version --quiet https://dl.gitea.com/gitea/$full_version/gitea-$full_version-linux-amd64 + + if test -s $DIR_BINARIES/gitea-$version; then + if test "$version" != "$full_version"; then + log_info "downloaded Gitea $full_version for $version" + fi + else + echo unable to download Gitea $version + return 1 + fi + chmod +x $DIR_BINARIES/gitea-$version + fi +} + +function cleanup_logs() { + local config=$1 + + local base=$(work_path_base $config) + local work_path=$DIR/$base + + rm -f $DIR/$base*.log + rm -f $work_path/log/*.log +} + +function clobber() { + rm -fr /tmp/forgejo-end-to-end +} + +: ${GITLAB_USER:=root} +: ${GITLAB_PASSWORD:=Wrobyak4} +: ${GITLAB_PORT:=8181} + +function start_gitlab_cache_load() { + local image=$1 + local d=$DIR_BINARIES/gitlab + if test -d $d; then + log_info "loading $image from $d" + skopeo copy dir:$d docker-daemon:$image + fi +} + +function start_gitlab_cache_save() { + local image=$1 + local d=$DIR_BINARIES/gitlab + if ! test -d $d; then + log_info "saving $image to $d" + skopeo copy docker-daemon:$image dir:$d + fi +} + +function start_gitlab() { + local image=$1 + local config=$2 + + start_gitlab_cache_load $image + + local GITLAB_OMNIBUS_CONFIG="nginx['listen_https'] = false ; nginx['listen_port'] = 8181 ; external_url 'http://$IP:$GITLAB_PORT'; gitlab_rails['gitlab_shell_ssh_port'] = 2221; $config" + docker run --name="test-gitlab" --shm-size=128M -d \ + -e GITLAB_OMNIBUS_CONFIG="$GITLAB_OMNIBUS_CONFIG" \ + -p 2221:22 -p $GITLAB_PORT:8181 \ + $image >&/dev/null &/dev/null +} + +function stop_forgejo() { + local config=$1 + + stop_daemon $(work_path_base $config) +} + +function start_gitea() { + local version=$1 + local config=$2 + + download_gitea $version + start_forgejo_daemon $version $DIR_BINARIES/gitea-$version $config +} + +function start_forgejo() { + local version=$1 + local config=$2 + + download_forgejo $version + start_forgejo_daemon $version $DIR_BINARIES/forgejo-$version $config +} + +function start_forgejo_daemon() { + local version=$1 + local binary=$2 + local config=$3 + + local base=$(work_path_base $config) + local work_path=$DIR/$base + daemon --chdir=$DIR --unsafe --env="TERM=$TERM" --env="HOME=$HOME" --env="PATH=$PATH" --pidfile=$DIR/$base-pid --errlog=$DIR/$base-err.log --output=$DIR/$base-out.log -- $binary --config $work_path/app.ini --work-path $work_path + if ! retry grep --no-messages --quiet 'Starting server on' $work_path/log/forgejo.log; then + grep '' $DIR/$base*.log + grep '' $work_path/log/*.log 2>/dev/null + return 1 + fi + echo "$binary --config $work_path/app.ini --work-path $work_path" '"$@"' >$work_path/forgejocli + chmod +x $work_path/forgejocli + cp -a $work_path/forgejocli $DIR/forgejocli # because setup-forgejo/forgejo-runner.sh expects it here + create_user_and_login $version $config +} + +function start_minio() { + mkdir -p $DIR/minio + daemon --chdir=$DIR --unsafe \ + --env="PATH=$PATH" \ + --env=MINIO_ROOT_USER=123456 \ + --env=MINIO_ROOT_PASSWORD=12345678 \ + --env=MINIO_VOLUMES=$DIR/minio \ + --pidfile=$DIR/minio-pid --errlog=$DIR/minio-err.log --output=$DIR/minio-out.log -- /usr/local/bin/minio server + retry mc alias set testS3 http://127.0.0.1:9000 123456 12345678 >&/dev/null + mc alias set testS3 http://127.0.0.1:9000 123456 12345678 +} + +function start_garage() { + mkdir -p $DIR/garage/{data,meta} + cat >$DIR/garage/garage.toml <$work_path/app.ini +} + +function reset_minio() { + rm -fr $DIR/minio +} + +function reset_garage() { + rm -fr $DIR/garage +} + +function create_user_and_login() { + local version=$1 + local config=$2 + + local work_path=$DIR/$(work_path_base $config) + + local email="$FORGEJO_USER@example.com" + if ! $work_path/forgejocli admin user list | grep --quiet "$email"; then + $work_path/forgejocli admin user create --admin --username "$FORGEJO_USER" --password "$FORGEJO_PASSWORD" --email $email + fi + + forgejo-curl.sh logout + local scopes='--scopes ["all"]' + if echo $version | grep --quiet 1.18; then + scopes="" + fi + forgejo-curl.sh --user "$FORGEJO_USER" --password "$FORGEJO_PASSWORD" $scopes login http://$(get_host_port $config) + + local forgejo_curl=$work_path/forgejo-curl.sh + cat >$forgejo_curl <$DIR/$fun.out + tail --follow $DIR/$fun.out |& sed --unbuffered -n -e "/^$PREFIX/s/^$PREFIX //p" & + local pid=$! + if ! VERBOSE=true $SELF $fun "$@" >&$DIR/$fun.out; then + kill $pid + cat $DIR/$fun.out + echo Failure running $fun + return 1 + fi + kill $pid + echo Success running $fun +} diff --git a/packages/alpine-10.0 b/packages/alpine-10.0 new file mode 120000 index 0000000..b34295a --- /dev/null +++ b/packages/alpine-10.0 @@ -0,0 +1 @@ +alpine-7.0 \ No newline at end of file diff --git a/packages/alpine-7.0/package-source/forgejo-2173/APKBUILD b/packages/alpine-7.0/package-source/forgejo-2173/APKBUILD new file mode 100644 index 0000000..75c9dd1 --- /dev/null +++ b/packages/alpine-7.0/package-source/forgejo-2173/APKBUILD @@ -0,0 +1,24 @@ +# -*- mode: Shell-script; eval: (setq indent-tabs-mode 't); eval: (setq tab-width 4) -*- +# Maintainer: Dominic Meiser +pkgname=forgejo-2173 +pkgver=1.0 +pkgrel=0 +pkgdesc="Forgejo #2173 Reproduction" +url="https://msrd0.dev/msrd0/$pkgname" +arch="noarch" +license="custom" + +subpackages="$pkgname-openrc" + +source="forgejo_2173 forgejo_2173.init" +builddir="$srcdir" + +package() { + install -D -m755 "$srcdir/forgejo_2173" "$pkgdir"/usr/bin/forgejo_2173 + install -D -m755 "$srcdir/forgejo_2173.init" "$pkgdir"/etc/init.d/forgejo_2173 +} + +sha512sums=" +651c2a816510a18981bcd45077eb5acd6e58511d641949ddc690e326b81018d851eb7f1c88e2336eada2f216606ce2aa0569eb2d02d7c423c80705cc00acf838 forgejo_2173 +abc3b1c91bd69478e8e0d46a31148bcd5b4e7838dc35e7b601673866d7e925d70ab70d63c32df98aad060134eaaa6f957691c2c4397d85af5a77f9773de21b5b forgejo_2173.init +" diff --git a/packages/alpine-7.0/package-source/forgejo-2173/forgejo_2173 b/packages/alpine-7.0/package-source/forgejo-2173/forgejo_2173 new file mode 100755 index 0000000..b12f87b --- /dev/null +++ b/packages/alpine-7.0/package-source/forgejo-2173/forgejo_2173 @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Hello World" diff --git a/packages/alpine-7.0/package-source/forgejo-2173/forgejo_2173.init b/packages/alpine-7.0/package-source/forgejo-2173/forgejo_2173.init new file mode 100755 index 0000000..b5d66cc --- /dev/null +++ b/packages/alpine-7.0/package-source/forgejo-2173/forgejo_2173.init @@ -0,0 +1,7 @@ +#!/sbin/openrc-run + +command="/usr/bin/forgejo_2173" + +depend() { + need net +} diff --git a/packages/alpine-7.0/package-source/forgejo-2174/APKBUILD b/packages/alpine-7.0/package-source/forgejo-2174/APKBUILD new file mode 100644 index 0000000..ce75e29 --- /dev/null +++ b/packages/alpine-7.0/package-source/forgejo-2174/APKBUILD @@ -0,0 +1,26 @@ +# -*- mode: Shell-script; eval: (setq indent-tabs-mode 't); eval: (setq tab-width 4) -*- +# Maintainer: Dominic Meiser +pkgname=forgejo-2174 +pkgver=1.0 +pkgrel=0 +pkgdesc="Forgejo #2174 Reproduction" +url="https://msrd0.dev/msrd0/$pkgname" +arch="x86_64" +license="custom" + +# using x86_64 instead of noarch as a workaround of +# https://codeberg.org/forgejo/forgejo/issues/2173 +subpackages="$pkgname-openrc::x86_64" + +source="forgejo_2174 forgejo_2174.init" +builddir="$srcdir" + +package() { + install -D -m755 "$srcdir/forgejo_2174" "$pkgdir"/usr/bin/forgejo_2174 + install -D -m755 "$srcdir/forgejo_2174.init" "$pkgdir"/etc/init.d/forgejo_2174 +} + +sha512sums=" +651c2a816510a18981bcd45077eb5acd6e58511d641949ddc690e326b81018d851eb7f1c88e2336eada2f216606ce2aa0569eb2d02d7c423c80705cc00acf838 forgejo_2174 +b1cba77139cdaf9e0cdd78de93becbb3891ec59646e8d2cb40620b230bd798d51e6d9c58e65b584812a6bb8eb2b9c9f89262a8700a39c62af8ec8ea09aee4e29 forgejo_2174.init +" diff --git a/packages/alpine-7.0/package-source/forgejo-2174/forgejo_2174 b/packages/alpine-7.0/package-source/forgejo-2174/forgejo_2174 new file mode 100755 index 0000000..b12f87b --- /dev/null +++ b/packages/alpine-7.0/package-source/forgejo-2174/forgejo_2174 @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Hello World" diff --git a/packages/alpine-7.0/package-source/forgejo-2174/forgejo_2174.init b/packages/alpine-7.0/package-source/forgejo-2174/forgejo_2174.init new file mode 100755 index 0000000..06b7f20 --- /dev/null +++ b/packages/alpine-7.0/package-source/forgejo-2174/forgejo_2174.init @@ -0,0 +1,7 @@ +#!/sbin/openrc-run + +command="/usr/bin/forgejo_2174" + +depend() { + need net +} diff --git a/packages/alpine-7.0/test.sh b/packages/alpine-7.0/test.sh new file mode 100755 index 0000000..4676e7c --- /dev/null +++ b/packages/alpine-7.0/test.sh @@ -0,0 +1,63 @@ +#!/bin/busybox ash +set -exuo pipefail + +forgejo_url=$1 +forgejo_token=$2 + +# initialize abuild +apk update +apk add --no-cache alpine-sdk sudo util-linux curl +adduser -D user -h /home/user +addgroup user abuild +echo "root ALL=(ALL) ALL" >/etc/sudoers +echo "%abuild ALL=(ALL) NOPASSWD: ALL" >>/etc/sudoers +mkdir -p /var/cache/distfiles +chgrp abuild /var/cache/distfiles +chmod 775 /var/cache/distfiles +mkdir -p "/home/user/.abuild" +echo "/home/user/.abuild/user.rsa" | abuild-keygen -i -b 4096 +echo 'PACKAGER_PRIVKEY=/home/user/.abuild/user.rsa' >/home/user/.abuild/abuild.conf +chown -R "user:user" /home/user/ + +# make sure we own the relevant directory +cp -r package-source /srv/alpine +cd /srv +mkdir packages +echo "REPODEST=/srv/packages" >>/home/user/.abuild/abuild.conf +cat /home/user/.abuild/abuild.conf +chown -R user:user alpine packages + +# build the package +sudo -u user APKBUILD=alpine/forgejo-2174/APKBUILD abuild -r + +# build the package +sudo -u user APKBUILD=alpine/forgejo-2173/APKBUILD abuild -r + +# upload new package +cd packages/alpine/x86_64/ +for file in $(find . -name '*.apk' -type f | sed -e 's,./,,'); do + # remove old package + curl \ + --fail \ + -H "Authorization: token $forgejo_token" \ + -X DELETE \ + "$forgejo_url/api/packages/root/alpine/3.21/e2e-tests/$file" || + true + + # upload new package + curl \ + --fail \ + -H "Authorization: token $forgejo_token" \ + -T "$file" \ + "$forgejo_url/api/packages/root/alpine/3.21/e2e-tests" +done + +# ensure that the install-if condition works as expected +apk add openrc +(cd /etc/apk/keys && curl -JO $forgejo_url/api/packages/root/alpine/key) +echo "$forgejo_url/api/packages/root/alpine/3.21/e2e-tests" >>/etc/apk/repositories +apk add forgejo-2174 forgejo-2173 +[ -e /usr/bin/forgejo_2174 ] # from the installed package +[ -e /usr/bin/forgejo_2173 ] # from the installed package +[ -e /etc/init.d/forgejo_2174 ] # from the -openrc package installed because of the install-if condition +[ -e /etc/init.d/forgejo_2173 ] # from the -openrc package installed because of the install-if condition diff --git a/forgejo/upgrades/merged-app.ini b/packages/alpine-app.ini similarity index 80% rename from forgejo/upgrades/merged-app.ini rename to packages/alpine-app.ini index 0b7150c..ee378c5 100644 --- a/forgejo/upgrades/merged-app.ini +++ b/packages/alpine-app.ini @@ -1,18 +1,20 @@ RUN_MODE = prod -WORK_PATH = ${WORK_PATH} +WORK_PATH = forgejo-work-path [server] APP_DATA_PATH = ${WORK_PATH}/data +DOMAIN = ${IP} HTTP_PORT = 3000 SSH_LISTEN_PORT = 2222 LFS_START_SERVER = true [database] DB_TYPE = sqlite3 +PATH = ${WORK_PATH}/forgejo.db [log] MODE = file -LEVEL = debug +LEVEL = trace ROUTER = file [log.file] @@ -27,6 +29,3 @@ DEFAULT_PUSH_CREATE_PRIVATE = false [actions] ENABLED = true - -[storage] -PATH = ${WORK_PATH}/merged diff --git a/packages/alpine.sh b/packages/alpine.sh new file mode 100644 index 0000000..c8679a5 --- /dev/null +++ b/packages/alpine.sh @@ -0,0 +1,24 @@ +# Copyright 2025 The Forgejo Authors +# SPDX-License-Identifier: MIT + +function test_packages_alpine_version() { + local alpine_version=$1 forgejo_version=$2 + stop_forgejo + reset_forgejo $PACKAGES_DIR/alpine-app.ini + start_forgejo $forgejo_version + + local d=$PACKAGES_DIR/alpine-$forgejo_version + local token=$(cat $DIR/forgejo-curl/token) + local url=http://${HOST_PORT} + + log_info "alpine:$alpine_version & Forgejo $forgejo_version" + docker run --rm --volume $d:$d:ro --workdir $d code.forgejo.org/oci/alpine:$alpine_version ash -c "./test.sh $url $token" +} + +function test_packages_alpine() { + for alpine_version in 3.20 3.21; do + for forgejo_version in 7.0 10.0; do + test_packages_alpine_version $alpine_version $forgejo_version + done + done +} diff --git a/packages/packages.sh b/packages/packages.sh new file mode 100644 index 0000000..002ed79 --- /dev/null +++ b/packages/packages.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Copyright 2024 The Forgejo Authors +# SPDX-License-Identifier: MIT + +PACKAGES_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +source $PACKAGES_DIR/alpine.sh + +function test_packages() { + run test_packages_alpine +} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..cd7ea57 --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "local>forgejo/renovate-config" + ] +} diff --git a/forgejo/upgrades/default-app.ini b/storage/default-app.ini similarity index 90% rename from forgejo/upgrades/default-app.ini rename to storage/default-app.ini index a51290a..ee378c5 100644 --- a/forgejo/upgrades/default-app.ini +++ b/storage/default-app.ini @@ -1,8 +1,9 @@ RUN_MODE = prod -WORK_PATH = ${WORK_PATH} +WORK_PATH = forgejo-work-path [server] APP_DATA_PATH = ${WORK_PATH}/data +DOMAIN = ${IP} HTTP_PORT = 3000 SSH_LISTEN_PORT = 2222 LFS_START_SERVER = true diff --git a/forgejo/upgrades/specific-app.ini b/storage/specific-app.ini similarity index 93% rename from forgejo/upgrades/specific-app.ini rename to storage/specific-app.ini index d7a0bad..c30911e 100644 --- a/forgejo/upgrades/specific-app.ini +++ b/storage/specific-app.ini @@ -1,8 +1,9 @@ RUN_MODE = prod -WORK_PATH = ${WORK_PATH} +WORK_PATH = forgejo-work-path [server] APP_DATA_PATH = ${WORK_PATH}/elsewhere +DOMAIN = ${IP} HTTP_PORT = 3000 SSH_LISTEN_PORT = 2222 LFS_START_SERVER = true diff --git a/forgejo/upgrades/stable-s3-app.ini b/storage/stable-s3-app.ini similarity index 93% rename from forgejo/upgrades/stable-s3-app.ini rename to storage/stable-s3-app.ini index e8c48ae..cedbfa5 100644 --- a/forgejo/upgrades/stable-s3-app.ini +++ b/storage/stable-s3-app.ini @@ -1,8 +1,9 @@ RUN_MODE = prod -WORK_PATH = ${WORK_PATH} +WORK_PATH = forgejo-work-path [server] APP_DATA_PATH = ${WORK_PATH}/elsewhere +DOMAIN = ${IP} HTTP_PORT = 3000 SSH_LISTEN_PORT = 2222 LFS_START_SERVER = true diff --git a/storage/storage.sh b/storage/storage.sh new file mode 100755 index 0000000..8fdb64f --- /dev/null +++ b/storage/storage.sh @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: MIT + +STORAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +STORAGE_PATHS="attachments avatars lfs packages repo-archive repo-avatars" + +function storage_reset() { + local config=$1 + reset_forgejo $STORAGE_DIR/$config-app.ini + reset_minio + reset_garage +} + +function verify_storage() { + local work_path=$DIR/forgejo-work-path + + for path in ${STORAGE_PATHS}; do + test -d $work_path/data/$path + done +} + +function cleanup_storage() { + local work_path=$DIR/forgejo-work-path + + for path in ${STORAGE_PATHS}; do + rm -fr $work_path/data/$path + done +} + +function test_storage_stable_s3() { + local work_path=$DIR/forgejo-work-path + local s3_backend=${1:-minio} + + for version in $RELEASE_NUMBERS; do + log_info "Forgejo $version & $s3_backend" + stop + storage_reset stable-s3 + start $version $s3_backend + fixture_create + for fun in ${STORAGE_FUN}; do + fixture_${fun}_assert_s3 + done + done +} + +function test_storage() { + run test_storage_stable_s3 minio + run test_storage_stable_s3 garage +} diff --git a/forgejo/upgrades/legagy-relative-app.ini b/upgrade/default-app.ini similarity index 65% rename from forgejo/upgrades/legagy-relative-app.ini rename to upgrade/default-app.ini index 130294a..a39c034 100644 --- a/forgejo/upgrades/legagy-relative-app.ini +++ b/upgrade/default-app.ini @@ -1,12 +1,19 @@ RUN_MODE = prod -WORK_PATH = ${WORK_PATH} +WORK_PATH = forgejo-work-path [server] APP_DATA_PATH = ${WORK_PATH}/data +DOMAIN = ${IP} HTTP_PORT = 3000 SSH_LISTEN_PORT = 2222 LFS_START_SERVER = true -LFS_CONTENT_PATH = relative-lfs +ENABLE_PPROF = true + +[repository] +ROOT = ${WORK_PATH}/data/forgejo-repositories + +[queue] +TYPE = immediate [database] DB_TYPE = sqlite3 @@ -14,7 +21,7 @@ PATH = ${WORK_PATH}/forgejo.db [log] MODE = file -LEVEL = debug +LEVEL = trace ROUTER = file [log.file] @@ -27,6 +34,5 @@ INSTALL_LOCK = true ENABLE_PUSH_CREATE_USER = true DEFAULT_PUSH_CREATE_PRIVATE = false -[picture] -AVATAR_UPLOAD_PATH = relative-avatars -REPOSITORY_AVATAR_UPLOAD_PATH = relative-repo-avatars +[actions] +ENABLED = true diff --git a/upgrade/test-pprof-upload.sh b/upgrade/test-pprof-upload.sh new file mode 100644 index 0000000..732fae7 --- /dev/null +++ b/upgrade/test-pprof-upload.sh @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: MIT + +function test_upload_profiles() { + FORGEJO_URL="http://localhost:6060" + PROFILECLI_URL="http://0.0.0.0:4040" + + endpoints=("/debug/pprof/allocs" "/debug/pprof/block" "/debug/pprof/goroutine" "/debug/pprof/mutex" "/debug/pprof/profile?seconds=5") + curl -fL https://github.com/grafana/pyroscope/releases/download/v1.1.5/profilecli_1.1.5_linux_amd64.tar.gz -o profilecli.tar.gz + tar xzf profilecli.tar.gz + + for endpoint in "${endpoints[@]}"; do + output=$(basename "$endpoint") + if [[ $endpoint == *"/profile"* ]]; then + output="profile" + fi + output="${output}.pprof" + # Download the content and save it to a file + curl -s "${FORGEJO_URL}${endpoint}" -o "${output}" + ./profilecli upload ${output} --url=${PROFILECLI_URL} + + rm ${output} + done +} + +function test_forgejo_pprof() { + stop + docker rm -f test_pyroscope + docker run --name test_pyroscope --rm -d -p 4040:4040 code.forgejo.org/oci/pyroscope:1.12.0 + + reset_forgejo $UPGRADE_DIR/default-app.ini + log_info "run 7.0" + start 7.0 + test_upload_profiles + stop + + log_info "run 9.0" + start 9.0 + test_upload_profiles + stop + + docker stop test_pyroscope +} diff --git a/upgrade/upgrade.sh b/upgrade/upgrade.sh new file mode 100755 index 0000000..377399d --- /dev/null +++ b/upgrade/upgrade.sh @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: MIT + +UPGRADE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +function upgrade_reset() { + local config=$1 + reset_forgejo $config + reset_minio +} + +function verify_storage() { + local work_path=$DIR/forgejo-work-path + + for path in ${STORAGE_PATHS}; do + test -d $work_path/data/$path + done +} + +function cleanup_storage() { + local work_path=$DIR/forgejo-work-path + + for path in ${STORAGE_PATHS}; do + rm -fr $work_path/data/$path + done +} + +function test_successful_upgrades() { + stop + for config in $UPGRADE_DIR/default-app.ini; do + log_info "using $config" + upgrade_reset $config + + version=7.0 + log_info "run $version" + cleanup_storage + start $version + fixture_create + fixture_assert + doctor_run $config + + for version in $RELEASE_NUMBERS; do + stop + log_info "run $version" + start $version + verify_storage + fixture_assert + doctor_run $config + done + + migration_assert + done +} + +function migration_assert() { + local work_path=$DIR/forgejo-work-path + local logfile=$work_path/log/forgejo.log + + grep --quiet 'ORM engine initialization successful' $logfile + if grep 'serveInstalled() \[[EW]\] Table' $logfile; then + echo "unexpected warnings in database migration" + return 1 + fi +} + +function test_gitea_upgrades() { + local config=$UPGRADE_DIR/default-app.ini + ( + echo gitea 1.21 forgejo 10.0 + echo gitea 1.22 forgejo 10.0 + ) | while read gitea gitea_version forgejo forgejo_version; do + log_info "upgrading from Gitea $gitea_version to Forgejo $forgejo_version" + stop + upgrade_reset $config + + log_info "run Gitea $gitea_version" + cleanup_storage + start_s3 minio + start_gitea $gitea_version $config + fixture_create + fixture_assert + doctor_run $config + + stop + log_info "run Forgejo $forgejo_version" + start $forgejo_version + verify_storage + fixture_assert + doctor_run $config + + migration_assert + done +} + +source $UPGRADE_DIR/test-pprof-upload.sh + +function test_upgrades() { + run dependencies + + run test_successful_upgrades + run test_forgejo_pprof + run test_gitea_upgrades +}