diff --git a/.woodpecker/docker-combine.yaml b/.woodpecker/docker-combine.yaml index be8583763..161987d64 100644 --- a/.woodpecker/docker-combine.yaml +++ b/.woodpecker/docker-combine.yaml @@ -1,7 +1,7 @@ when: - event: push branch: ${CI_REPO_DEFAULT_BRANCH} - path: [ "*.ex", "*.eex", "*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ] + path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ] - event: tag - event: manual branch: stable diff --git a/.woodpecker/docker.yaml b/.woodpecker/docker.yaml index 317cb5fbb..4053be91d 100644 --- a/.woodpecker/docker.yaml +++ b/.woodpecker/docker.yaml @@ -1,7 +1,7 @@ when: - event: push branch: ${CI_REPO_DEFAULT_BRANCH} - path: [ "*.ex", "*.eex", "*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ] + path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ] - event: tag - event: manual branch: stable diff --git a/.woodpecker/otp-musl.yaml b/.woodpecker/otp-musl.yaml index c58eee66b..e0fb0fb99 100644 --- a/.woodpecker/otp-musl.yaml +++ b/.woodpecker/otp-musl.yaml @@ -1,7 +1,7 @@ when: - event: push branch: ${CI_REPO_DEFAULT_BRANCH} - path: [ "*.ex", "*.eex", "*.exs", "mix.lock", ".woodpecker/**" ] + path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ] - event: tag - event: manual branch: stable @@ -9,6 +9,7 @@ when: matrix: platform: - linux/amd64 + - linux/arm - linux/arm64 # This is needed for the when clauses below. @@ -27,6 +28,7 @@ variables: - export PLEROMA_BUILD_BRANCH=${CI_COMMIT_BRANCH} - mix release --path release build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-alpine-3.22.1 + build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3-alpine build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-alpine-3.22.1 artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0 artifacts_uploader_settings: &artifacts_uploader_settings @@ -66,6 +68,32 @@ steps: - <<: *build_cmds - zip -9rq stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip release + otp-develop-arm-musl: + image: *build_image_arm + when: + - evaluate: 'platform == "linux/arm" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' + environment: *env + commands: &arm_build + - <<: *build_cmds + - zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip release + + otp-stable-arm-musl: + image: *build_image_arm + when: + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"' + environment: *env + commands: *arm_build + + # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable + otp-stable-tag-arm-musl: + image: *build_image_arm + when: + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"' + environment: *env + commands: + - <<: *build_cmds + - zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip release + otp-develop-arm64-musl: image: *build_image_arm64 when: @@ -102,7 +130,20 @@ steps: package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64-musl package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip - file_name: ./pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip + file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip + + upload-latest-amd64-musl: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' + - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64-musl + package_version: latest + file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip + file_name: pleroma.zip + update: 'true' # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable upload-artifacts-tag-amd64-musl: @@ -114,7 +155,70 @@ steps: package_name: pleroma-otp-stable-amd64-musl package_version: stable-${CI_COMMIT_SHA:0:8}-amd64-musl file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip - file_name: ./pleroma-stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip + file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip + + # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable + upload-latest-tag-amd64-musl: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-stable-amd64-musl + package_version: latest + file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip + file_name: pleroma.zip + update: 'true' + + upload-artifacts-arm-musl: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm-musl + package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl + file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip + file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip + + upload-latest-arm-musl: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm-musl + package_version: latest + file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip + file_name: pleroma.zip + update: 'true' + + # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable + upload-artifacts-tag-arm-musl: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-stable-arm-musl + package_version: stable-${CI_COMMIT_SHA:0:8}-arm-musl + file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip + file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip + + # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable + upload-latest-tag-arm-musl: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-stable-arm-musl + package_version: latest + file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip + file_name: pleroma.zip + update: 'true' upload-artifacts-arm64-musl: image: *artifacts_uploader_image @@ -126,7 +230,20 @@ steps: package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64-musl package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip - file_name: ./pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip + file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip + + upload-latest-arm64-musl: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' + - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64-musl + package_version: latest + file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip + file_name: pleroma.zip + update: 'true' # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable upload-artifacts-tag-arm64-musl: @@ -138,4 +255,17 @@ steps: package_name: pleroma-otp-stable-arm64-musl package_version: stable-${CI_COMMIT_SHA:0:8}-arm64-musl file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip - file_name: ./pleroma-stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip + file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip + + # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable + upload-latest-tag-arm64-musl: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-stable-arm64-musl + package_version: latest + file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip + file_name: pleroma.zip + update: 'true' diff --git a/.woodpecker/otp.yaml b/.woodpecker/otp.yaml index c01c2e557..3c8b71814 100644 --- a/.woodpecker/otp.yaml +++ b/.woodpecker/otp.yaml @@ -1,7 +1,7 @@ when: - event: push branch: ${CI_REPO_DEFAULT_BRANCH} - path: [ "*.ex", "*.eex", "*.exs", "mix.lock", ".woodpecker/**" ] + path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ] - event: tag - event: manual branch: stable @@ -9,6 +9,7 @@ when: matrix: platform: - linux/amd64 + - linux/arm - linux/arm64 # This is needed for the when clauses below. @@ -27,6 +28,7 @@ variables: - export PLEROMA_BUILD_BRANCH=${CI_COMMIT_BRANCH} - mix release --path release build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716 + build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3 build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716 artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0 artifacts_uploader_settings: &artifacts_uploader_settings @@ -67,6 +69,32 @@ steps: - <<: *build_cmds - zip -9rq stable-${CI_COMMIT_SHA:0:8}-amd64.zip release + otp-develop-arm: + image: *build_image_arm + when: + - evaluate: 'platform == "linux/arm" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' + environment: *env + commands: &arm_build + - <<: *build_cmds + - zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip release + + otp-stable-arm: + image: *build_image_arm + when: + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"' + environment: *env + commands: *arm_build + + # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable + otp-stable-tag-arm: + image: *build_image_arm + when: + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"' + environment: *env + commands: + - <<: *build_cmds + - zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm.zip release + otp-develop-arm64: image: *build_image_arm64 when: @@ -103,7 +131,20 @@ steps: package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64 package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64 file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip - file_name: ./pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip + file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip + + upload-latest-amd64: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' + - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64 + package_version: latest + file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip + file_name: pleroma.zip + update: 'true' # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable upload-artifacts-tag-amd64: @@ -115,7 +156,70 @@ steps: package_name: pleroma-otp-stable-amd64 package_version: stable-${CI_COMMIT_SHA:0:8}-amd64 file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64.zip - file_name: ./pleroma-stable-${CI_COMMIT_SHA:0:8}-amd64.zip + file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-amd64.zip + + # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable + upload-latest-tag-amd64: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-stable-amd64 + package_version: latest + file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64.zip + file_name: pleroma.zip + update: 'true' + + upload-artifacts-arm: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm + package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm + file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip + file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip + + upload-latest-arm: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm + package_version: latest + file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip + file_name: pleroma.zip + update: 'true' + + # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable + upload-artifacts-tag-arm: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-stable-arm + package_version: stable-${CI_COMMIT_SHA:0:8}-arm + file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm.zip + file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm.zip + + # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable + upload-latest-tag-arm: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-stable-arm + package_version: latest + file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm.zip + file_name: pleroma.zip + update: 'true' upload-artifacts-arm64: image: *artifacts_uploader_image @@ -127,7 +231,20 @@ steps: package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64 package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64 file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip - file_name: ./pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip + file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip + + upload-latest-arm64: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' + - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64 + package_version: latest + file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip + file_name: pleroma.zip + update: 'true' # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable upload-artifacts-tag-arm64: @@ -139,4 +256,17 @@ steps: package_name: pleroma-otp-stable-arm64 package_version: stable-${CI_COMMIT_SHA:0:8}-arm64 file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64.zip - file_name: ./pleroma-stable-${CI_COMMIT_SHA:0:8}-arm64.zip + file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm64.zip + + # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable + upload-latest-tag-arm64: + image: *artifacts_uploader_image + when: + - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"' + settings: + <<: *artifacts_uploader_settings + package_name: pleroma-otp-stable-arm64 + package_version: latest + file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64.zip + file_name: pleroma.zip + update: 'true' diff --git a/rel/files/bin/pleroma_ctl b/rel/files/bin/pleroma_ctl index 2e114ff50..f5d3f321b 100755 --- a/rel/files/bin/pleroma_ctl +++ b/rel/files/bin/pleroma_ctl @@ -79,18 +79,18 @@ update() { RELEASE_ROOT=$(dirname "$SCRIPTPATH") uri="https://git.pleroma.social" project_name="pleroma" - api_base="${uri}/api/v1/packages/${project_name}/generic" package_base="${uri}/api/packages/${project_name}/generic" - project_branch="${BRANCH:-$(detect_branch)}" - flavour="${FLAVOUR:-$(detect_flavour)}" - # API responds in JSON, optimistically try to make it one object per line - ver=$(curl -s "${api_base}"/pleroma-otp-"${project_branch}"-"${flavour}"/-/latest | tr ',' '\n' | grep '"version":' | cut -d':' -f2 | tr -d '"') - file=$(curl -s "${api_base}"/pleroma-otp-"${project_branch}"-"${flavour}"/"${ver}"/files | tr ',' '\n' | grep '"name":' | cut -d':' -f2 | tr -d '"') - full_uri=${FULL_URI:-"${package_base}"/pleroma-otp-"${project_branch}"-"${flavour}"/"${ver}"/"${file}"} + if [ -n "$FULL_URI" ]; then + full_uri="$FULL_URI" + else + project_branch="${BRANCH:-$(detect_branch)}" + flavour="${FLAVOUR:-$(detect_flavour)}" + full_uri="${package_base}"/pleroma-otp-"${project_branch}"-"${flavour}"/latest/pleroma.zip + fi tmp="${TMP_DIR:-/tmp}" artifact="$tmp/pleroma.zip" echo "Downloading the artifact from ${full_uri} to ${artifact}" - curl "$full_uri" -o "${artifact}" + curl -fL "$full_uri" -o "${artifact}" echo "Unpacking ${artifact} to ${tmp}" unzip -q "$artifact" -d "$tmp" echo "Copying files over to $RELEASE_ROOT" @@ -144,9 +144,12 @@ else # HACK: Script arguments need to be sent as an array to Mix tasks, otherwise they will break (quoted) arguments with whitespace. # Previously it was sent as string, which would get split on whitespace on the task side. - # Escaping does not help including non-POSIX printf %q + # Encode as Elixir binary literals to avoid string escaping and interpolation issues. PREPARED_ARGS="" - for arg in "$@"; do PREPARED_ARGS="$PREPARED_ARGS \"$arg\","; done + for arg in "$@"; do + bytes=$(printf '%s' "$arg" | od -An -v -tu1 | tr -s '[:space:]' ',' | sed 's/^,//; s/,$//') + PREPARED_ARGS="$PREPARED_ARGS <<$bytes>>," + done ACTION="$1" if [ $# -gt 0 ]; then diff --git a/test/pleroma/pleroma_ctl_test.exs b/test/pleroma/pleroma_ctl_test.exs new file mode 100644 index 000000000..d96396399 --- /dev/null +++ b/test/pleroma/pleroma_ctl_test.exs @@ -0,0 +1,293 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.PleromaCtlTest do + use ExUnit.Case, async: false + + @pleroma_ctl Path.expand("../../rel/files/bin/pleroma_ctl", __DIR__) + + setup do + tmp_dir = + Path.join(System.tmp_dir!(), "pleroma_ctl_test_#{System.unique_integer([:positive])}") + + release_root = Path.join(tmp_dir, "release") + bin_dir = Path.join(release_root, "bin") + + File.mkdir_p!(bin_dir) + File.cp!(@pleroma_ctl, Path.join(bin_dir, "pleroma_ctl")) + File.chmod!(Path.join(bin_dir, "pleroma_ctl"), 0o755) + + on_exit(fn -> File.rm_rf!(tmp_dir) end) + + {:ok, tmp_dir: tmp_dir, release_root: release_root, bin_dir: bin_dir} + end + + test "update downloads branch-scoped latest OTP package", %{ + tmp_dir: tmp_dir, + bin_dir: bin_dir, + release_root: release_root + } do + stubs_dir = Path.join(tmp_dir, "stubs") + curl_args_path = Path.join(tmp_dir, "curl.args") + File.mkdir_p!(stubs_dir) + write_update_stubs(stubs_dir, curl_args_path, "unused", "glibc") + + update_tmp_dir = Path.join(tmp_dir, "update_tmp") + File.mkdir_p!(update_tmp_dir) + + {output, status} = + System.cmd( + Path.join(bin_dir, "pleroma_ctl"), + [ + "update", + "--branch", + "develop", + "--flavour", + "amd64", + "--tmp-dir", + update_tmp_dir, + "--no-rm" + ], + env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}], + stderr_to_stdout: true + ) + + assert status == 0, output + assert File.exists?(Path.join(release_root, "bin/marker")) + + assert ["-fL", url, "-o", artifact_path] = + curl_args_path + |> File.read!() + |> String.split("\n", trim: true) + + assert url == + "https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-develop-amd64/latest/pleroma.zip" + + assert artifact_path == Path.join(update_tmp_dir, "pleroma.zip") + end + + test "update detects stable branch and local flavour", %{ + tmp_dir: tmp_dir, + bin_dir: bin_dir, + release_root: release_root + } do + stubs_dir = Path.join(tmp_dir, "stubs") + curl_args_path = Path.join(tmp_dir, "curl.args") + File.mkdir_p!(stubs_dir) + write_update_stubs(stubs_dir, curl_args_path, "x86_64", "glibc") + write_start_erl_data(release_root, "2.10.0") + + {output, status} = + System.cmd( + Path.join(bin_dir, "pleroma_ctl"), + ["update", "--tmp-dir", create_update_tmp_dir(tmp_dir), "--no-rm"], + env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}], + stderr_to_stdout: true + ) + + assert status == 0, output + + assert curl_url(curl_args_path) == + "https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-stable-amd64/latest/pleroma.zip" + end + + test "update detects develop branch and musl arm flavour", %{ + tmp_dir: tmp_dir, + bin_dir: bin_dir, + release_root: release_root + } do + stubs_dir = Path.join(tmp_dir, "stubs") + curl_args_path = Path.join(tmp_dir, "curl.args") + File.mkdir_p!(stubs_dir) + write_update_stubs(stubs_dir, curl_args_path, "armv7l", "musl") + write_start_erl_data(release_root, "2.10.0.develop") + + {output, status} = + System.cmd( + Path.join(bin_dir, "pleroma_ctl"), + ["update", "--tmp-dir", create_update_tmp_dir(tmp_dir), "--no-rm"], + env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}], + stderr_to_stdout: true + ) + + assert status == 0, output + + assert curl_url(curl_args_path) == + "https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-develop-arm-musl/latest/pleroma.zip" + end + + test "update with zip URL bypasses branch and flavour detection", %{ + tmp_dir: tmp_dir, + bin_dir: bin_dir, + release_root: release_root + } do + stubs_dir = Path.join(tmp_dir, "stubs") + curl_args_path = Path.join(tmp_dir, "curl.args") + File.mkdir_p!(stubs_dir) + write_update_stubs(stubs_dir, curl_args_path, "unsupported-arch", "unsupported-libc") + write_start_erl_data(release_root, "2.10.0.custombranch") + + custom_url = "https://example.test/custom.zip" + + {output, status} = + System.cmd( + Path.join(bin_dir, "pleroma_ctl"), + [ + "update", + "--zip-url", + custom_url, + "--tmp-dir", + create_update_tmp_dir(tmp_dir), + "--no-rm" + ], + env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}], + stderr_to_stdout: true + ) + + assert status == 0, output + assert curl_url(curl_args_path) == custom_url + end + + test "passes arguments with spaces and Elixir string metacharacters", %{ + tmp_dir: tmp_dir, + bin_dir: bin_dir + } do + capture_path = Path.join(tmp_dir, "captured_args") + eval_path = Path.join(tmp_dir, "pleroma_ctl_eval.exs") + + write_executable(Path.join(bin_dir, "pleroma"), """ + #!/bin/sh + { + printf '%s\n' 'defmodule Pleroma.ReleaseTasks do' + printf '%s\n' ' def run(args), do: File.write!(System.fetch_env!("PLEROMA_CTL_CAPTURE"), :erlang.term_to_binary(args))' + printf '%s\n' 'end' + printf '%s\n' "$2" + } > "$PLEROMA_CTL_EVAL_FILE" + + exec elixir "$PLEROMA_CTL_EVAL_FILE" + """) + + {output, status} = + System.cmd( + Path.join(bin_dir, "pleroma_ctl"), + [ + "user", + "", + "has space", + ~s(has "quote"), + ~s(has \\ backslash), + ~S(#{:not_interpolated}) + ], + env: [ + {"PLEROMA_CTL_CAPTURE", capture_path}, + {"PLEROMA_CTL_EVAL_FILE", eval_path} + ], + stderr_to_stdout: true + ) + + assert status == 0, output + + assert capture_path + |> File.read!() + |> :erlang.binary_to_term() == [ + "user", + "", + "has space", + ~s(has "quote"), + ~s(has \\ backslash), + ~S(#{:not_interpolated}) + ] + end + + defp write_executable(path, contents) do + File.write!(path, contents) + File.chmod!(path, 0o755) + end + + defp write_start_erl_data(release_root, version) do + releases_dir = Path.join(release_root, "releases") + File.mkdir_p!(releases_dir) + File.write!(Path.join(releases_dir, "start_erl.data"), "erts-15.0 #{version}\n") + end + + defp create_update_tmp_dir(tmp_dir) do + update_tmp_dir = Path.join(tmp_dir, "update_tmp") + File.mkdir_p!(update_tmp_dir) + update_tmp_dir + end + + defp write_update_stubs(stubs_dir, curl_args_path, arch, libc) do + write_executable(Path.join(stubs_dir, "curl"), """ + #!/bin/sh + printf '%s\n' "$@" > "#{curl_args_path}" + + while [ $# -gt 0 ]; do + case "$1" in + -o) + artifact="$2" + shift 2 + ;; + *) + shift + ;; + esac + done + + : > "$artifact" + """) + + write_executable(Path.join(stubs_dir, "unzip"), """ + #!/bin/sh + while [ $# -gt 0 ]; do + case "$1" in + -d) + dest="$2" + shift 2 + ;; + *) + shift + ;; + esac + done + + mkdir -p "$dest/release/bin" + printf 'marker' > "$dest/release/bin/marker" + """) + + write_executable(Path.join(stubs_dir, "uname"), """ + #!/bin/sh + printf '%s\n' '#{arch}' + """) + + write_executable(Path.join(stubs_dir, "getconf"), getconf_stub(libc)) + + write_executable(Path.join(stubs_dir, "ldd"), """ + #!/bin/sh + printf '%s\n' 'musl libc (mock)' + """) + end + + defp getconf_stub("glibc") do + """ + #!/bin/sh + printf '%s\n' 'glibc 2.40' + """ + end + + defp getconf_stub(_libc) do + """ + #!/bin/sh + exit 1 + """ + end + + defp curl_url(curl_args_path) do + ["-fL", url, "-o", _artifact_path] = + curl_args_path + |> File.read!() + |> String.split("\n", trim: true) + + url + end +end