diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ad109f7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.git +.cache +packages +packaging/**/pkg +packaging/**/src diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index d0d4b64..7a7ad9e 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -39,7 +39,7 @@ jobs: steps: - name: Prepare Environment run: | - apk add --no-cache --update abuild-rootbld alpine-sdk atools-apkbuild-lint bash ca-certificates curl doas git nodejs sudo tar + apk add --no-cache --update abuild-rootbld alpine-sdk atools-apkbuild-lint bash ca-certificates curl doas git nodejs rsync shellcheck sudo tar mkdir -p "$ALPINE_APK_REPO_CACHE" "$CARGO_HOME" "$RUSTUP_HOME" "$CARGO_TARGET_DIR" chown -R 1000:1000 /cache/public-alpine-packages/${{ matrix.alpine_registry_branch }} @@ -50,6 +50,7 @@ jobs: run: | scripts/apk/clean.sh find packaging/alpine/local -mindepth 2 -maxdepth 2 -name APKBUILD -exec apkbuild-lint {} + + scripts/apk/lint-shell.sh scripts/apk/outdated.sh | tee update-check.txt if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then { diff --git a/README.md b/README.md index 27e8bf1..86e3bbc 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ apk:ci-build Build all configured Alpine packages directly in the curre apk:ci-smoke Install-test built packages directly in the current Alpine environment apk:clean Remove generated Alpine package build outputs apk:lint Run Alpine APKBUILD lint in Docker +apk:lint-shell Run ShellCheck for packaging automation apk:packages List built Alpine packages and dependencies apk:publish-check Run lint, multi-arch build, package listing, and smoke test apk:publish-gitea Publish built packages to the Gitea Alpine package registry diff --git a/mise.toml b/mise.toml index 0d2f6cf..a0cb587 100644 --- a/mise.toml +++ b/mise.toml @@ -35,6 +35,10 @@ run = "scripts/apk/update-generated-sources.sh" description = "Run Alpine APKBUILD lint in Docker" run = "scripts/apk/build.sh lint" +[tasks."apk:lint-shell"] +description = "Run ShellCheck for packaging automation" +run = "scripts/apk/lint-shell.sh" + [tasks."apk:smoke"] description = "Install-test built packages from the local repository in Docker" run = "scripts/apk/smoke.sh" diff --git a/packaging/alpine/local/gitea/scripts/test-install.sh b/packaging/alpine/local/gitea/scripts/test-install.sh index bc9fbdb..71f439b 100755 --- a/packaging/alpine/local/gitea/scripts/test-install.sh +++ b/packaging/alpine/local/gitea/scripts/test-install.sh @@ -2,45 +2,10 @@ set -euo pipefail repo_root="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../.." && pwd)}" -package_name="${PACKAGE_NAME:-gitea}" -package_version="${PACKAGE_VERSION:-1.26.4-r0}" -alpine_version="${ALPINE_VERSION:-3.24}" -arch="${ALPINE_ARCH:-x86_64}" -repository_url="${ALPINE_REPOSITORY_URL:-}" -repository_key_url="${ALPINE_REPOSITORY_KEY_URL:-}" +# shellcheck source=scripts/apk/test-install-lib.sh +. "${repo_root}/scripts/apk/test-install-lib.sh" -case "${arch}" in - x86_64) - platform="${ALPINE_TEST_PLATFORM:-linux/amd64}" - ;; - aarch64) - platform="${ALPINE_TEST_PLATFORM:-linux/arm64}" - ;; - *) - printf 'unsupported Alpine architecture: %s\n' "${arch}" >&2 - exit 2 - ;; -esac - -if [[ -z "${repository_url}" && "${SKIP_BUILD:-0}" != "1" ]]; then - ALPINE_PACKAGE="${package_name}" "${repo_root}/scripts/apk/build.sh" build "${arch}" -fi - -if [[ -z "${repository_url}" && ! -d "${repo_root}/packages/local/${arch}" ]]; then - printf 'missing local repository: packages/local/%s\n' "${arch}" >&2 - printf 'run: ALPINE_PACKAGE=%s mise run apk:build\n' "${package_name}" >&2 - exit 1 -fi - -docker run --rm --platform "${platform}" \ - -e "ALPINE_ARCH=${arch}" \ - -e "PACKAGE_NAME=${package_name}" \ - -e "PACKAGE_VERSION=${package_version}" \ - -e "ALPINE_REPOSITORY_URL=${repository_url}" \ - -e "ALPINE_REPOSITORY_KEY_URL=${repository_key_url}" \ - -v "${repo_root}/packages/local:/repo:ro" \ - "alpine:${alpine_version}" \ - sh -lc ' +apk_test_install_run "${repo_root}" "${PACKAGE_NAME:-gitea}" "${PACKAGE_VERSION:-1.26.4-r0}" <<'SCRIPT' set -e if [ -n "${ALPINE_REPOSITORY_URL}" ]; then @@ -85,4 +50,4 @@ docker run --rm --platform "${platform}" \ rc-update add gitea default test -L /etc/runlevels/default/gitea - ' +SCRIPT diff --git a/packaging/alpine/local/greptimedb/scripts/test-install.sh b/packaging/alpine/local/greptimedb/scripts/test-install.sh index b02a798..4e5faa2 100755 --- a/packaging/alpine/local/greptimedb/scripts/test-install.sh +++ b/packaging/alpine/local/greptimedb/scripts/test-install.sh @@ -2,43 +2,10 @@ set -euo pipefail repo_root="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../.." && pwd)}" -package_name="${PACKAGE_NAME:-greptimedb}" -alpine_version="${ALPINE_VERSION:-3.24}" -arch="${ALPINE_ARCH:-x86_64}" -repository_url="${ALPINE_REPOSITORY_URL:-}" -repository_key_url="${ALPINE_REPOSITORY_KEY_URL:-}" +# shellcheck source=scripts/apk/test-install-lib.sh +. "${repo_root}/scripts/apk/test-install-lib.sh" -case "${arch}" in - x86_64) - platform="${ALPINE_TEST_PLATFORM:-linux/amd64}" - ;; - aarch64) - platform="${ALPINE_TEST_PLATFORM:-linux/arm64}" - ;; - *) - printf 'unsupported Alpine architecture: %s\n' "${arch}" >&2 - exit 2 - ;; -esac - -if [[ -z "${repository_url}" && "${SKIP_BUILD:-0}" != "1" ]]; then - ALPINE_PACKAGE="${package_name}" "${repo_root}/scripts/apk/build.sh" build "${arch}" -fi - -if [[ -z "${repository_url}" && ! -d "${repo_root}/packages/local/${arch}" ]]; then - printf 'missing local repository: packages/local/%s\n' "${arch}" >&2 - printf 'run: ALPINE_PACKAGE=%s mise run apk:build\n' "${package_name}" >&2 - exit 1 -fi - -docker run --rm --platform "${platform}" \ - -e "ALPINE_ARCH=${arch}" \ - -e "PACKAGE_NAME=${package_name}" \ - -e "ALPINE_REPOSITORY_URL=${repository_url}" \ - -e "ALPINE_REPOSITORY_KEY_URL=${repository_key_url}" \ - -v "${repo_root}/packages/local:/repo:ro" \ - "alpine:${alpine_version}" \ - sh -lc ' +apk_test_install_run "${repo_root}" "${PACKAGE_NAME:-greptimedb}" "${PACKAGE_VERSION:-}" <<'SCRIPT' set -e if [ -n "${ALPINE_REPOSITORY_URL}" ]; then @@ -68,4 +35,4 @@ docker run --rm --platform "${platform}" \ test -f "/usr/share/doc/${PACKAGE_NAME}/config/config.md" test -f "/usr/share/doc/${PACKAGE_NAME}/config/standalone.example.toml" find /etc/init.d -maxdepth 1 -name "greptimedb.*" -print | sort - ' +SCRIPT diff --git a/packaging/alpine/local/lavinmq/scripts/test-install.sh b/packaging/alpine/local/lavinmq/scripts/test-install.sh index a10424a..e8a2663 100755 --- a/packaging/alpine/local/lavinmq/scripts/test-install.sh +++ b/packaging/alpine/local/lavinmq/scripts/test-install.sh @@ -2,43 +2,10 @@ set -euo pipefail repo_root="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../.." && pwd)}" -package_name="${PACKAGE_NAME:-lavinmq}" -alpine_version="${ALPINE_VERSION:-3.24}" -arch="${ALPINE_ARCH:-x86_64}" -repository_url="${ALPINE_REPOSITORY_URL:-}" -repository_key_url="${ALPINE_REPOSITORY_KEY_URL:-}" +# shellcheck source=scripts/apk/test-install-lib.sh +. "${repo_root}/scripts/apk/test-install-lib.sh" -case "${arch}" in - x86_64) - platform="${ALPINE_TEST_PLATFORM:-linux/amd64}" - ;; - aarch64) - platform="${ALPINE_TEST_PLATFORM:-linux/arm64}" - ;; - *) - printf 'unsupported Alpine architecture: %s\n' "${arch}" >&2 - exit 2 - ;; -esac - -if [[ -z "${repository_url}" && "${SKIP_BUILD:-0}" != "1" ]]; then - ALPINE_PACKAGE="${package_name}" "${repo_root}/scripts/apk/build.sh" build "${arch}" -fi - -if [[ -z "${repository_url}" && ! -d "${repo_root}/packages/local/${arch}" ]]; then - printf 'missing local repository: packages/local/%s\n' "${arch}" >&2 - printf 'run: ALPINE_PACKAGE=%s mise run apk:build\n' "${package_name}" >&2 - exit 1 -fi - -docker run --rm --platform "${platform}" \ - -e "ALPINE_ARCH=${arch}" \ - -e "PACKAGE_NAME=${package_name}" \ - -e "ALPINE_REPOSITORY_URL=${repository_url}" \ - -e "ALPINE_REPOSITORY_KEY_URL=${repository_key_url}" \ - -v "${repo_root}/packages/local:/repo:ro" \ - "alpine:${alpine_version}" \ - sh -lc ' +apk_test_install_run "${repo_root}" "${PACKAGE_NAME:-lavinmq}" "${PACKAGE_VERSION:-}" <<'SCRIPT' set -e if [ -n "${ALPINE_REPOSITORY_URL}" ]; then @@ -72,4 +39,4 @@ docker run --rm --platform "${platform}" \ rc-update add lavinmq default test -L /etc/runlevels/default/lavinmq - ' +SCRIPT diff --git a/packaging/alpine/local/seaweedfs/scripts/test-install.sh b/packaging/alpine/local/seaweedfs/scripts/test-install.sh index 4d5ce5d..72bb5f4 100755 --- a/packaging/alpine/local/seaweedfs/scripts/test-install.sh +++ b/packaging/alpine/local/seaweedfs/scripts/test-install.sh @@ -2,38 +2,10 @@ set -euo pipefail repo_root="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../.." && pwd)}" -package_name="${PACKAGE_NAME:-seaweedfs}" -alpine_version="${ALPINE_VERSION:-3.24}" -arch="${ALPINE_ARCH:-x86_64}" +# shellcheck source=scripts/apk/test-install-lib.sh +. "${repo_root}/scripts/apk/test-install-lib.sh" -case "${arch}" in - x86_64) - platform="${ALPINE_TEST_PLATFORM:-linux/amd64}" - ;; - aarch64) - platform="${ALPINE_TEST_PLATFORM:-linux/arm64}" - ;; - *) - printf 'unsupported Alpine architecture: %s\n' "${arch}" >&2 - exit 2 - ;; -esac - -if [[ "${SKIP_BUILD:-0}" != "1" ]]; then - ALPINE_PACKAGE="${package_name}" "${repo_root}/scripts/apk/build.sh" build "${arch}" -fi - -if [[ ! -d "${repo_root}/packages/local/${arch}" ]]; then - printf 'missing local repository: packages/local/%s\n' "${arch}" >&2 - printf 'run: ALPINE_PACKAGE=%s mise run apk:build\n' "${package_name}" >&2 - exit 1 -fi - -docker run --rm --platform "${platform}" \ - -e "ALPINE_ARCH=${arch}" \ - -v "${repo_root}/packages/local:/repo:ro" \ - "alpine:${alpine_version}" \ - sh -lc ' +apk_test_install_run "${repo_root}" "${PACKAGE_NAME:-seaweedfs}" "${PACKAGE_VERSION:-}" <<'SCRIPT' set -e cp "/repo/${ALPINE_ARCH}"/*.rsa.pub /etc/apk/keys/ @@ -70,4 +42,4 @@ docker run --rm --platform "${platform}" \ test -d /usr/share/doc/seaweedfs/examples test -f /usr/share/doc/seaweedfs/examples/master.toml test -f /usr/share/bash-completion/completions/weed - ' +SCRIPT diff --git a/scripts/apk/build.sh b/scripts/apk/build.sh index bea6f78..8b6e654 100755 --- a/scripts/apk/build.sh +++ b/scripts/apk/build.sh @@ -19,6 +19,8 @@ if [[ "${builder_gid}" == "0" ]]; then builder_gid=1000 fi +image_name="${ALPINE_APK_BUILDER_IMAGE:-alpine-apk-builder:${alpine_version}-${builder_uid}-${builder_gid}}" + cache_scope_from_remote() { local remote local path @@ -63,7 +65,6 @@ run_for_arch() { local arch="$1" local subcommand="$2" local package_dir="$3" - local image_name local container_package_dir local package_name local cache_scope @@ -73,19 +74,9 @@ run_for_arch() { apk_validate_package_dir "${package_dir}" package_name="$(apk_package_name "${package_dir}")" container_package_dir="$(apk_container_package_dir "${repo_root}" "${package_dir}")" - image_name="${ALPINE_APK_BUILDER_IMAGE:-alpine-apk-builder:${arch}}" cache_scope="${ALPINE_APK_CACHE_SCOPE:-$(cache_scope_from_remote)}" cache_prefix="${ALPINE_APK_CACHE_PREFIX:-alpine-apk}-${cache_scope}-${package_name}-${arch}" - docker build \ - --platform "${build_platform}" \ - --build-arg "ALPINE_VERSION=${alpine_version}" \ - --build-arg "BUILDER_UID=${builder_uid}" \ - --build-arg "BUILDER_GID=${builder_gid}" \ - -f "${repo_root}/scripts/apk/Dockerfile" \ - -t "${image_name}" \ - "${repo_root}" - docker_args=(--rm --platform "${build_platform}") if [[ -t 0 && -t 1 ]]; then docker_args+=(-it) @@ -111,11 +102,33 @@ run_for_arch() { "${subcommand}" } +build_builder_image() { + docker build \ + --platform "${build_platform}" \ + --build-arg "ALPINE_VERSION=${alpine_version}" \ + --build-arg "BUILDER_UID=${builder_uid}" \ + --build-arg "BUILDER_GID=${builder_gid}" \ + -f "${repo_root}/scripts/apk/Dockerfile" \ + -t "${image_name}" \ + "${repo_root}" +} + mkdir -p \ "${repo_root}/.cache/abuild" \ "${repo_root}/.cache/apk-distfiles" \ "${repo_root}/packages" +case "${command_name}" in + build-all|build|checksum|lint|shell) ;; + *) + printf 'unknown command: %s\n' "${command_name}" >&2 + printf 'usage: %s [build|build-all|checksum|lint|shell] [x86_64|aarch64]\n' "$0" >&2 + exit 2 + ;; +esac + +build_builder_image + case "${command_name}" in build-all) while IFS= read -r package_dir; do diff --git a/scripts/apk/ci-build.sh b/scripts/apk/ci-build.sh index 642e6ff..a4d8b41 100755 --- a/scripts/apk/ci-build.sh +++ b/scripts/apk/ci-build.sh @@ -72,10 +72,8 @@ while IFS= read -r package_dir; do printf 'Building %s for %s\n' "$(basename "${package_dir}")" "${arch}" ( - export ALPINE_ARCH="${arch}" - export CARCH="${arch}" cd "${package_dir}" - abuild -r + ALPINE_ARCH="${arch}" CARCH="${arch}" abuild -r ) mkdir -p "${REPODEST}/${repo_name}/${arch}" diff --git a/scripts/apk/ci-package-cache.sh b/scripts/apk/ci-package-cache.sh index 1a09d44..d41b1ba 100755 --- a/scripts/apk/ci-package-cache.sh +++ b/scripts/apk/ci-package-cache.sh @@ -70,7 +70,7 @@ changed_packages() { mark_all_packages "shared build logic changed" return ;; - packaging/alpine/local/*/APKBUILD) + packaging/alpine/local/*) package="${changed_path#packaging/alpine/local/}" package="${package%%/*}" selected["${package}"]=1 @@ -231,8 +231,7 @@ sync_cache() { prune_stale_apks mkdir -p "${cache_root}" - find "${cache_root}" -mindepth 1 -maxdepth 1 -exec rm -rf {} + - cp -a "${repo_dest}/." "${cache_root}/" + rsync -a --delete "${repo_dest}/" "${cache_root}/" printf 'Updated Alpine package repository cache at %s\n' "${cache_root}" } diff --git a/scripts/apk/lint-shell.sh b/scripts/apk/lint-shell.sh new file mode 100755 index 0000000..1d42390 --- /dev/null +++ b/scripts/apk/lint-shell.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" + +shellcheck \ + "${repo_root}"/scripts/apk/*.sh \ + "${repo_root}"/packaging/alpine/local/*/scripts/*.sh diff --git a/scripts/apk/package-lib.sh b/scripts/apk/package-lib.sh index f1b5c2e..edc77cd 100644 --- a/scripts/apk/package-lib.sh +++ b/scripts/apk/package-lib.sh @@ -107,7 +107,7 @@ apk_package_apk_names() { local subpackage local subpackages export CARCH="${arch}" - cd "${package_dir}" + cd "${package_dir}" || exit 1 # shellcheck source=/dev/null . ./APKBUILD diff --git a/scripts/apk/publish-check.sh b/scripts/apk/publish-check.sh index ea7aadf..2c64c46 100755 --- a/scripts/apk/publish-check.sh +++ b/scripts/apk/publish-check.sh @@ -6,6 +6,7 @@ repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd "${repo_root}" mise run apk:outdated mise run apk:lint +mise run apk:lint-shell mise run apk:build-all mise run apk:packages mise run apk:smoke diff --git a/scripts/apk/runtime-test.Dockerfile b/scripts/apk/runtime-test.Dockerfile new file mode 100644 index 0000000..f1235ec --- /dev/null +++ b/scripts/apk/runtime-test.Dockerfile @@ -0,0 +1,4 @@ +ARG ALPINE_VERSION=3.24 +FROM alpine:${ALPINE_VERSION} + +RUN apk add --no-cache bash openrc diff --git a/scripts/apk/runtime-test.sh b/scripts/apk/runtime-test.sh index 26a9d25..2c4c7f1 100755 --- a/scripts/apk/runtime-test.sh +++ b/scripts/apk/runtime-test.sh @@ -10,6 +10,7 @@ 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}" +runtime_image="${ALPINE_RUNTIME_TEST_IMAGE:-alpine-apk-runtime-test:${alpine_version}}" validate_repo() { if [[ ! -d "${repo_dir}" ]]; then @@ -66,10 +67,9 @@ run_hook_docker() { -e "PACKAGE_NAME=${package_name}" \ -v "${repo_root}:/work:ro" \ -v "${repo_root}/packages/${repo_name}:/repo:ro" \ - "alpine:${alpine_version}" \ + "${runtime_image}" \ 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 @@ -79,6 +79,15 @@ run_hook_docker() { ' } +build_runtime_image() { + docker build \ + --platform "${platform}" \ + --build-arg "ALPINE_VERSION=${alpine_version}" \ + -f "${repo_root}/scripts/apk/runtime-test.Dockerfile" \ + -t "${runtime_image}" \ + "${repo_root}" +} + validate_repo case "${runtime_mode}" in @@ -99,6 +108,8 @@ esac if [[ "${runtime_mode}" == "direct" ]]; then prepare_direct_runtime +else + build_runtime_image fi while IFS= read -r package_dir; do diff --git a/scripts/apk/test-install-lib.sh b/scripts/apk/test-install-lib.sh new file mode 100644 index 0000000..0ba3780 --- /dev/null +++ b/scripts/apk/test-install-lib.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +apk_test_install_run() { + local repo_root="$1" + local package_name="$2" + local package_version="$3" + local alpine_version="${ALPINE_VERSION:-3.24}" + local arch="${ALPINE_ARCH:-x86_64}" + local repository_url="${ALPINE_REPOSITORY_URL:-}" + local repository_key_url="${ALPINE_REPOSITORY_KEY_URL:-}" + local platform + local test_command + + case "${arch}" in + x86_64) platform="${ALPINE_TEST_PLATFORM:-linux/amd64}" ;; + aarch64) platform="${ALPINE_TEST_PLATFORM:-linux/arm64}" ;; + *) + printf 'unsupported Alpine architecture: %s\n' "${arch}" >&2 + return 2 + ;; + esac + + if [[ -z "${repository_url}" && "${SKIP_BUILD:-0}" != "1" ]]; then + ALPINE_PACKAGE="${package_name}" "${repo_root}/scripts/apk/build.sh" build "${arch}" + fi + + if [[ -z "${repository_url}" && ! -d "${repo_root}/packages/local/${arch}" ]]; then + printf 'missing local repository: packages/local/%s\n' "${arch}" >&2 + printf 'run: ALPINE_PACKAGE=%s mise run apk:build\n' "${package_name}" >&2 + return 1 + fi + + test_command="$(cat)" + docker run --rm --platform "${platform}" \ + -e "ALPINE_ARCH=${arch}" \ + -e "PACKAGE_NAME=${package_name}" \ + -e "PACKAGE_VERSION=${package_version}" \ + -e "ALPINE_REPOSITORY_URL=${repository_url}" \ + -e "ALPINE_REPOSITORY_KEY_URL=${repository_key_url}" \ + -v "${repo_root}/packages/local:/repo:ro" \ + "alpine:${alpine_version}" \ + sh -lc "${test_command}" +}