Files
minio/buildscripts/package-release.sh
T
Feng Ruohang 15ab10833b build: rename the delivery artifacts to silo and complete the package payload
Everything a user installs is renamed, and the package finally installs enough
to be startable on a clean host.

Artifact names
- goreleaser.yml: build id, binary, archive and checksum manifest become silo_*.
  release.github.name stays "minio" with a comment - the GitHub repository has
  not been renamed yet, and pointing at pgsty/silo before the rename would 404.
  Also adds per-archive SPDX SBOMs and a keyless cosign signature over the
  checksum manifest, so the signed manifest covers archives and SBOMs together.
- nfpm.yml: package name silo, and the binary moves from /usr/local/bin/minio
  to /usr/bin/silo. /usr/local is not on the default PATH of a systemd unit and
  is not FHS-correct for a distribution package.
- package-release.sh, sign-release-rpms.sh and verify-build-provenance.sh follow
  the new names; the RPM signing script asserts NAME=silo and the new four-file
  payload. nfpm is now invoked from the repository root so relative script paths
  in the config resolve regardless of the caller's directory.

Package relationships are deliberately empty
No Provides, Obsoletes, Replaces or package-level Conflicts. Obsoletes: minio
cannot distinguish a pgsty package from upstream's own identically named one,
so an unattended dnf upgrade could silently swap a different vendor's product
for this one. With no relationships, both packages coexist, their file sets do
not overlap, and migration and rollback are single explicit commands. The
mutual exclusion lives in the unit instead: silo.service carries
Conflicts=minio.service plus After=minio.service.

Payload, from two files to four
- /usr/bin/silo
- /usr/lib/systemd/system/silo.service
- /etc/default/silo, installed config|noreplace
- /usr/lib/sysusers.d/silo.conf

The old package shipped a unit referencing an account nothing created, so a
clean install could not start. postinstall.sh now creates the silo system
account through systemd-sysusers, useradd or BusyBox adduser in that order and
runs daemon-reload. It never stops a service, never chowns data and never
touches /etc/default/minio. preremove.sh disables silo.service only on a real
removal - Debian "remove", RPM 0, Alpine's dotted version - so upgrades leave
the running service alone. lifecycle_test.sh exercises both against a stubbed
PATH, so a green run cannot create an account or touch the host.

silo.service reads /etc/default/minio then /etc/default/silo, in that order, so
an existing node's MINIO_* values keep working and the new file overrides them.
The packaged silo.env therefore ships comments only: any active assignment
would shadow the legacy file with an empty value.

Makefile: build/install/install-race produce ./silo, and the docker target now
assembles a context from a locally built linux binary plus Dockerfile.goreleaser
instead of the deleted Dockerfile. The hotfix, hotfix-push, docker-hotfix and
docker-hotfix-push targets are gone - they downloaded upstream's pkger, signed
with upstream's minisign key and scp'd to dl-N.minio.io. verifiers now depends
on a new rebrand-guard target.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 08:47:44 +08:00

142 lines
4.6 KiB
Bash
Executable File

#!/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}/silo.service"
defaults_file="${repo_dir}/silo.env"
sysusers_file="${repo_dir}/silo.sysusers"
postinstall_file="${repo_dir}/buildscripts/package/postinstall.sh"
preremove_file="${repo_dir}/buildscripts/package/preremove.sh"
if [ ! -f "${unit_file}" ]; then
echo "Missing systemd unit: ${unit_file}" >&2
exit 1
fi
if [ ! -f "${defaults_file}" ]; then
echo "Missing defaults file: ${defaults_file}" >&2
exit 1
fi
if [ ! -f "${sysusers_file}" ]; then
echo "Missing sysusers file: ${sysusers_file}" >&2
exit 1
fi
for lifecycle_script in "${postinstall_file}" "${preremove_file}"; do
if [ ! -x "${lifecycle_script}" ]; then
echo "Missing executable package lifecycle script: ${lifecycle_script}" >&2
exit 1
fi
done
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}/silo_linux_${goarch}*/silo" | 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}/silo-${PKG_VERSION}-1.${rpm_arch}.rpm"
deb_file="${packages_dir}/silo_${PKG_VERSION}_${deb_arch}.deb"
apk_file="${packages_dir}/silo_${PKG_VERSION}_${apk_arch}.apk"
(
cd "${repo_dir}"
PKG_VERSION="${PKG_VERSION}" NFPM_RELEASE=1 NFPM_ARCH="${goarch}" NFPM_SOURCE="${source}" NFPM_UNIT="${unit_file}" NFPM_DEFAULTS="${defaults_file}" NFPM_SYSUSERS="${sysusers_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_DEFAULTS="${defaults_file}" NFPM_SYSUSERS="${sysusers_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_DEFAULTS="${defaults_file}" NFPM_SYSUSERS="${sysusers_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