From 47c398a6aea864b32160ea37bf33970cd17d8855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joachim=20Schl=C3=B6ffel?= Date: Mon, 6 Jul 2026 09:37:18 +0200 Subject: [PATCH] Add Alpine package runtime tests --- .gitea/workflows/build.yml | 13 ++ README.md | 11 ++ mise.toml | 5 + packaging/alpine/blueprint/README.md | 3 + .../local/greptimedb/scripts/runtime-test.sh | 23 +++ .../local/seaweedfs/scripts/runtime-test.sh | 37 +++++ scripts/apk/runtime-test.sh | 138 ++++++++++++++++++ 7 files changed, 230 insertions(+) create mode 100755 packaging/alpine/local/greptimedb/scripts/runtime-test.sh create mode 100755 packaging/alpine/local/seaweedfs/scripts/runtime-test.sh create mode 100755 scripts/apk/runtime-test.sh diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 1936b0d..cb35ca7 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -53,7 +53,17 @@ jobs: scripts/apk/export-ci-assets.sh scripts/apk/list-packages.sh + - name: Check artifact upload support + id: artifact-upload + run: | + if [ -n "${ACTIONS_RUNTIME_TOKEN:-}" ]; then + echo "available=true" >> "$GITHUB_OUTPUT" + else + echo "available=false" >> "$GITHUB_OUTPUT" + fi + - name: Upload generated CI assets + if: steps.artifact-upload.outputs.available == 'true' uses: actions/upload-artifact@v3 with: name: greptimedb-musl-binaries-${{ matrix.alpine_registry_branch }} @@ -62,6 +72,9 @@ jobs: - name: Smoke test run: scripts/apk/ci-smoke.sh + - name: Runtime test + run: scripts/apk/runtime-test.sh + - name: Publish Alpine packages if: github.event_name != 'pull_request' run: scripts/apk/publish-gitea.sh diff --git a/README.md b/README.md index 63f0e85..7dcdb15 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ apk:smoke Install-test built packages from the local repository in D apk:test-install Test README installation instructions in a fresh Alpine container apk:test-shell Open an Alpine shell with the current local package build installed apk:update-generated Refresh packaged upstream examples and shell completions +apk:runtime-test Run package-local Docker runtime tests against the local... gitea-workflow-build Run the Gitea build workflow locally through act as a pull_request event ``` @@ -139,6 +140,14 @@ SKIP_BUILD=1 mise run apk:test-install SKIP_BUILD=1 mise run apk:test-shell ``` +Run package-local runtime tests: + +```sh +mise run apk:runtime-test +ALPINE_PACKAGE=seaweedfs mise run apk:runtime-test +ALPINE_VERSION=3.24 mise run apk:runtime-test +``` + ### Adding Packages Package path: @@ -152,6 +161,7 @@ Optional package hooks: ```text packaging/alpine/local//scripts/test-install.sh packaging/alpine/local//scripts/update-generated-sources.sh +packaging/alpine/local//scripts/runtime-test.sh ``` Start from the blueprint: @@ -222,6 +232,7 @@ Before opening a PR, run: mise run apk:publish-check mise run gitea-workflow-build mise run apk:test-install +mise run apk:runtime-test ``` Then test at least one installed role in the package shell: diff --git a/mise.toml b/mise.toml index 491ed42..5e25f05 100644 --- a/mise.toml +++ b/mise.toml @@ -52,6 +52,11 @@ run = "scripts/apk/test-shell.sh" description = "Test README installation instructions in a fresh Alpine container" run = "scripts/apk/test-install.sh" +[tasks."apk:runtime-test"] +description = "Run package-local Docker runtime tests against the local Alpine repository" +run = "scripts/apk/runtime-test.sh" +env = { ALPINE_ARCH = "x86_64", ALPINE_REPO_NAME = "local", ALPINE_VERSION = "3.24" } + [tasks."apk:packages"] description = "List built Alpine packages and dependencies" run = "scripts/apk/list-packages.sh" diff --git a/packaging/alpine/blueprint/README.md b/packaging/alpine/blueprint/README.md index af65688..04093f5 100644 --- a/packaging/alpine/blueprint/README.md +++ b/packaging/alpine/blueprint/README.md @@ -12,6 +12,8 @@ Optional package-local hooks: - `scripts/test-install.sh` validates README-style installation instructions. - `scripts/update-generated-sources.sh` refreshes generated files that are listed in `source=`. +- `scripts/runtime-test.sh` validates an installed package in the shared Docker + runtime test. Repo-level tasks discover packages from `packaging/alpine/local/*/APKBUILD`. Limit a task to one or more packages with: @@ -20,6 +22,7 @@ Limit a task to one or more packages with: ALPINE_PACKAGE= mise run apk:build ALPINE_PACKAGES=" " mise run apk:build-all ALPINE_PACKAGE= mise run apk:test-install +ALPINE_PACKAGE= mise run apk:runtime-test ``` For compiled software, put normal Alpine build logic in `build()`, `check()`, diff --git a/packaging/alpine/local/greptimedb/scripts/runtime-test.sh b/packaging/alpine/local/greptimedb/scripts/runtime-test.sh new file mode 100755 index 0000000..5e7c861 --- /dev/null +++ b/packaging/alpine/local/greptimedb/scripts/runtime-test.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -euo pipefail + +apk add greptimedb \ + greptimedb-datanode-openrc \ + greptimedb-docs \ + greptimedb-flownode-openrc \ + greptimedb-frontend-openrc \ + greptimedb-metasrv-openrc \ + greptimedb-standalone-openrc + +test -x /usr/bin/greptime +test -d /etc/greptimedb +test -f /etc/greptimedb/standalone.toml +test -f /etc/conf.d/greptimedb.standalone +test -f /etc/init.d/greptimedb.standalone +test -f /usr/share/doc/greptimedb/config/config.md +test -f /usr/share/doc/greptimedb/config/standalone.example.toml + +greptime --version + +rc-update add greptimedb.standalone default || true +test -L /etc/runlevels/default/greptimedb.standalone diff --git a/packaging/alpine/local/seaweedfs/scripts/runtime-test.sh b/packaging/alpine/local/seaweedfs/scripts/runtime-test.sh new file mode 100755 index 0000000..83e5f59 --- /dev/null +++ b/packaging/alpine/local/seaweedfs/scripts/runtime-test.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -euo pipefail + +apk add seaweedfs \ + seaweedfs-doc \ + bash-completion \ + seaweedfs-master-openrc \ + seaweedfs-volume-openrc \ + seaweedfs-filer-openrc + +test -x /usr/bin/weed +test -d /etc/seaweedfs +test -f /etc/seaweedfs/master.toml +test -f /etc/seaweedfs/filer.toml +test -f /etc/conf.d/seaweedfs.master +test -f /etc/conf.d/seaweedfs.volume +test -f /etc/conf.d/seaweedfs.filer +test -f /etc/init.d/seaweedfs.master +test -f /etc/init.d/seaweedfs.volume +test -f /etc/init.d/seaweedfs.filer +test -d /usr/share/doc/seaweedfs/examples +test -f /usr/share/doc/seaweedfs/examples/master.toml +test -f /usr/share/bash-completion/completions/weed + +weed version + +rc-update add seaweedfs.master default || true +rc-update add seaweedfs.volume default || true +rc-update add seaweedfs.filer default || true + +rc-service seaweedfs.master start +rc-service seaweedfs.volume start +rc-service seaweedfs.filer start + +rc-service seaweedfs.master status +rc-service seaweedfs.volume status +rc-service seaweedfs.filer status diff --git a/scripts/apk/runtime-test.sh b/scripts/apk/runtime-test.sh new file mode 100755 index 0000000..26a9d25 --- /dev/null +++ b/scripts/apk/runtime-test.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +. "${repo_root}/scripts/apk/package-lib.sh" + +alpine_version="${ALPINE_VERSION:-3.24}" +repo_name="${ALPINE_REPO_NAME:-local}" +arch="${ALPINE_ARCH:-x86_64}" +platform="${ALPINE_TEST_PLATFORM:-linux/amd64}" +repo_dir="${repo_root}/packages/${repo_name}/${arch}" +runtime_mode="${ALPINE_RUNTIME_TEST_MODE:-auto}" + +validate_repo() { + if [[ ! -d "${repo_dir}" ]]; then + printf 'missing local repository: packages/%s/%s\n' "${repo_name}" "${arch}" >&2 + printf 'run: ALPINE_ARCH=%s mise run apk:build\n' "${arch}" >&2 + exit 1 + fi + + shopt -s nullglob + repo_keys=("${repo_dir}"/*.rsa.pub) + repo_indexes=("${repo_dir}"/APKINDEX.tar.gz) + shopt -u nullglob + + if [[ "${#repo_keys[@]}" -eq 0 ]]; then + printf 'missing repository key: packages/%s/%s/*.rsa.pub\n' "${repo_name}" "${arch}" >&2 + exit 1 + fi + if [[ "${#repo_indexes[@]}" -eq 0 ]]; then + printf 'missing repository index: packages/%s/%s/APKINDEX.tar.gz\n' "${repo_name}" "${arch}" >&2 + exit 1 + fi +} + +in_alpine() { + [[ -f /etc/alpine-release ]] && command -v apk >/dev/null 2>&1 +} + +prepare_direct_runtime() { + apk add --no-cache bash openrc >/dev/null + cp "${repo_dir}"/*.rsa.pub /etc/apk/keys/ + printf '%s\n' "${repo_root}/packages/${repo_name}" >> /etc/apk/repositories + apk update + mkdir -p /run/openrc + touch /run/openrc/softlevel +} + +run_hook_direct() { + local hook_path="$1" + local package_dir="$2" + local package_name="$3" + + PACKAGE_DIR="${package_dir}" \ + PACKAGE_NAME="${package_name}" \ + "${hook_path}" +} + +run_hook_docker() { + local package_dir_rel="$1" + + docker run --rm --privileged --platform "${platform}" \ + -e "ALPINE_ARCH=${arch}" \ + -e "ALPINE_REPO_NAME=${repo_name}" \ + -e "PACKAGE_DIR=/work/${package_dir_rel}" \ + -e "PACKAGE_NAME=${package_name}" \ + -v "${repo_root}:/work:ro" \ + -v "${repo_root}/packages/${repo_name}:/repo:ro" \ + "alpine:${alpine_version}" \ + sh -lc ' + set -e + apk add --no-cache bash openrc >/dev/null + cp "/repo/${ALPINE_ARCH}"/*.rsa.pub /etc/apk/keys/ + printf "%s\n" /repo >> /etc/apk/repositories + apk update + mkdir -p /run/openrc + touch /run/openrc/softlevel + exec /bin/bash "${PACKAGE_DIR}/scripts/runtime-test.sh" + ' +} + +validate_repo + +case "${runtime_mode}" in + auto) + if in_alpine; then + runtime_mode="direct" + else + runtime_mode="docker" + fi + ;; + direct|docker) ;; + *) + printf 'unsupported ALPINE_RUNTIME_TEST_MODE: %s\n' "${runtime_mode}" >&2 + printf 'expected: auto, direct, or docker\n' >&2 + exit 2 + ;; +esac + +if [[ "${runtime_mode}" == "direct" ]]; then + prepare_direct_runtime +fi + +while IFS= read -r package_dir; do + apk_validate_package_dir "${package_dir}" + package_name="$(apk_package_name "${package_dir}")" + hook_path="${package_dir}/scripts/runtime-test.sh" + + if [[ ! -e "${hook_path}" ]]; then + printf 'Skipping %s: no runtime-test hook\n' "${package_name}" + continue + fi + if [[ ! -x "${hook_path}" ]]; then + printf 'package hook is not executable: %s\n' "${hook_path}" >&2 + exit 1 + fi + if ! apk_package_supports_arch "${package_dir}" "${arch}"; then + printf 'Skipping %s: unsupported arch %s\n' "${package_name}" "${arch}" + continue + fi + + case "${package_dir}" in + "${repo_root}"/*) + package_dir_rel="${package_dir#"${repo_root}/"}" + ;; + *) + printf 'package directory must be inside repository: %s\n' "${package_dir}" >&2 + exit 1 + ;; + esac + + printf 'Running runtime-test hook for %s\n' "${package_name}" + if [[ "${runtime_mode}" == "direct" ]]; then + run_hook_direct "${hook_path}" "${package_dir}" "${package_name}" + else + run_hook_docker "${package_dir_rel}" + fi +done < <(apk_package_dirs "${repo_root}")