build: package releases with nFPM under the PGSTY identity

Replace minio/pkger's hard-coded upstream identity with an in-tree nFPM configuration. Packages now name PGSTY as vendor and maintainer, use the SILO homepage and SPDX license, and preserve the established package names, versions, payload paths, modes, and checksum format.

Resolve both the release binary and systemd unit independently of the caller's working directory. The release and test workflows share the same package script, while the signing script consumes the same final metadata contract.

Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Feng Ruohang
2026-08-04 15:51:47 +08:00
parent 9c799f42d5
commit 10c7670b80
5 changed files with 233 additions and 68 deletions
+38
View File
@@ -0,0 +1,38 @@
name: minio
arch: ${NFPM_ARCH}
platform: linux
version: ${PKG_VERSION}
version_schema: none
release: ${NFPM_RELEASE}
section: utils
priority: optional
maintainer: "Ruohang Feng (@Vonng) <rh@vonng.com>"
description: S3-Interface Libre Object Storage, Community-maintained MinIO server fork.
vendor: PGSTY
homepage: https://silo.pgsty.com
license: AGPL-3.0-or-later
contents:
- src: ${NFPM_SOURCE}
dst: /usr/local/bin/minio
expand: true
file_info:
mode: 0755
owner: root
group: root
- src: ${NFPM_UNIT}
dst: /lib/systemd/system/minio.service
expand: true
file_info:
mode: 0644
owner: root
group: root
rpm:
group: Applications/File
compression: gzip:9
deb:
compression: gzip
fields:
License: AGPL-3.0-or-later
+8 -33
View File
@@ -84,49 +84,24 @@ jobs:
LDFLAGS: ${{ env.LDFLAGS }}
PKG_VERSION: ${{ env.PKG_VERSION }}
- name: Install pkger
- name: Install nFPM
run: |
go install github.com/minio/pkger/v2@v2.6.18
set -euo pipefail
go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.47.0
echo "$(go env GOPATH)/bin" >> "${GITHUB_PATH}"
- name: Prepare package layout
- name: Build nFPM packages
run: |
set -euo pipefail
copy_binary() {
local arch="$1"
local pattern="$2"
local src
src="$(find dist -maxdepth 2 -type f -path "dist/${pattern}/minio" | head -n1 || true)"
if [ -z "${src}" ]; then
echo "Missing GoReleaser binary for ${arch} (${pattern})"
exit 1
fi
mkdir -p "dist/linux-${arch}"
cp "${src}" "dist/linux-${arch}/minio.${RELEASE_TAG}"
}
buildscripts/package-release.sh
copy_binary amd64 "minio_linux_amd64*"
copy_binary arm64 "minio_linux_arm64*"
- name: Build standard pkger packages
run: |
set -euo pipefail
pkger -r "${RELEASE_TAG}" --appName minio --releaseDir dist --ignore
# Keep only full package files; drop convenience symlinks (minio.rpm/minio.deb/minio.apk)
find dist/linux-* -maxdepth 1 -type l \
\( -name 'minio.rpm' -o -name 'minio.deb' -o -name 'minio.apk' \) -delete
find dist -maxdepth 2 -type f \
\( -name '*.rpm' -o -name '*.deb' -o -name '*.apk' -o -name '*.sha256sum' -o -name 'downloads-minio.json' \) | sort
- name: Upload pkger artifacts to Draft release
- name: Upload nFPM packages to Draft release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
mapfile -t files < <(find dist -maxdepth 2 -type f \
\( -name '*.rpm' -o -name '*.deb' -o -name '*.apk' -o -name '*.sha256sum' -o -name 'downloads-minio.json' \) | sort)
mapfile -t files < <(find dist/packages -maxdepth 1 -type f \
\( -name '*.rpm' -o -name '*.deb' -o -name '*.apk' -o -name '*.sha256sum' \) | sort)
if [ "${#files[@]}" -eq 0 ]; then
echo "No packages were generated."
exit 1
+57 -26
View File
@@ -56,34 +56,65 @@ jobs:
LDFLAGS: ${{ env.LDFLAGS }}
PKG_VERSION: ${{ env.PKG_VERSION }}
- name: Install pkger
run: |
go install github.com/minio/pkger/v2@v2.6.18
echo "$(go env GOPATH)/bin" >> "${GITHUB_PATH}"
- name: Package snapshot binaries with pkger
- name: Install nFPM
run: |
set -euo pipefail
copy_binary() {
local arch="$1"
local pattern="$2"
local src
src="$(find dist -maxdepth 2 -type f -path "dist/${pattern}/minio" | head -n1 || true)"
if [ -z "${src}" ]; then
echo "Missing GoReleaser binary for ${arch} (${pattern})"
exit 1
fi
mkdir -p "dist/linux-${arch}"
cp "${src}" "dist/linux-${arch}/minio.${RELEASE_TAG}"
}
go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.47.0
echo "$(go env GOPATH)/bin" >> "${GITHUB_PATH}"
copy_binary amd64 "minio_linux_amd64*"
copy_binary arm64 "minio_linux_arm64*"
pkger -r "${RELEASE_TAG}" --appName minio --releaseDir dist --ignore
- name: Package snapshot binaries with nFPM
run: |
set -euo pipefail
buildscripts/package-release.sh
# Keep only full package files; drop convenience symlinks (minio.rpm/minio.deb/minio.apk)
find dist/linux-* -maxdepth 1 -type l \
\( -name 'minio.rpm' -o -name 'minio.deb' -o -name 'minio.apk' \) -delete
- name: Verify package metadata
working-directory: dist/packages
run: |
set -euo pipefail
find dist -maxdepth 2 -type f \
\( -name '*.rpm' -o -name '*.deb' -o -name '*.apk' -o -name '*.sha256sum' -o -name 'downloads-minio.json' \) | sort
# The signing script asserts these same values, but it runs on the
# maintainer's machine after the release workflow has already built
# and uploaded. Take its expectations as the single source of truth
# so nfpm.yml and the signing script cannot drift apart without
# failing here first, while a fix is still cheap.
eval "$(grep -E '^expected_(vendor|packager|url|summary|description|license|group)=' \
../../buildscripts/sign-release-rpms.sh)"
for value in "${expected_vendor}" "${expected_packager}" "${expected_url}" \
"${expected_summary}" "${expected_description}" \
"${expected_license}" "${expected_group}"; do
test -n "${value}"
done
rpm_file="minio-${PKG_VERSION}-1.x86_64.rpm"
test "$(rpm -qp --queryformat '%{VENDOR}' "${rpm_file}")" = "${expected_vendor}"
test "$(rpm -qp --queryformat '%{PACKAGER}' "${rpm_file}")" = "${expected_packager}"
test "$(rpm -qp --queryformat '%{URL}' "${rpm_file}")" = "${expected_url}"
test "$(rpm -qp --queryformat '%{SUMMARY}' "${rpm_file}")" = "${expected_summary}"
test "$(rpm -qp --queryformat '%{DESCRIPTION}' "${rpm_file}")" = "${expected_description}"
test "$(rpm -qp --queryformat '%{LICENSE}' "${rpm_file}")" = "${expected_license}"
test "$(rpm -qp --queryformat '%{GROUP}' "${rpm_file}")" = "${expected_group}"
# Both payload entries: the unit file is as much a part of the
# package as the binary, and losing it would install a server with
# nothing to start it.
rpm -qpl "${rpm_file}" | grep -Fx '/usr/local/bin/minio'
rpm -qpl "${rpm_file}" | grep -Fx '/lib/systemd/system/minio.service'
deb_file="minio_${PKG_VERSION}_amd64.deb"
test "$(dpkg-deb --field "${deb_file}" Maintainer)" = "${expected_packager}"
test "$(dpkg-deb --field "${deb_file}" Version)" = "${PKG_VERSION}"
test "$(dpkg-deb --field "${deb_file}" License)" = "${expected_license}"
test "$(dpkg-deb --field "${deb_file}" Section)" = "utils"
test "$(dpkg-deb --field "${deb_file}" Homepage)" = "${expected_url}"
test "$(dpkg-deb --field "${deb_file}" Description)" = "${expected_description}"
dpkg-deb --contents "${deb_file}" | grep -E 'usr/local/bin/minio$'
dpkg-deb --contents "${deb_file}" | grep -E 'lib/systemd/system/minio\.service$'
apk_info="$(tar -xOzf "minio_${PKG_VERSION}_x86_64.apk" .PKGINFO)"
grep -Fx "pkgver = ${PKG_VERSION}" <<< "${apk_info}"
grep -Fx "url = ${expected_url}" <<< "${apk_info}"
grep -Fx "maintainer = ${expected_packager}" <<< "${apk_info}"
grep -Fx "license = ${expected_license}" <<< "${apk_info}"
grep -Fx "pkgdesc = ${expected_description}" <<< "${apk_info}"
tar -tzf "minio_${PKG_VERSION}_x86_64.apk" | grep -Fx 'usr/local/bin/minio'
find . -maxdepth 1 -type f | sort
+120
View File
@@ -0,0 +1,120 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_dir="$(cd "${script_dir}/.." && pwd)"
dist_dir="${DIST_DIR:-${repo_dir}/dist}"
nfpm_config="${NFPM_CONFIG:-${repo_dir}/.github/nfpm.yml}"
if [ -z "${PKG_VERSION:-}" ]; then
echo "PKG_VERSION is required" >&2
exit 1
fi
# Re-validate the shape release.yml derived from the tag. The packages are
# named from this, so a malformed value would ship under a name no repository
# can order against.
if ! [[ "${PKG_VERSION}" =~ ^[0-9]{14}\.0\.0$ ]]; then
echo "Invalid PKG_VERSION: ${PKG_VERSION}" >&2
exit 1
fi
if ! command -v nfpm >/dev/null 2>&1; then
echo "nfpm is required" >&2
exit 1
fi
if [ ! -f "${nfpm_config}" ]; then
echo "Missing nFPM config: ${nfpm_config}" >&2
exit 1
fi
# nfpm resolves a relative content src against the current directory, not
# against the config file, so the unit path is passed in absolute. Otherwise
# this only works when invoked from the repository root and fails elsewhere on
# a message that names the file rather than the cause.
unit_file="${repo_dir}/minio.service"
if [ ! -f "${unit_file}" ]; then
echo "Missing systemd unit: ${unit_file}" >&2
exit 1
fi
packages_dir="${dist_dir}/packages"
mkdir -p "${packages_dir}"
# Two spaces, no trailing newline: sign-release-rpms.sh parses these files to
# check download integrity before it signs, and regenerates them afterwards in
# the same shape.
sha256_file() {
local file="$1"
local digest
if command -v sha256sum >/dev/null 2>&1; then
digest="$(sha256sum "${file}" | awk '{print $1}')"
else
digest="$(shasum -a 256 "${file}" | awk '{print $1}')"
fi
printf '%s %s' "${digest}" "$(basename "${file}")" > "${file}.sha256sum"
}
find_binary() {
local goarch="$1"
local matches
local count
# Must resolve to exactly one binary. Picking the first of several build
# variants (an added goamd64 level, a stale dist entry) would silently ship a
# package whose contents do not match its name.
matches="$(find "${dist_dir}" -maxdepth 2 -type f \
-path "${dist_dir}/minio_linux_${goarch}*/minio" | sort)"
count="$(printf '%s' "${matches}" | grep -c . || true)"
if [ "${count}" -eq 0 ]; then
echo "Missing GoReleaser binary for linux/${goarch}" >&2
exit 1
fi
if [ "${count}" -ne 1 ]; then
echo "Expected exactly one GoReleaser binary for linux/${goarch}, found ${count}:" >&2
printf '%s\n' "${matches}" >&2
exit 1
fi
printf '%s\n' "${matches}"
}
build_arch() {
local goarch="$1"
local rpm_arch="$2"
local deb_arch="$3"
local apk_arch="$4"
local source
local rpm_file
local deb_file
local apk_file
source="$(find_binary "${goarch}")"
# These names are the public download names and must not drift; RPM carries a
# release number, DEB and APK do not, matching what pkger produced.
rpm_file="${packages_dir}/minio-${PKG_VERSION}-1.${rpm_arch}.rpm"
deb_file="${packages_dir}/minio_${PKG_VERSION}_${deb_arch}.deb"
apk_file="${packages_dir}/minio_${PKG_VERSION}_${apk_arch}.apk"
PKG_VERSION="${PKG_VERSION}" NFPM_RELEASE=1 NFPM_ARCH="${goarch}" NFPM_SOURCE="${source}" NFPM_UNIT="${unit_file}" \
nfpm package --config "${nfpm_config}" --packager rpm --target "${rpm_file}"
PKG_VERSION="${PKG_VERSION}" NFPM_RELEASE='' NFPM_ARCH="${goarch}" NFPM_SOURCE="${source}" NFPM_UNIT="${unit_file}" \
nfpm package --config "${nfpm_config}" --packager deb --target "${deb_file}"
PKG_VERSION="${PKG_VERSION}" NFPM_RELEASE='' NFPM_ARCH="${goarch}" NFPM_SOURCE="${source}" NFPM_UNIT="${unit_file}" \
nfpm package --config "${nfpm_config}" --packager apk --target "${apk_file}"
sha256_file "${rpm_file}"
sha256_file "${deb_file}"
sha256_file "${apk_file}"
}
build_arch amd64 x86_64 amd64 x86_64
build_arch arm64 aarch64 arm64 aarch64
find "${packages_dir}" -maxdepth 1 -type f | sort
+10 -9
View File
@@ -2,16 +2,17 @@
set -euo pipefail
# These are the single source of truth for the package identity: .github/nfpm.yml
# must agree with them, and test-release.yml asserts that it does. Drift the
# other way round would only surface here, on the maintainer's machine, after
# the build has already run and uploaded.
expected_fingerprint="9592A7BC7A682E7333376E09E7935D8DB9BD8B20"
expected_vendor="MinIO, Inc."
expected_packager="MinIO Development <dev@minio.io>"
expected_url="https://min.io"
expected_summary="MinIO is a High Performance Object Storage released under AGPLv3."
expected_description="MinIO is a High Performance Object Storage released under AGPLv3.
It is API compatible with Amazon S3 cloud storage service. Use MinIO to build
high performance infrastructure for machine learning, analytics and application
data workloads."
expected_license="AGPLv3"
expected_vendor="PGSTY"
expected_packager="Ruohang Feng (@Vonng) <rh@vonng.com>"
expected_url="https://silo.pgsty.com"
expected_summary="S3-Interface Libre Object Storage, Community-maintained MinIO server fork."
expected_description="S3-Interface Libre Object Storage, Community-maintained MinIO server fork."
expected_license="AGPL-3.0-or-later"
expected_group="Applications/File"
expected_payload="/lib/systemd/system/minio.service
/usr/local/bin/minio"