diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index faa46ed64..cbcc2b88e 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -25,9 +25,10 @@ # # Flow: # 1. Resolve and validate the selected Build workflow run and source identity -# 2. Download Linux binaries (x86_64-gnu, aarch64-gnu) from build artifacts -# 3. Build DEB packages for amd64 and arm64 -# 4. Build RPM packages for x86_64 and aarch64 +# 2. Download Linux binaries (x86_64-gnu, aarch64-gnu, x86_64-musl, +# aarch64-musl) from build artifacts +# 3. Build DEB packages for amd64 and arm64, gnu and musl variants +# 4. Build RPM packages for x86_64 and aarch64, gnu and musl variants # 5. Upload all packages to Cloudflare R2 and the GitHub release name: Package DEB/RPM @@ -224,9 +225,9 @@ jobs: echo " Build run ID: $BUILD_RUN_ID" echo " Build run number: $RUN_NUMBER" - # Build DEB and RPM packages for each architecture + # Build DEB and RPM packages for each architecture and libc variant package: - name: Package (${{ matrix.arch }}) + name: Package (${{ matrix.arch }}-${{ matrix.libc }}) needs: resolve runs-on: ubuntu-latest timeout-minutes: 30 @@ -236,13 +237,35 @@ jobs: matrix: include: - arch: x86_64 + libc: gnu deb_arch: amd64 rpm_arch: x86_64 artifact_name: "rustfs-linux-x86_64-gnu" + # gnu binaries dynamically link glibc; musl binaries are static + # and run without a libc dependency. + deb_depends: "libc6 (>= 2.31)" + rpm_depends: "glibc >= 2.31" - arch: aarch64 + libc: gnu deb_arch: arm64 rpm_arch: aarch64 artifact_name: "rustfs-linux-aarch64-gnu" + deb_depends: "libc6 (>= 2.31)" + rpm_depends: "glibc >= 2.31" + - arch: x86_64 + libc: musl + deb_arch: amd64 + rpm_arch: x86_64 + artifact_name: "rustfs-linux-x86_64-musl" + deb_depends: "" + rpm_depends: "" + - arch: aarch64 + libc: musl + deb_arch: arm64 + rpm_arch: aarch64 + artifact_name: "rustfs-linux-aarch64-musl" + deb_depends: "" + rpm_depends: "" steps: - name: Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 @@ -256,13 +279,13 @@ jobs: BUILD_TYPE: ${{ needs.resolve.outputs.build_type }} SOURCE_VERSION: ${{ needs.resolve.outputs.version }} DEV_SEQUENCE: ${{ needs.resolve.outputs.dev_sequence }} - DEB_ARCH: ${{ matrix.deb_arch }} - RPM_ARCH: ${{ matrix.rpm_arch }} + ARCH: ${{ matrix.rpm_arch }} + LIBC: ${{ matrix.libc }} run: | set -euo pipefail normalized=$(./scripts/release/package_versions.sh \ - "$BUILD_TYPE" "$SOURCE_VERSION" "$DEV_SEQUENCE" "$DEB_ARCH" "$RPM_ARCH") + "$BUILD_TYPE" "$SOURCE_VERSION" "$DEV_SEQUENCE" "$ARCH" "$LIBC") printf '%s\n' "$normalized" >> "$GITHUB_OUTPUT" - name: Download binary artifact from build run @@ -308,6 +331,8 @@ jobs: DEB_VERSION: ${{ steps.versions.outputs.deb_version }} DEB_ARCH: ${{ matrix.deb_arch }} DEB_FILE: ${{ steps.versions.outputs.deb_file }} + DEB_DEPENDS: ${{ matrix.deb_depends }} + LIBC: ${{ matrix.libc }} run: | set -euo pipefail @@ -345,9 +370,17 @@ jobs: Section: utils Priority: optional Architecture: ${DEB_ARCH} - Depends: libc6 (>= 2.31) + EOF + + # gnu binaries dynamically link glibc and must declare it; musl + # binaries are statically linked and need no libc dependency. + if [[ -n "$DEB_DEPENDS" ]]; then + echo "Depends: ${DEB_DEPENDS}" >> "${PKG_DIR}/DEBIAN/control" + fi + + cat >> "${PKG_DIR}/DEBIAN/control" << EOF Maintainer: RustFS Team - Description: High-performance distributed object storage + Description: High-performance distributed object storage (${LIBC} build) RustFS is a high-performance distributed object storage software built using Rust. It is compatible with MinIO and S3 API. Homepage: https://rustfs.com @@ -389,15 +422,24 @@ jobs: RPM_RELEASE: ${{ steps.versions.outputs.rpm_release }} RPM_ARCH: ${{ matrix.rpm_arch }} RPM_FILE: ${{ steps.versions.outputs.rpm_file }} + RPM_DEPENDS: ${{ matrix.rpm_depends }} + LIBC: ${{ matrix.libc }} run: | set -euo pipefail - echo "Building RPM for ${RPM_ARCH}" + echo "Building RPM for ${RPM_ARCH} (${LIBC})" sudo apt-get update && sudo apt-get install -y ruby ruby-dev build-essential rpm sudo gem install fpm ./scripts/test_package_versions.sh --require-package-managers + # gnu binaries dynamically link glibc and must declare it; musl + # binaries are statically linked and need no libc dependency. + depends_args=() + if [[ -n "$RPM_DEPENDS" ]]; then + depends_args+=(--depends "$RPM_DEPENDS") + fi + # Create config file for fpm (DEB build creates it in its package dir structure, # but fpm needs the file to exist before packaging) mkdir -p ./tmp-pkg/etc/default @@ -420,9 +462,9 @@ jobs: --iteration "$RPM_RELEASE" \ --architecture "$RPM_ARCH" \ --package "$RPM_FILE" \ - --depends "glibc >= 2.31" \ + "${depends_args[@]}" \ + --description "High-performance distributed object storage (${LIBC} build)" \ --maintainer "RustFS Team " \ - --description "High-performance distributed object storage" \ --url "https://rustfs.com" \ --license "Apache-2.0" \ --before-install <(./scripts/release/package_service_scripts.sh rpm-before-install) \ @@ -455,7 +497,7 @@ jobs: - name: Upload packages to artifacts uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: - name: packages-${{ matrix.arch }} + name: packages-${{ matrix.arch }}-${{ matrix.libc }} path: | *.deb *.rpm @@ -551,10 +593,11 @@ jobs: for f in "$DEB_FILE" "$RPM_FILE"; do if [[ -n "$f" && -f "$f" ]]; then base="$(basename "$f")" - # GitHub stores release asset names with '~' normalized to '.' - # (e.g. rustfs_1.0.0~rc.2_amd64.deb is stored as - # rustfs_1.0.0.rc.2_amd64.deb), so checksum entries must - # reference the name as stored on the release. + # GitHub stores release asset names with '~' normalized to '.'. + # Current package file names embed the raw tag and contain no + # '~', but older releases may still ship such assets, so the + # checksum entries must reference the name as stored on the + # release. github_base="${base//\~/.}" # Remove any stale entry (both naming variants), then append grep -Fv -- "$base" "$checksum_file" > "${checksum_file}.tmp" || true diff --git a/.github/workflows/rustfs-table-test.yml b/.github/workflows/rustfs-table-test.yml index 41185565b..a45b6963d 100644 --- a/.github/workflows/rustfs-table-test.yml +++ b/.github/workflows/rustfs-table-test.yml @@ -133,7 +133,7 @@ jobs: if [ -n "${PACKAGE_URL}" ]; then ARGS+=(--package-url "${PACKAGE_URL}") elif [ -n "${RUSTFS_VERSION}" ] && [ "${RUSTFS_VERSION}" != "null" ]; then - ARGS+=(--package-url "https://github.com/rustfs/rustfs/releases/download/${RUSTFS_VERSION}/rustfs_${RUSTFS_VERSION}_amd64.deb") + ARGS+=(--package-url "https://github.com/rustfs/rustfs/releases/download/${RUSTFS_VERSION}/rustfs-linux-x86_64-gnu-v${RUSTFS_VERSION}.deb") else ARGS+=(--package-url "${RUSTFS_NIGHTLY_PACKAGE_URL}") fi diff --git a/.github/workflows/rustfs-upgrade-test.yml b/.github/workflows/rustfs-upgrade-test.yml index 8c74ce845..caa63d89c 100644 --- a/.github/workflows/rustfs-upgrade-test.yml +++ b/.github/workflows/rustfs-upgrade-test.yml @@ -207,7 +207,7 @@ jobs: local version="$1" tag asset url [ -n "${version}" ] && [ "${version}" != "null" ] || return 0 tag="${version#v}" - asset="rustfs_${tag//-/.}_amd64.deb" + asset="rustfs-linux-x86_64-gnu-v${tag}.deb" url="https://github.com/rustfs/rustfs/releases/download/${tag}/${asset}" if ! gh api "repos/rustfs/rustfs/releases/tags/${tag}" --jq '.assets[].name' 2>/dev/null | grep -qxF "${asset}"; then echo "ERROR: release ${tag} has no downloadable asset ${asset}:" >&2 diff --git a/scripts/release/package_versions.sh b/scripts/release/package_versions.sh index e265cfbef..322f95dbc 100755 --- a/scripts/release/package_versions.sh +++ b/scripts/release/package_versions.sh @@ -21,18 +21,28 @@ fail() { } if [[ $# -ne 5 ]]; then - fail "expected BUILD_TYPE SOURCE_VERSION DEV_SEQUENCE DEB_ARCH RPM_ARCH" + fail "expected BUILD_TYPE SOURCE_VERSION DEV_SEQUENCE ARCH LIBC" fi build_type=$1 source_version=$2 dev_sequence=$3 -deb_arch=$4 -rpm_arch=$5 +arch=$4 +libc=$5 -case "${deb_arch}:${rpm_arch}" in - amd64:x86_64 | arm64:aarch64) ;; - *) fail "unsupported or mismatched architecture pair" ;; +case "$arch" in + x86_64 | aarch64) ;; + *) fail "unsupported architecture (expected x86_64 or aarch64)" ;; +esac + +# The libc variant of the binary being packaged. It only distinguishes the +# package FILE names (gnu and musl builds of the same version would otherwise +# collide on the release); the dpkg/rpm package identity stays plain "rustfs" +# so the two variants remain mutually exclusive upgrades, not co-installable +# packages. +case "$libc" in + gnu | musl) ;; + *) fail "unsupported libc variant (expected gnu or musl)" ;; esac semver_core='(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)' @@ -89,8 +99,23 @@ case "$build_type" in *) fail "unsupported build type" ;; esac -deb_file="rustfs_${deb_version}_${deb_arch}.deb" -rpm_file="rustfs-${rpm_version}-${rpm_release}.${rpm_arch}.rpm" +# Package file names mirror the binary artifact names, whose zips are named +# rustfs-linux---.zip: all release assets of one build +# share the same stem and differ only by extension. Non-development builds +# embed the raw release tag after the 'v' marker exactly like the zips; +# development builds embed dev- (zips use the short SHA, packages the +# full one). The dpkg/rpm versions with their '~' prerelease ordering live +# in the package metadata above and are independent of the file name. +case "$build_type" in + development) + package_stem="rustfs-linux-${arch}-${libc}-dev-${source_sha}" + ;; + *) + package_stem="rustfs-linux-${arch}-${libc}-v${source_version}" + ;; +esac +deb_file="${package_stem}.deb" +rpm_file="${package_stem}.rpm" # Emit only after every input and derived value has been validated. Consumers # may append this fixed five-line protocol directly to GITHUB_OUTPUT. diff --git a/scripts/test_package_versions.sh b/scripts/test_package_versions.sh index a847de815..3573f0620 100755 --- a/scripts/test_package_versions.sh +++ b/scripts/test_package_versions.sh @@ -70,61 +70,73 @@ run_failure() { sha=0123456789abcdef0123456789abcdef01234567 -run_success stable-amd64 \ - $'deb_version=1.2.3\nrpm_version=1.2.3\nrpm_release=1\ndeb_file=rustfs_1.2.3_amd64.deb\nrpm_file=rustfs-1.2.3-1.x86_64.rpm' \ - release 1.2.3 '' amd64 x86_64 -run_success alpha-arm64 \ - $'deb_version=1.2.3~alpha.1\nrpm_version=1.2.3~alpha.1\nrpm_release=1\ndeb_file=rustfs_1.2.3~alpha.1_arm64.deb\nrpm_file=rustfs-1.2.3~alpha.1-1.aarch64.rpm' \ - prerelease 1.2.3-alpha.1 '' arm64 aarch64 -run_success beta-amd64 \ - $'deb_version=1.2.3~beta.2\nrpm_version=1.2.3~beta.2\nrpm_release=1\ndeb_file=rustfs_1.2.3~beta.2_amd64.deb\nrpm_file=rustfs-1.2.3~beta.2-1.x86_64.rpm' \ - prerelease 1.2.3-beta.2 '' amd64 x86_64 -run_success rc-amd64 \ - $'deb_version=1.2.3~rc.4\nrpm_version=1.2.3~rc.4\nrpm_release=1\ndeb_file=rustfs_1.2.3~rc.4_amd64.deb\nrpm_file=rustfs-1.2.3~rc.4-1.x86_64.rpm' \ - prerelease 1.2.3-rc.4 '' amd64 x86_64 -run_success preview-amd64 \ - $'deb_version=1.0.0~rc.5~preview.2\nrpm_version=1.0.0~rc.5~preview.2\nrpm_release=1\ndeb_file=rustfs_1.0.0~rc.5~preview.2_amd64.deb\nrpm_file=rustfs-1.0.0~rc.5~preview.2-1.x86_64.rpm' \ - preview 1.0.0-rc.5-preview.2 '' amd64 x86_64 -run_success development-amd64 \ +run_success stable-x86_64-gnu \ + $'deb_version=1.2.3\nrpm_version=1.2.3\nrpm_release=1\ndeb_file=rustfs-linux-x86_64-gnu-v1.2.3.deb\nrpm_file=rustfs-linux-x86_64-gnu-v1.2.3.rpm' \ + release 1.2.3 '' x86_64 gnu +run_success stable-x86_64-musl \ + $'deb_version=1.2.3\nrpm_version=1.2.3\nrpm_release=1\ndeb_file=rustfs-linux-x86_64-musl-v1.2.3.deb\nrpm_file=rustfs-linux-x86_64-musl-v1.2.3.rpm' \ + release 1.2.3 '' x86_64 musl +run_success alpha-aarch64-gnu \ + $'deb_version=1.2.3~alpha.1\nrpm_version=1.2.3~alpha.1\nrpm_release=1\ndeb_file=rustfs-linux-aarch64-gnu-v1.2.3-alpha.1.deb\nrpm_file=rustfs-linux-aarch64-gnu-v1.2.3-alpha.1.rpm' \ + prerelease 1.2.3-alpha.1 '' aarch64 gnu +run_success alpha-aarch64-musl \ + $'deb_version=1.2.3~alpha.1\nrpm_version=1.2.3~alpha.1\nrpm_release=1\ndeb_file=rustfs-linux-aarch64-musl-v1.2.3-alpha.1.deb\nrpm_file=rustfs-linux-aarch64-musl-v1.2.3-alpha.1.rpm' \ + prerelease 1.2.3-alpha.1 '' aarch64 musl +run_success beta-x86_64-gnu \ + $'deb_version=1.2.3~beta.2\nrpm_version=1.2.3~beta.2\nrpm_release=1\ndeb_file=rustfs-linux-x86_64-gnu-v1.2.3-beta.2.deb\nrpm_file=rustfs-linux-x86_64-gnu-v1.2.3-beta.2.rpm' \ + prerelease 1.2.3-beta.2 '' x86_64 gnu +run_success rc-x86_64-musl \ + $'deb_version=1.2.3~rc.4\nrpm_version=1.2.3~rc.4\nrpm_release=1\ndeb_file=rustfs-linux-x86_64-musl-v1.2.3-rc.4.deb\nrpm_file=rustfs-linux-x86_64-musl-v1.2.3-rc.4.rpm' \ + prerelease 1.2.3-rc.4 '' x86_64 musl +run_success preview-x86_64-gnu \ + $'deb_version=1.0.0~rc.5~preview.2\nrpm_version=1.0.0~rc.5~preview.2\nrpm_release=1\ndeb_file=rustfs-linux-x86_64-gnu-v1.0.0-rc.5-preview.2.deb\nrpm_file=rustfs-linux-x86_64-gnu-v1.0.0-rc.5-preview.2.rpm' \ + preview 1.0.0-rc.5-preview.2 '' x86_64 gnu +run_success preview-aarch64-musl \ + $'deb_version=1.0.0~rc.5~preview.2\nrpm_version=1.0.0~rc.5~preview.2\nrpm_release=1\ndeb_file=rustfs-linux-aarch64-musl-v1.0.0-rc.5-preview.2.deb\nrpm_file=rustfs-linux-aarch64-musl-v1.0.0-rc.5-preview.2.rpm' \ + preview 1.0.0-rc.5-preview.2 '' aarch64 musl +run_success development-x86_64-gnu \ "deb_version=0~dev.7463.${sha} rpm_version=0 rpm_release=0.dev.7463.${sha} -deb_file=rustfs_0~dev.7463.${sha}_amd64.deb -rpm_file=rustfs-0-0.dev.7463.${sha}.x86_64.rpm" \ - development "dev-${sha}" 7463 amd64 x86_64 -run_success development-arm64 \ +deb_file=rustfs-linux-x86_64-gnu-dev-${sha}.deb +rpm_file=rustfs-linux-x86_64-gnu-dev-${sha}.rpm" \ + development "dev-${sha}" 7463 x86_64 gnu +run_success development-aarch64-musl \ "deb_version=0~dev.7463.${sha} rpm_version=0 rpm_release=0.dev.7463.${sha} -deb_file=rustfs_0~dev.7463.${sha}_arm64.deb -rpm_file=rustfs-0-0.dev.7463.${sha}.aarch64.rpm" \ - development "dev-${sha}" 7463 arm64 aarch64 +deb_file=rustfs-linux-aarch64-musl-dev-${sha}.deb +rpm_file=rustfs-linux-aarch64-musl-dev-${sha}.rpm" \ + development "dev-${sha}" 7463 aarch64 musl run_failure missing-arguments -run_failure empty-build-type '' 1.2.3 '' amd64 x86_64 -run_failure unknown-build-type nightly 1.2.3 '' amd64 x86_64 -run_failure empty-version release '' '' amd64 x86_64 -run_failure release-with-sequence release 1.2.3 1 amd64 x86_64 -run_failure release-prerelease-mismatch release 1.2.3-rc.1 '' amd64 x86_64 -run_failure prerelease-release-mismatch prerelease 1.2.3 '' amd64 x86_64 -run_failure preview-malformed preview 1.2.3-rc.1-preview '' amd64 x86_64 -run_failure preview-wrong-shape preview 1.2.3-preview.1 '' amd64 x86_64 -run_failure short-semver release 1.2 '' amd64 x86_64 -run_failure leading-v release v1.2.3 '' amd64 x86_64 -run_failure leading-zero release 01.2.3 '' amd64 x86_64 -run_failure zero-sequence development "dev-${sha}" 0 amd64 x86_64 -run_failure leading-zero-sequence development "dev-${sha}" 01 amd64 x86_64 -run_failure non-decimal-sequence development "dev-${sha}" seven amd64 x86_64 -run_failure empty-dev-sha development dev- 1 amd64 x86_64 -run_failure short-dev-sha development dev-0123456 1 amd64 x86_64 -run_failure uppercase-dev-sha development dev-0123456789ABCDEF0123456789ABCDEF01234567 1 amd64 x86_64 -run_failure dev-extra-suffix development "dev-${sha}-dirty" 1 amd64 x86_64 -run_failure whitespace release '1.2.3 bad' '' amd64 x86_64 -run_failure command-substitution release "1.2.3\$(id)" '' amd64 x86_64 -run_failure backticks release "1.2.3\`id\`" '' amd64 x86_64 -run_failure newline release $'1.2.3\nforged=1' '' amd64 x86_64 -run_failure unsupported-deb-arch release 1.2.3 '' x86_64 x86_64 -run_failure mismatched-arch release 1.2.3 '' amd64 aarch64 +run_failure empty-build-type '' 1.2.3 '' x86_64 gnu +run_failure unknown-build-type nightly 1.2.3 '' x86_64 gnu +run_failure empty-version release '' '' x86_64 gnu +run_failure release-with-sequence release 1.2.3 1 x86_64 gnu +run_failure release-prerelease-mismatch release 1.2.3-rc.1 '' x86_64 gnu +run_failure prerelease-release-mismatch prerelease 1.2.3 '' x86_64 gnu +run_failure preview-malformed preview 1.2.3-rc.1-preview '' x86_64 gnu +run_failure preview-wrong-shape preview 1.2.3-preview.1 '' x86_64 gnu +run_failure short-semver release 1.2 '' x86_64 gnu +run_failure leading-v release v1.2.3 '' x86_64 gnu +run_failure leading-zero release 01.2.3 '' x86_64 gnu +run_failure zero-sequence development "dev-${sha}" 0 x86_64 gnu +run_failure leading-zero-sequence development "dev-${sha}" 01 x86_64 gnu +run_failure non-decimal-sequence development "dev-${sha}" seven x86_64 gnu +run_failure empty-dev-sha development dev- 1 x86_64 gnu +run_failure short-dev-sha development dev-0123456 1 x86_64 gnu +run_failure uppercase-dev-sha development dev-0123456789ABCDEF0123456789ABCDEF01234567 1 x86_64 gnu +run_failure dev-extra-suffix development "dev-${sha}-dirty" 1 x86_64 gnu +run_failure whitespace release '1.2.3 bad' '' x86_64 gnu +run_failure command-substitution release "1.2.3\$(id)" '' x86_64 gnu +run_failure backticks release "1.2.3\`id\`" '' x86_64 gnu +run_failure newline release $'1.2.3\nforged=1' '' x86_64 gnu +run_failure unsupported-arch release 1.2.3 '' armv7 gnu +run_failure empty-arch release 1.2.3 '' '' gnu +run_failure empty-libc release 1.2.3 '' x86_64 '' +run_failure unknown-libc release 1.2.3 '' x86_64 static +run_failure too-many-arguments release 1.2.3 '' x86_64 gnu extra # Ordering contract shared by both package managers: every pre-release sorts # below its final release, every preview sorts below the pre-release it