mirror of
https://github.com/pgsty/minio.git
synced 2026-08-09 07:43:29 +03:00
c46b16ec62
The transitional references land in one commit, because they are only correct together: the repository is pgsty/silo, its default branch is main, and nothing in the tree should point a user at the old names. Changed: - Workflow branch filters. go.yml and vulncheck.yml gated on `branches: master` for both push and pull_request, so renaming the default branch would have taken automatic CI offline with no error and no signal - the workflows would simply never trigger again. They now name main. - Release target. goreleaser's `release.github.name` becomes silo, which is what actually decides where a tagged build publishes. sign-release-rpms.sh's GH_REPO default follows. - The OCI `image.source` label, the Helm chart `sources` entry, the security advisory link in the issue-template config, and the go.mod comment citing the LDAP TLS fix. - 115 occurrences across README, README_ZH, SECURITY, CONTRIBUTING and 30 docs pages, including 72 links that also carried the master branch in their path. Those matter most: GitHub redirects clone, fetch, push and web URLs after a rename, but raw.githubusercontent.com does not, and neither follows a branch rename - every one of those links would 404 twice over. - Three error strings in cmd/erasure-sets.go, cmd/storage-errors.go and internal/config/errors.go that print an issue URL to operators. These are Go string literals inside rebrand-guard's brand allowlist, so the baseline is regenerated. The regeneration removes exactly those three entries and adds none; all twelve other protected sets, including the 9014 exported symbols, are byte-identical. - The transitional-naming disclaimers in README, README_ZH, SECURITY and CONTRIBUTING are dropped, since they no longer describe anything. Deliberately unchanged, all three because they exist to reject or freeze the old name rather than to point at it: - buildscripts/minio-upgrade.sh pins pgsty/minio@sha256:b6bfe72... - the frozen pre-rebrand image is the control group for the MinIO-to-Silo upgrade test. - helm-migration-guard rejects any rendered container still pulling pgsty/minio. - verify-rebrand.sh rejects the same in the delivery surfaces. Also unchanged: docs/config/README.md links to pgsty/mc/blob/master, and that repository's default branch really is still master. It moves when mc does. verify-rebrand.sh gains three assertions so this cannot silently regress: no source reference may name pgsty/minio outside the three allowlisted guards, no link may target pgsty/silo's master branch, and go.yml and vulncheck.yml must filter on main. Both new rejections were negative-tested - reintroducing a master branch filter and adding a pgsty/minio URL each fail the gate with the specific message. This commit assumes the rename actually happens. Until the GitHub branch and repository renames are executed, the links it introduces do not resolve. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
285 lines
9.1 KiB
Bash
Executable File
285 lines
9.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
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="PGSTY"
|
|
expected_packager="Ruohang Feng (@Vonng) <rh@vonng.com>"
|
|
expected_url="https://silo.pgsty.com"
|
|
expected_summary="S3-Interface Libre Object Storage, a community-maintained S3-compatible server."
|
|
expected_description="S3-Interface Libre Object Storage, a community-maintained S3-compatible server."
|
|
expected_license="AGPL-3.0-or-later"
|
|
expected_group="Applications/File"
|
|
expected_payload="/etc/default/silo
|
|
/usr/bin/silo
|
|
/usr/lib/systemd/system/silo.service
|
|
/usr/lib/sysusers.d/silo.conf"
|
|
repository="${GH_REPO:-pgsty/silo}"
|
|
container="${DNFUPDATE_CONTAINER:-dnfupdate}"
|
|
upload=false
|
|
release_tag=""
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: buildscripts/sign-release-rpms.sh RELEASE.TAG [--upload] [--repo OWNER/REPO] [--container NAME]
|
|
|
|
Downloads the two unsigned RPMs from a Draft GitHub Release, signs them with
|
|
the expected Pigsty key in the local dnfupdate container, verifies the result,
|
|
and regenerates their .sha256sum files. Nothing is uploaded unless --upload is
|
|
provided.
|
|
EOF
|
|
}
|
|
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--upload)
|
|
upload=true
|
|
;;
|
|
--repo)
|
|
shift
|
|
if [ "$#" -eq 0 ]; then
|
|
echo "--repo requires OWNER/REPO" >&2
|
|
exit 1
|
|
fi
|
|
repository="$1"
|
|
;;
|
|
--container)
|
|
shift
|
|
if [ "$#" -eq 0 ]; then
|
|
echo "--container requires a name" >&2
|
|
exit 1
|
|
fi
|
|
container="$1"
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
-*)
|
|
echo "Unknown option: $1" >&2
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
*)
|
|
if [ -n "${release_tag}" ]; then
|
|
echo "Only one release tag may be specified" >&2
|
|
exit 1
|
|
fi
|
|
release_tag="$1"
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [ -z "${release_tag}" ]; then
|
|
usage >&2
|
|
exit 1
|
|
fi
|
|
|
|
for command in docker gh; do
|
|
if ! command -v "${command}" >/dev/null 2>&1; then
|
|
echo "${command} is required" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
version_hyphen="${release_tag#RELEASE.}"
|
|
package_version="$(printf '%s\n' "${version_hyphen}" | sed -E \
|
|
's/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2})-([0-9]{2})-([0-9]{2})Z$/\1\2\3\4\5\6.0.0/')"
|
|
if [ "${package_version}" = "${version_hyphen}" ]; then
|
|
echo "Invalid release tag: ${release_tag}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$(gh release view "${release_tag}" --repo "${repository}" --json isDraft --jq .isDraft)" != "true" ]; then
|
|
echo "Refusing to sign: ${release_tag} is not a Draft release" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$(docker inspect --format '{{.State.Running}}' "${container}" 2>/dev/null || true)" != "true" ]; then
|
|
echo "Signing container is not running: ${container}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
secret_fingerprints="$(docker exec "${container}" \
|
|
gpg --batch --with-colons --list-secret-keys 2>/dev/null |
|
|
awk -F: '$1 == "fpr" { print toupper($10) }')"
|
|
if ! printf '%s\n' "${secret_fingerprints}" | grep -Fxq "${expected_fingerprint}"; then
|
|
echo "Expected signing key is not available in ${container}: ${expected_fingerprint}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
repo_dir="$(cd "${script_dir}/.." && pwd)"
|
|
work_root="${SIGN_WORKDIR:-${repo_dir}/.release-sign}"
|
|
mkdir -p "${work_root}"
|
|
work_dir="$(mktemp -d "${work_root}/${release_tag}.XXXXXX")"
|
|
unsigned_dir="${work_dir}/unsigned"
|
|
signed_dir="${work_dir}/signed"
|
|
mkdir -p "${unsigned_dir}" "${signed_dir}"
|
|
chmod 700 "${work_dir}" "${unsigned_dir}" "${signed_dir}"
|
|
|
|
rpm_files=(
|
|
"silo-${package_version}-1.x86_64.rpm"
|
|
"silo-${package_version}-1.aarch64.rpm"
|
|
)
|
|
|
|
download_patterns=()
|
|
for rpm_file in "${rpm_files[@]}"; do
|
|
download_patterns+=(--pattern "${rpm_file}" --pattern "${rpm_file}.sha256sum")
|
|
done
|
|
|
|
echo "Downloading RPMs from Draft release ${repository}@${release_tag}"
|
|
gh release download "${release_tag}" --repo "${repository}" \
|
|
--dir "${unsigned_dir}" "${download_patterns[@]}"
|
|
|
|
sha256_digest() {
|
|
local file="$1"
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
sha256sum "${file}" | awk '{print $1}'
|
|
else
|
|
shasum -a 256 "${file}" | awk '{print $1}'
|
|
fi
|
|
}
|
|
|
|
for rpm_file in "${rpm_files[@]}"; do
|
|
rpm_path="${unsigned_dir}/${rpm_file}"
|
|
checksum_path="${rpm_path}.sha256sum"
|
|
test -s "${rpm_path}"
|
|
test -s "${checksum_path}"
|
|
|
|
actual_line="$(sha256_digest "${rpm_path}") ${rpm_file}"
|
|
published_line="$(tr -d '\n' < "${checksum_path}")"
|
|
if [ "${actual_line}" != "${published_line}" ]; then
|
|
echo "Checksum mismatch for ${rpm_file}" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
safe_tag="$(printf '%s' "${release_tag}" | tr -c 'A-Za-z0-9._-' '_')"
|
|
container_dir="/tmp/silo-sign-${safe_tag}-$$"
|
|
docker exec "${container}" mkdir -p "${container_dir}"
|
|
|
|
cleanup_container() {
|
|
local rpm_file
|
|
for rpm_file in "${rpm_files[@]}"; do
|
|
docker exec "${container}" rm -f "${container_dir}/${rpm_file}" >/dev/null 2>&1 || true
|
|
done
|
|
docker exec "${container}" rmdir "${container_dir}" >/dev/null 2>&1 || true
|
|
}
|
|
trap cleanup_container EXIT
|
|
|
|
assert_rpm_tag() {
|
|
local rpm_path="$1"
|
|
local tag="$2"
|
|
local expected="$3"
|
|
local actual
|
|
|
|
actual="$(docker exec "${container}" rpm -qp --queryformat "%{${tag}}" "${rpm_path}")"
|
|
if [ "${actual}" != "${expected}" ]; then
|
|
echo "Unexpected RPM ${tag}: ${actual}" >&2
|
|
echo "Expected RPM ${tag}: ${expected}" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
for rpm_file in "${rpm_files[@]}"; do
|
|
case "${rpm_file}" in
|
|
*.x86_64.rpm)
|
|
expected_arch="x86_64"
|
|
;;
|
|
*.aarch64.rpm)
|
|
expected_arch="aarch64"
|
|
;;
|
|
*)
|
|
echo "Unexpected RPM filename: ${rpm_file}" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
echo "Signing ${rpm_file} with ${expected_fingerprint}"
|
|
docker cp "${unsigned_dir}/${rpm_file}" "${container}:${container_dir}/${rpm_file}" >/dev/null
|
|
container_rpm="${container_dir}/${rpm_file}"
|
|
|
|
assert_rpm_tag "${container_rpm}" NAME silo
|
|
assert_rpm_tag "${container_rpm}" VERSION "${package_version}"
|
|
assert_rpm_tag "${container_rpm}" RELEASE 1
|
|
assert_rpm_tag "${container_rpm}" ARCH "${expected_arch}"
|
|
assert_rpm_tag "${container_rpm}" VENDOR "${expected_vendor}"
|
|
assert_rpm_tag "${container_rpm}" PACKAGER "${expected_packager}"
|
|
assert_rpm_tag "${container_rpm}" URL "${expected_url}"
|
|
assert_rpm_tag "${container_rpm}" LICENSE "${expected_license}"
|
|
assert_rpm_tag "${container_rpm}" GROUP "${expected_group}"
|
|
assert_rpm_tag "${container_rpm}" SUMMARY "${expected_summary}"
|
|
assert_rpm_tag "${container_rpm}" DESCRIPTION "${expected_description}"
|
|
|
|
rpm_payload="$(docker exec "${container}" rpm -qpl "${container_rpm}")"
|
|
if [ "${rpm_payload}" != "${expected_payload}" ]; then
|
|
echo "Unexpected RPM payload for ${rpm_file}:" >&2
|
|
printf '%s\n' "${rpm_payload}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
docker exec "${container}" rpmsign \
|
|
--define "_gpg_name ${expected_fingerprint}" \
|
|
--addsign "${container_rpm}"
|
|
|
|
signature_output="$(docker exec "${container}" rpmkeys --checksig --verbose "${container_rpm}")"
|
|
printf '%s\n' "${signature_output}"
|
|
if ! printf '%s\n' "${signature_output}" | tr '[:upper:]' '[:lower:]' | grep -q 'key id b9bd8b20: ok'; then
|
|
echo "Signature verification failed for ${rpm_file}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
docker cp "${container}:${container_dir}/${rpm_file}" "${signed_dir}/${rpm_file}" >/dev/null
|
|
signed_digest="$(sha256_digest "${signed_dir}/${rpm_file}")"
|
|
printf '%s %s' "${signed_digest}" "${rpm_file}" > "${signed_dir}/${rpm_file}.sha256sum"
|
|
|
|
docker exec "${container}" rpm -qp --queryformat \
|
|
$'Name: %{NAME}\nVersion: %{VERSION}-%{RELEASE}\nArch: %{ARCH}\nVendor: %{VENDOR}\nPackager: %{PACKAGER}\nURL: %{URL}\n' \
|
|
"${container_rpm}"
|
|
echo "SHA256: ${signed_digest}"
|
|
done
|
|
|
|
if [ "${upload}" = true ]; then
|
|
upload_files=()
|
|
for rpm_file in "${rpm_files[@]}"; do
|
|
upload_files+=("${signed_dir}/${rpm_file}" "${signed_dir}/${rpm_file}.sha256sum")
|
|
done
|
|
|
|
echo "Replacing RPMs in Draft release ${release_tag}"
|
|
gh release upload "${release_tag}" --repo "${repository}" --clobber "${upload_files[@]}"
|
|
|
|
for rpm_file in "${rpm_files[@]}"; do
|
|
for asset in "${rpm_file}" "${rpm_file}.sha256sum"; do
|
|
local_digest="sha256:$(sha256_digest "${signed_dir}/${asset}")"
|
|
remote_digest=""
|
|
for attempt in 1 2 3 4 5; do
|
|
remote_digest="$(gh release view "${release_tag}" --repo "${repository}" --json assets \
|
|
--jq ".assets[] | select(.name == \"${asset}\") | .digest")"
|
|
if [ "${local_digest}" = "${remote_digest}" ]; then
|
|
break
|
|
fi
|
|
if [ "${attempt}" -lt 5 ]; then
|
|
sleep 2
|
|
fi
|
|
done
|
|
if [ "${local_digest}" != "${remote_digest}" ]; then
|
|
echo "GitHub asset digest mismatch for ${asset}" >&2
|
|
echo "Local: ${local_digest}" >&2
|
|
echo "Remote: ${remote_digest}" >&2
|
|
exit 1
|
|
fi
|
|
echo "Verified GitHub asset: ${asset} ${remote_digest}"
|
|
done
|
|
done
|
|
else
|
|
echo
|
|
echo "Signed RPMs are ready for review in: ${signed_dir}"
|
|
echo "Re-run with --upload to replace the RPM assets in the Draft release."
|
|
fi
|