From 6387fa41de2cef4375eb9b884e88298740592069 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Sun, 5 Apr 2026 20:21:30 -0700 Subject: [PATCH 01/43] [ci] Refactor RPM nfpm configs for reuse Parameterize nfpm config paths with env vars (${NFPM_CHANGELOG}, ${NFPM_SIGNING_KEY}) and resolve them via envsubst before calling nfpm. This enables DEB packaging to reuse the same template pattern. - Add gettext to RPM builder Dockerfile for envsubst - Add DOCKERFILE param to build-builder-image.sh for future DEB image - Clean up root-owned build/gpg in RPM workflow cleanup No functional change to RPM output. --- .github/packaging/Dockerfile | 2 ++ .github/packaging/nfpm/avalanchego.yml | 5 ++--- .github/packaging/nfpm/subnet-evm.yml | 5 ++--- .github/packaging/scripts/build-builder-image.sh | 4 ++++ .github/packaging/scripts/build-rpm.sh | 11 ++++++++--- .github/workflows/build-rpm-release.yml | 1 + 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.github/packaging/Dockerfile b/.github/packaging/Dockerfile index 3a7d729c05d9..024ac73562bd 100644 --- a/.github/packaging/Dockerfile +++ b/.github/packaging/Dockerfile @@ -15,10 +15,12 @@ ARG TARGETARCH # Install build dependencies # - gcc: required for cgo (CGO_ENABLED=1 in scripts/constants.sh) +# - gettext: envsubst for nfpm config template expansion # - gnupg2: GPG signing of RPM packages # - git: version detection in build scripts RUN dnf install -y \ gcc \ + gettext \ gnupg2 \ git \ && dnf clean all diff --git a/.github/packaging/nfpm/avalanchego.yml b/.github/packaging/nfpm/avalanchego.yml index 30247b703f25..94147cc9fd00 100644 --- a/.github/packaging/nfpm/avalanchego.yml +++ b/.github/packaging/nfpm/avalanchego.yml @@ -13,9 +13,8 @@ contents: expand: true file_info: mode: 0755 -# changelog and key_file paths are set to well-known locations by build-rpm.sh -changelog: "/build/build/nfpm-changelog.yml" +changelog: "${NFPM_CHANGELOG}" rpm: compression: zstd signature: - key_file: "/build/build/gpg/signing-key.asc" + key_file: "${NFPM_SIGNING_KEY}" diff --git a/.github/packaging/nfpm/subnet-evm.yml b/.github/packaging/nfpm/subnet-evm.yml index 7e129a3e54dc..bbe13bb15550 100644 --- a/.github/packaging/nfpm/subnet-evm.yml +++ b/.github/packaging/nfpm/subnet-evm.yml @@ -14,9 +14,8 @@ contents: expand: true file_info: mode: 0755 -# changelog and key_file paths are set to well-known locations by build-rpm.sh -changelog: "/build/build/nfpm-changelog.yml" +changelog: "${NFPM_CHANGELOG}" rpm: compression: zstd signature: - key_file: "/build/build/gpg/signing-key.asc" + key_file: "${NFPM_SIGNING_KEY}" diff --git a/.github/packaging/scripts/build-builder-image.sh b/.github/packaging/scripts/build-builder-image.sh index f134922fa6cf..df8672fed2eb 100755 --- a/.github/packaging/scripts/build-builder-image.sh +++ b/.github/packaging/scripts/build-builder-image.sh @@ -9,6 +9,9 @@ # GO_VERSION - Go version to install (e.g., "1.24.12") # DOCKER_IMAGE - Name for the built Docker image # CONTEXT_DIR - Path to the Dockerfile directory +# +# Optional env vars: +# DOCKERFILE - Dockerfile name (default: "Dockerfile") set -euo pipefail @@ -53,5 +56,6 @@ fi docker build "${build_flags[@]}" \ --build-arg GO_VERSION="${GO_VERSION}" \ --build-arg GO_CHECKSUM="${checksum}" \ + -f "${CONTEXT_DIR}/${DOCKERFILE:-Dockerfile}" \ -t "${DOCKER_IMAGE}" \ "${CONTEXT_DIR}" diff --git a/.github/packaging/scripts/build-rpm.sh b/.github/packaging/scripts/build-rpm.sh index 4f996e03fb9b..51688616dba1 100755 --- a/.github/packaging/scripts/build-rpm.sh +++ b/.github/packaging/scripts/build-rpm.sh @@ -26,8 +26,8 @@ REPO_ROOT="/build" PACKAGING_DIR="${REPO_ROOT}/.github/packaging" # Well-known paths referenced by nfpm configs -NFPM_CHANGELOG="${REPO_ROOT}/build/nfpm-changelog.yml" -NFPM_SIGNING_KEY="${REPO_ROOT}/build/gpg/signing-key.asc" +export NFPM_CHANGELOG="${REPO_ROOT}/build/nfpm-changelog.yml" +export NFPM_SIGNING_KEY="${REPO_ROOT}/build/gpg/signing-key.asc" echo "=== Building ${PACKAGE} RPM for ${RPM_ARCH} (tag: ${TAG}) ===" @@ -147,9 +147,14 @@ esac export VERSION RPM_ARCH +# nfpm does not expand env vars in top-level fields (changelog, signature.key_file). +# Preprocess the config template with envsubst so all ${VAR} references resolve. +NFPM_CONFIG_RESOLVED="${REPO_ROOT}/build/${PACKAGE}-rpm-resolved.yml" +envsubst < "${PACKAGING_DIR}/nfpm/${PACKAGE}.yml" > "${NFPM_CONFIG_RESOLVED}" + echo "Packaging ${RPM_FILENAME}..." nfpm package \ - --config "${PACKAGING_DIR}/nfpm/${PACKAGE}.yml" \ + --config "${NFPM_CONFIG_RESOLVED}" \ --packager rpm \ --target "${RPM_PATH}" diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index 2dbbf9b22ff9..39d57de389e0 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -100,4 +100,5 @@ jobs: rm -f "${GPG_KEY_FILE}" fi rm -rf build/rpm + sudo rm -rf build/gpg shell: bash From 0b641f88c516546bd24e4db16bf0c7c39d14a872 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:40:56 -0700 Subject: [PATCH 02/43] Rename RPM packaging files for naming consistency Add explicit -rpm/.rpm suffixes to match the DEB naming convention: - Dockerfile -> Dockerfile.rpm - avalanchego.yml -> avalanchego-rpm.yml - subnet-evm.yml -> subnet-evm-rpm.yml Update all references in build-builder-image.sh, build-rpm.sh, and Taskfile.yml. --- .github/packaging/{Dockerfile => Dockerfile.rpm} | 0 .github/packaging/Taskfile.yml | 1 + .../packaging/nfpm/{avalanchego.yml => avalanchego-rpm.yml} | 0 .github/packaging/nfpm/{subnet-evm.yml => subnet-evm-rpm.yml} | 0 .github/packaging/scripts/build-builder-image.sh | 4 ++-- .github/packaging/scripts/build-rpm.sh | 2 +- 6 files changed, 4 insertions(+), 3 deletions(-) rename .github/packaging/{Dockerfile => Dockerfile.rpm} (100%) rename .github/packaging/nfpm/{avalanchego.yml => avalanchego-rpm.yml} (100%) rename .github/packaging/nfpm/{subnet-evm.yml => subnet-evm-rpm.yml} (100%) diff --git a/.github/packaging/Dockerfile b/.github/packaging/Dockerfile.rpm similarity index 100% rename from .github/packaging/Dockerfile rename to .github/packaging/Dockerfile.rpm diff --git a/.github/packaging/Taskfile.yml b/.github/packaging/Taskfile.yml index 8d1def282896..f932340e22ec 100644 --- a/.github/packaging/Taskfile.yml +++ b/.github/packaging/Taskfile.yml @@ -50,6 +50,7 @@ tasks: GO_VERSION: '{{.PACKAGING_GO_VERSION}}' DOCKER_IMAGE: '{{.PACKAGING_DOCKER_IMAGE}}' CONTEXT_DIR: '{{.REPO_ROOT}}/.github/packaging' + DOCKERFILE: Dockerfile.rpm cmds: - cmd: '{{.REPO_ROOT}}/.github/packaging/scripts/build-builder-image.sh' diff --git a/.github/packaging/nfpm/avalanchego.yml b/.github/packaging/nfpm/avalanchego-rpm.yml similarity index 100% rename from .github/packaging/nfpm/avalanchego.yml rename to .github/packaging/nfpm/avalanchego-rpm.yml diff --git a/.github/packaging/nfpm/subnet-evm.yml b/.github/packaging/nfpm/subnet-evm-rpm.yml similarity index 100% rename from .github/packaging/nfpm/subnet-evm.yml rename to .github/packaging/nfpm/subnet-evm-rpm.yml diff --git a/.github/packaging/scripts/build-builder-image.sh b/.github/packaging/scripts/build-builder-image.sh index df8672fed2eb..90073ada3fa1 100755 --- a/.github/packaging/scripts/build-builder-image.sh +++ b/.github/packaging/scripts/build-builder-image.sh @@ -11,7 +11,7 @@ # CONTEXT_DIR - Path to the Dockerfile directory # # Optional env vars: -# DOCKERFILE - Dockerfile name (default: "Dockerfile") +# DOCKERFILE - Dockerfile name (default: "Dockerfile.rpm") set -euo pipefail @@ -56,6 +56,6 @@ fi docker build "${build_flags[@]}" \ --build-arg GO_VERSION="${GO_VERSION}" \ --build-arg GO_CHECKSUM="${checksum}" \ - -f "${CONTEXT_DIR}/${DOCKERFILE:-Dockerfile}" \ + -f "${CONTEXT_DIR}/${DOCKERFILE:-Dockerfile.rpm}" \ -t "${DOCKER_IMAGE}" \ "${CONTEXT_DIR}" diff --git a/.github/packaging/scripts/build-rpm.sh b/.github/packaging/scripts/build-rpm.sh index 51688616dba1..d09c2cfcb969 100755 --- a/.github/packaging/scripts/build-rpm.sh +++ b/.github/packaging/scripts/build-rpm.sh @@ -150,7 +150,7 @@ export VERSION RPM_ARCH # nfpm does not expand env vars in top-level fields (changelog, signature.key_file). # Preprocess the config template with envsubst so all ${VAR} references resolve. NFPM_CONFIG_RESOLVED="${REPO_ROOT}/build/${PACKAGE}-rpm-resolved.yml" -envsubst < "${PACKAGING_DIR}/nfpm/${PACKAGE}.yml" > "${NFPM_CONFIG_RESOLVED}" +envsubst < "${PACKAGING_DIR}/nfpm/${PACKAGE}-rpm.yml" > "${NFPM_CONFIG_RESOLVED}" echo "Packaging ${RPM_FILENAME}..." nfpm package \ From 28e9a976795667b2d3b8822e38955b82b7f71664 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:42:10 -0700 Subject: [PATCH 03/43] Add lib-build-common.sh with shared packaging functions Extract build_binary, generate_changelog, setup_gpg, and run_nfpm_package into a sourceable library. Both build-rpm.sh and build-deb.sh will source this to eliminate duplicated logic. --- .github/packaging/scripts/lib-build-common.sh | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 .github/packaging/scripts/lib-build-common.sh diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh new file mode 100644 index 000000000000..9676aee0bd69 --- /dev/null +++ b/.github/packaging/scripts/lib-build-common.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash + +# Shared functions for RPM and DEB package build scripts. +# +# Sourced (not executed) by build-rpm.sh and build-deb.sh. +# All functions expect REPO_ROOT to be set by the caller. + +# Initialize the build environment inside the container. +# Marks the bind-mounted source tree as git-safe, sources project +# scripts (constants.sh, git_commit.sh), and disables Go VCS stamping. +init_build_env() { + if ! git -C "${REPO_ROOT}" rev-parse HEAD &>/dev/null; then + git config --global --add safe.directory "${REPO_ROOT}" + fi + + # shellcheck disable=SC1091 + source "${REPO_ROOT}/scripts/constants.sh" + # shellcheck disable=SC1091 + source "${REPO_ROOT}/scripts/git_commit.sh" + + # shellcheck disable=SC2154 + echo "Git commit: ${git_commit}" + + # Disable Go's automatic VCS stamping — the commit hash is passed + # explicitly via AVALANCHEGO_COMMIT and -ldflags instead. + export GOFLAGS="${GOFLAGS:-} -buildvcs=false" +} + +# Build the binary for the specified package. +# Sets BINARY_PATH (global) as a side effect. +# +# Args: package_name ("avalanchego" or "subnet-evm") +build_binary() { + local package="${1:?package name required}" + + case "${package}" in + avalanchego) + echo "Building avalanchego..." + "${REPO_ROOT}/scripts/build.sh" + # shellcheck disable=SC2154 + BINARY_PATH="${avalanchego_path}" + ;; + subnet-evm) + echo "Building subnet-evm..." + SUBNET_EVM_VM_ID=$( + grep '^DEFAULT_VM_ID=' "${REPO_ROOT}/graft/subnet-evm/scripts/constants.sh" \ + | cut -d'"' -f2 + ) + export SUBNET_EVM_VM_ID + echo "Subnet-EVM VM ID: ${SUBNET_EVM_VM_ID}" + + SUBNET_EVM_BINARY="${REPO_ROOT}/build/subnet-evm" + (cd "${REPO_ROOT}/graft/subnet-evm" && ./scripts/build.sh "${SUBNET_EVM_BINARY}") + BINARY_PATH="${SUBNET_EVM_BINARY}" + ;; + *) + echo "Unknown package: ${package}" >&2 + exit 1 + ;; + esac + + echo "Binary built at: ${BINARY_PATH}" +} + +# Generate the nfpm changelog file. +# +# Args: version (semver without "v" prefix) +generate_changelog() { + local version="${1:?version required}" + + mkdir -p "$(dirname "${NFPM_CHANGELOG}")" + cat > "${NFPM_CHANGELOG}" < + changes: + - note: "See https://github.com/ava-labs/avalanchego/releases/tag/v${version}" +EOF +} + +# Set up GPG signing (import provided key, reuse ephemeral, or generate new). +# +# Args: +# gpg_key_file - path to GPG private key, or empty for ephemeral +# public_key_out - output path for exported public key +# key_label - label for ephemeral key ("RPM" or "DEB") +setup_gpg() { + local gpg_key_file="${1:-}" + local public_key_out="${2:?public key output path required}" + local key_label="${3:-Package}" + + mkdir -p "$(dirname "${NFPM_SIGNING_KEY}")" + + if [[ -n "${gpg_key_file}" ]]; then + echo "Using provided GPG key for signing" + gpg --batch --import "${gpg_key_file}" + cp "${gpg_key_file}" "${NFPM_SIGNING_KEY}" + elif [[ -f "${NFPM_SIGNING_KEY}" ]]; then + echo "Reusing existing ephemeral GPG key" + gpg --batch --import "${NFPM_SIGNING_KEY}" + else + echo "Generating ephemeral GPG key for signing" + gpg --batch --gen-key < "${NFPM_SIGNING_KEY}" + fi + + gpg --batch --armor --export "security@avalabs.org" > "${public_key_out}" + echo "GPG public key exported to: ${public_key_out}" +} + +# Package with nfpm after envsubst preprocessing. +# +# nfpm does not expand env vars in top-level fields (changelog, +# signature.key_file). Preprocess the config template with envsubst +# so all ${VAR} references resolve before nfpm sees them. +# +# Args: +# config_template - path to nfpm YAML template (with ${VAR} placeholders) +# resolved_path - output path for the preprocessed config +# packager - nfpm packager name ("rpm" or "deb") +# target_path - output path for the built package +run_nfpm_package() { + local config_template="${1:?config template path required}" + local resolved_path="${2:?resolved config path required}" + local packager="${3:?packager name required}" + local target_path="${4:?target path required}" + + mkdir -p "$(dirname "${target_path}")" + + envsubst < "${config_template}" > "${resolved_path}" + + echo "Packaging $(basename "${target_path}")..." + nfpm package \ + --config "${resolved_path}" \ + --packager "${packager}" \ + --target "${target_path}" +} From e6214350d3bbc6f9a2ce44ebf8e447fc9bd020bd Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:42:39 -0700 Subject: [PATCH 04/43] Extract shared smoke test into smoke-test.sh Move the avalanchego/subnet-evm version and commit verification logic into a standalone script. validate-rpm.sh bind-mounts it into the validation container. validate-deb.sh (DEB PR) will reuse the same script. --- .github/packaging/scripts/smoke-test.sh | 52 +++++++++++++++++++++++ .github/packaging/scripts/validate-rpm.sh | 39 ++++------------- 2 files changed, 60 insertions(+), 31 deletions(-) create mode 100755 .github/packaging/scripts/smoke-test.sh diff --git a/.github/packaging/scripts/smoke-test.sh b/.github/packaging/scripts/smoke-test.sh new file mode 100755 index 000000000000..e65977b21a7e --- /dev/null +++ b/.github/packaging/scripts/smoke-test.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +# Smoke test installed avalanchego and subnet-evm packages. +# +# Runs inside a validation container after packages have been installed. +# Verifies that binaries are present, executable, and report the expected +# version/commit information. +# +# Args: +# $1 - path to avalanchego binary +# $2 - plugin directory path (subnet-evm VM ID appended by this script) +# $3 - expected full git commit hash +# $4 - subnet-evm VM ID + +set -euxo pipefail + +AVAGO_BIN="${1:?avalanchego binary path required}" +PLUGIN_DIR="${2:?plugin directory path required}" +GIT_COMMIT="${3:?git commit hash required}" +SUBNET_EVM_VM_ID="${4:?subnet-evm VM ID required}" + +# ── Smoke test avalanchego ──────────────────────────────────────── + +output=$("${AVAGO_BIN}" --version) +echo "avalanchego --version: ${output}" +if [[ "${output}" != avalanchego/* ]]; then + echo "ERROR: --version output does not start with avalanchego/" >&2 + exit 1 +fi +if [[ "${output}" != *"${GIT_COMMIT}"* ]]; then + echo "ERROR: avalanchego --version output does not contain expected commit ${GIT_COMMIT}" >&2 + echo "Output: ${output}" >&2 + exit 1 +fi + +# ── Verify subnet-evm plugin ───────────────────────────────────── + +plugin="${PLUGIN_DIR}/${SUBNET_EVM_VM_ID}" +if [[ ! -x "${plugin}" ]]; then + echo "ERROR: subnet-evm plugin not found or not executable at ${plugin}" >&2 + exit 1 +fi + +evm_output=$("${plugin}" --version) +echo "subnet-evm --version: ${evm_output}" +if [[ "${evm_output}" != *"${GIT_COMMIT}"* ]]; then + echo "ERROR: subnet-evm --version output does not contain expected commit ${GIT_COMMIT}" >&2 + echo "Output: ${evm_output}" >&2 + exit 1 +fi + +echo "All package validations passed" diff --git a/.github/packaging/scripts/validate-rpm.sh b/.github/packaging/scripts/validate-rpm.sh index 12ae1d2a4087..290861dd6148 100755 --- a/.github/packaging/scripts/validate-rpm.sh +++ b/.github/packaging/scripts/validate-rpm.sh @@ -28,6 +28,7 @@ fi REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" RPM_DIR="${REPO_ROOT}/build/rpm" +SCRIPTS_DIR="${REPO_ROOT}/.github/packaging/scripts" # Source VM ID from constants.sh (canonical definition) SUBNET_EVM_VM_ID=$( @@ -49,6 +50,7 @@ done echo "=== Validating RPMs in fresh Rocky Linux 9 container ===" docker run --rm \ -v "${RPM_DIR}:/rpms:ro" \ + -v "${SCRIPTS_DIR}/smoke-test.sh:/smoke-test.sh:ro" \ rockylinux:9 \ bash -euxc ' # Import GPG key and verify signatures if available @@ -64,37 +66,12 @@ docker run --rm \ rpm -ivh "/rpms/avalanchego-'"${TAG}"'-'"${RPM_ARCH}"'.rpm" rpm -ivh "/rpms/subnet-evm-'"${TAG}"'-'"${RPM_ARCH}"'.rpm" - # Smoke test avalanchego - full_commit="'"${GIT_COMMIT}"'" - output=$(/var/opt/avalanchego/bin/avalanchego --version) - echo "avalanchego --version: ${output}" - if [[ "${output}" != avalanchego/* ]]; then - echo "ERROR: --version output does not start with avalanchego/" >&2 - exit 1 - fi - if [[ "${output}" != *"${full_commit}"* ]]; then - echo "ERROR: avalanchego --version output does not contain expected commit ${full_commit}" >&2 - echo "Output: ${output}" >&2 - exit 1 - fi - - # Verify subnet-evm plugin - plugin="/var/opt/avalanchego/plugins/'"${SUBNET_EVM_VM_ID}"'" - if [[ ! -x "${plugin}" ]]; then - echo "ERROR: subnet-evm plugin not found or not executable" >&2 - exit 1 - fi - - # Smoke test subnet-evm version and commit - evm_output=$("${plugin}" --version) - echo "subnet-evm --version: ${evm_output}" - if [[ "${evm_output}" != *"${full_commit}"* ]]; then - echo "ERROR: subnet-evm --version output does not contain expected commit ${full_commit}" >&2 - echo "Output: ${evm_output}" >&2 - exit 1 - fi - - echo "All RPM validations passed" + # Run shared smoke test + bash /smoke-test.sh \ + /var/opt/avalanchego/bin/avalanchego \ + /var/opt/avalanchego/plugins \ + "'"${GIT_COMMIT}"'" \ + "'"${SUBNET_EVM_VM_ID}"'" ' echo "=== RPM validation complete ===" From df3e925d680079023403b4647259b1a9f8be23df Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:43:32 -0700 Subject: [PATCH 05/43] Rewrite build-rpm.sh to use lib-build-common.sh Replace ~160 lines of inline logic with calls to shared functions. RPM-specific code (nfpm passphrase handling) remains in this script. --- .github/packaging/scripts/build-rpm.sh | 130 ++++--------------------- 1 file changed, 19 insertions(+), 111 deletions(-) diff --git a/.github/packaging/scripts/build-rpm.sh b/.github/packaging/scripts/build-rpm.sh index d09c2cfcb969..948c4713d4cb 100755 --- a/.github/packaging/scripts/build-rpm.sh +++ b/.github/packaging/scripts/build-rpm.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Build and validate an RPM package inside the container. +# Build and sign an RPM package inside the container. # # Required env vars: # PACKAGE - "avalanchego" or "subnet-evm" @@ -25,121 +25,31 @@ set -euo pipefail REPO_ROOT="/build" PACKAGING_DIR="${REPO_ROOT}/.github/packaging" +# shellcheck disable=SC1091 +source "${PACKAGING_DIR}/scripts/lib-build-common.sh" + # Well-known paths referenced by nfpm configs export NFPM_CHANGELOG="${REPO_ROOT}/build/nfpm-changelog.yml" export NFPM_SIGNING_KEY="${REPO_ROOT}/build/gpg/signing-key.asc" echo "=== Building ${PACKAGE} RPM for ${RPM_ARCH} (tag: ${TAG}) ===" -# ── Step 1: Build binary ────────────────────────────────────────── - -# In CI, the bind-mounted source tree is owned by the host user. Mark it -# as safe so that git works inside the container (needed by older build -# scripts that resolve the commit hash via git rather than AVALANCHEGO_COMMIT). -if ! git -C "${REPO_ROOT}" rev-parse HEAD &>/dev/null; then - git config --global --add safe.directory "${REPO_ROOT}" -fi - -# shellcheck disable=SC1091 -source "${REPO_ROOT}/scripts/constants.sh" -# shellcheck disable=SC1091 -source "${REPO_ROOT}/scripts/git_commit.sh" - -# shellcheck disable=SC2154 -echo "Git commit: ${git_commit}" - -# Disable Go's automatic VCS stamping — the commit hash is passed -# explicitly via AVALANCHEGO_COMMIT and -ldflags instead. -export GOFLAGS="${GOFLAGS:-} -buildvcs=false" - -case "${PACKAGE}" in - avalanchego) - echo "Building avalanchego..." - "${REPO_ROOT}/scripts/build.sh" - # shellcheck disable=SC2154 - BINARY_PATH="${avalanchego_path}" - ;; - subnet-evm) - echo "Building subnet-evm..." - # Source VM ID from constants.sh (canonical definition) - SUBNET_EVM_VM_ID=$( - grep '^DEFAULT_VM_ID=' "${REPO_ROOT}/graft/subnet-evm/scripts/constants.sh" \ - | cut -d'"' -f2 - ) - export SUBNET_EVM_VM_ID - echo "Subnet-EVM VM ID: ${SUBNET_EVM_VM_ID}" - - SUBNET_EVM_BINARY="${REPO_ROOT}/build/subnet-evm" - # Build from subnet-evm directory — build.sh uses relative glob "plugin/"*.go - (cd "${REPO_ROOT}/graft/subnet-evm" && ./scripts/build.sh "${SUBNET_EVM_BINARY}") - BINARY_PATH="${SUBNET_EVM_BINARY}" - ;; - *) - echo "Unknown package: ${PACKAGE}" >&2 - exit 1 - ;; -esac - -echo "Binary built at: ${BINARY_PATH}" - -# ── Step 2: Generate changelog ──────────────────────────────────── +init_build_env +build_binary "${PACKAGE}" +generate_changelog "${VERSION}" -cat > "${NFPM_CHANGELOG}" < - changes: - - note: "See https://github.com/ava-labs/avalanchego/releases/tag/v${VERSION}" -EOF +# ── GPG signing ─────────────────────────────────────────────────── -# ── Step 3: Set up GPG signing ──────────────────────────────────── - -GPG_WORKDIR="${REPO_ROOT}/build/gpg" -mkdir -p "${GPG_WORKDIR}" GPG_PUBLIC_KEY="${OUTPUT_DIR}/RPM-GPG-KEY-avalanchego" +setup_gpg "${RPM_GPG_KEY_FILE:-}" "${GPG_PUBLIC_KEY}" "RPM" -if [[ -n "${RPM_GPG_KEY_FILE:-}" ]]; then - echo "Using provided GPG key for signing" - gpg --batch --import "${RPM_GPG_KEY_FILE}" - # Copy to well-known path for nfpm config - cp "${RPM_GPG_KEY_FILE}" "${NFPM_SIGNING_KEY}" -elif [[ -f "${NFPM_SIGNING_KEY}" ]]; then - # Reuse ephemeral key from a previous build (e.g., avalanchego built before subnet-evm) - echo "Reusing existing ephemeral GPG key" - gpg --batch --import "${NFPM_SIGNING_KEY}" - export NFPM_RPM_PASSPHRASE="" -else - echo "Generating ephemeral GPG key for signing" - - gpg --batch --gen-key < "${NFPM_SIGNING_KEY}" +# For ephemeral keys (no passphrase), nfpm needs an empty passphrase +if [[ -z "${RPM_GPG_KEY_FILE:-}" && -z "${NFPM_RPM_PASSPHRASE+x}" ]]; then export NFPM_RPM_PASSPHRASE="" fi -# Export public key for verification -gpg --batch --armor --export "security@avalabs.org" > "${GPG_PUBLIC_KEY}" -echo "GPG public key exported to: ${GPG_PUBLIC_KEY}" - -# ── Step 4: Package with nfpm ───────────────────────────────────── +# ── Package with nfpm ───────────────────────────────────────────── -RPM_FILENAME="${PACKAGE}-${TAG}-${RPM_ARCH}.rpm" -RPM_PATH="${OUTPUT_DIR}/${RPM_FILENAME}" -mkdir -p "${OUTPUT_DIR}" - -# Set binary path env var for nfpm config (contents use expand: true) case "${PACKAGE}" in avalanchego) export AVALANCHEGO_BINARY="${BINARY_PATH}" ;; subnet-evm) export SUBNET_EVM_BINARY="${BINARY_PATH}" ;; @@ -147,15 +57,13 @@ esac export VERSION RPM_ARCH -# nfpm does not expand env vars in top-level fields (changelog, signature.key_file). -# Preprocess the config template with envsubst so all ${VAR} references resolve. -NFPM_CONFIG_RESOLVED="${REPO_ROOT}/build/${PACKAGE}-rpm-resolved.yml" -envsubst < "${PACKAGING_DIR}/nfpm/${PACKAGE}-rpm.yml" > "${NFPM_CONFIG_RESOLVED}" +RPM_FILENAME="${PACKAGE}-${TAG}-${RPM_ARCH}.rpm" +RPM_PATH="${OUTPUT_DIR}/${RPM_FILENAME}" -echo "Packaging ${RPM_FILENAME}..." -nfpm package \ - --config "${NFPM_CONFIG_RESOLVED}" \ - --packager rpm \ - --target "${RPM_PATH}" +run_nfpm_package \ + "${PACKAGING_DIR}/nfpm/${PACKAGE}-rpm.yml" \ + "${REPO_ROOT}/build/${PACKAGE}-rpm-resolved.yml" \ + rpm \ + "${RPM_PATH}" echo "RPM built: ${RPM_PATH}" From 7cedfee17b5b4dd028a99da82304f7227b5f843d Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:21:41 -0700 Subject: [PATCH 06/43] Fix ephemeral key passphrase clearing in build-rpm.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The refactored code only cleared NFPM_RPM_PASSPHRASE when unset, but the workflow always exports it from secrets — even on PR builds where RPM_GPG_KEY_FILE is skipped. Clear unconditionally when no key file is provided, matching the original behavior. --- .github/packaging/scripts/build-rpm.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/packaging/scripts/build-rpm.sh b/.github/packaging/scripts/build-rpm.sh index 948c4713d4cb..b4907985bcab 100755 --- a/.github/packaging/scripts/build-rpm.sh +++ b/.github/packaging/scripts/build-rpm.sh @@ -43,8 +43,10 @@ generate_changelog "${VERSION}" GPG_PUBLIC_KEY="${OUTPUT_DIR}/RPM-GPG-KEY-avalanchego" setup_gpg "${RPM_GPG_KEY_FILE:-}" "${GPG_PUBLIC_KEY}" "RPM" -# For ephemeral keys (no passphrase), nfpm needs an empty passphrase -if [[ -z "${RPM_GPG_KEY_FILE:-}" && -z "${NFPM_RPM_PASSPHRASE+x}" ]]; then +# Ephemeral keys have no passphrase; nfpm needs the variable set to empty. +# The workflow always exports NFPM_RPM_PASSPHRASE (from secrets) but skips +# RPM_GPG_KEY_FILE on PR builds, so we must clear it unconditionally here. +if [[ -z "${RPM_GPG_KEY_FILE:-}" ]]; then export NFPM_RPM_PASSPHRASE="" fi From 58c40cc507e37c8e283ffaa2ba44e52640362ace Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:45:39 -0700 Subject: [PATCH 07/43] Handle docker buildx inspect failure in build-builder-image.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With set -eo pipefail, a non-zero exit from docker buildx inspect kills the script before docker build runs. Add || true so the driver detection is best-effort — falling back to no --load flag, which is correct for the default docker driver. --- .github/packaging/scripts/build-builder-image.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/packaging/scripts/build-builder-image.sh b/.github/packaging/scripts/build-builder-image.sh index 90073ada3fa1..81c5b2ad3c05 100755 --- a/.github/packaging/scripts/build-builder-image.sh +++ b/.github/packaging/scripts/build-builder-image.sh @@ -48,7 +48,7 @@ build_flags=() build_driver=$( docker buildx inspect 2>/dev/null \ | awk '/^Driver:/ { print $2; exit }' -) +) || true if [[ "${build_driver}" == "docker-container" ]]; then build_flags+=(--load) fi From 97eaee37760b4480506772998ca0cb90eb0e8e21 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 6 Apr 2026 20:00:50 -0700 Subject: [PATCH 08/43] Standardize PACKAGE_ARCH and extract resolve_subnet_evm_vm_id - Rename RPM_ARCH to PACKAGE_ARCH across nfpm configs, build-rpm.sh, validate-rpm.sh, and Taskfile (values unchanged, only var name) - Extract resolve_subnet_evm_vm_id() into lib-build-common.sh to eliminate duplication across build and validate scripts --- .github/packaging/Taskfile.yml | 8 ++-- .github/packaging/nfpm/avalanchego-rpm.yml | 2 +- .github/packaging/nfpm/subnet-evm-rpm.yml | 2 +- .github/packaging/scripts/build-rpm.sh | 10 ++--- .github/packaging/scripts/lib-build-common.sh | 20 ++++++--- .github/packaging/scripts/validate-rpm.sh | 41 +++++++++---------- 6 files changed, 46 insertions(+), 37 deletions(-) diff --git a/.github/packaging/Taskfile.yml b/.github/packaging/Taskfile.yml index f932340e22ec..60fd39182795 100644 --- a/.github/packaging/Taskfile.yml +++ b/.github/packaging/Taskfile.yml @@ -57,7 +57,7 @@ tasks: build-avalanchego-rpm: desc: Builds RPM for avalanchego vars: - RPM_ARCH: '{{.RPM_ARCH | default .PACKAGING_HOST_ARCH}}' + PACKAGE_ARCH: '{{.PACKAGE_ARCH | default .PACKAGING_HOST_ARCH}}' RPM_TAG: '{{.PACKAGING_TAG}}' deps: [build-builder-docker-image] cmds: @@ -70,7 +70,7 @@ tasks: -e PACKAGE=avalanchego -e VERSION={{trimPrefix "v" .RPM_TAG}} -e TAG={{.RPM_TAG}} - -e RPM_ARCH={{.RPM_ARCH}} + -e PACKAGE_ARCH={{.PACKAGE_ARCH}} -e OUTPUT_DIR=/output -e AVALANCHEGO_COMMIT={{.PACKAGING_GIT_COMMIT}} {{if .RPM_GPG_KEY_FILE}}-e RPM_GPG_KEY_FILE={{.RPM_GPG_KEY_FILE}}{{end}} @@ -81,7 +81,7 @@ tasks: build-subnet-evm-rpm: desc: Builds RPM for subnet-evm vars: - RPM_ARCH: '{{.RPM_ARCH | default .PACKAGING_HOST_ARCH}}' + PACKAGE_ARCH: '{{.PACKAGE_ARCH | default .PACKAGING_HOST_ARCH}}' RPM_TAG: '{{.PACKAGING_TAG}}' deps: [build-builder-docker-image] cmds: @@ -94,7 +94,7 @@ tasks: -e PACKAGE=subnet-evm -e VERSION={{trimPrefix "v" .RPM_TAG}} -e TAG={{.RPM_TAG}} - -e RPM_ARCH={{.RPM_ARCH}} + -e PACKAGE_ARCH={{.PACKAGE_ARCH}} -e OUTPUT_DIR=/output -e AVALANCHEGO_COMMIT={{.PACKAGING_GIT_COMMIT}} {{if .RPM_GPG_KEY_FILE}}-e RPM_GPG_KEY_FILE={{.RPM_GPG_KEY_FILE}}{{end}} diff --git a/.github/packaging/nfpm/avalanchego-rpm.yml b/.github/packaging/nfpm/avalanchego-rpm.yml index 94147cc9fd00..dcf6167290bd 100644 --- a/.github/packaging/nfpm/avalanchego-rpm.yml +++ b/.github/packaging/nfpm/avalanchego-rpm.yml @@ -1,5 +1,5 @@ name: avalanchego -arch: "${RPM_ARCH}" +arch: "${PACKAGE_ARCH}" version: "${VERSION}" maintainer: "Ava Labs " description: "AvalancheGo node — the official Avalanche protocol implementation" diff --git a/.github/packaging/nfpm/subnet-evm-rpm.yml b/.github/packaging/nfpm/subnet-evm-rpm.yml index bbe13bb15550..1ed5f887b008 100644 --- a/.github/packaging/nfpm/subnet-evm-rpm.yml +++ b/.github/packaging/nfpm/subnet-evm-rpm.yml @@ -1,5 +1,5 @@ name: subnet-evm -arch: "${RPM_ARCH}" +arch: "${PACKAGE_ARCH}" version: "${VERSION}" maintainer: "Ava Labs " description: "Subnet-EVM plugin for AvalancheGo" diff --git a/.github/packaging/scripts/build-rpm.sh b/.github/packaging/scripts/build-rpm.sh index b4907985bcab..1db666857532 100755 --- a/.github/packaging/scripts/build-rpm.sh +++ b/.github/packaging/scripts/build-rpm.sh @@ -6,7 +6,7 @@ # PACKAGE - "avalanchego" or "subnet-evm" # VERSION - Semantic version without "v" prefix (e.g., "1.14.1") # TAG - Git tag (e.g., "v1.14.1") -# RPM_ARCH - RPM architecture name ("x86_64" or "aarch64") +# PACKAGE_ARCH - RPM architecture name ("x86_64" or "aarch64") # OUTPUT_DIR - Directory for the output RPM (bind-mounted from host) # # Optional env vars: @@ -19,7 +19,7 @@ set -euo pipefail : "${PACKAGE:?PACKAGE must be set (avalanchego or subnet-evm)}" : "${VERSION:?VERSION must be set}" : "${TAG:?TAG must be set}" -: "${RPM_ARCH:?RPM_ARCH must be set}" +: "${PACKAGE_ARCH:?PACKAGE_ARCH must be set}" : "${OUTPUT_DIR:?OUTPUT_DIR must be set}" REPO_ROOT="/build" @@ -32,7 +32,7 @@ source "${PACKAGING_DIR}/scripts/lib-build-common.sh" export NFPM_CHANGELOG="${REPO_ROOT}/build/nfpm-changelog.yml" export NFPM_SIGNING_KEY="${REPO_ROOT}/build/gpg/signing-key.asc" -echo "=== Building ${PACKAGE} RPM for ${RPM_ARCH} (tag: ${TAG}) ===" +echo "=== Building ${PACKAGE} RPM for ${PACKAGE_ARCH} (tag: ${TAG}) ===" init_build_env build_binary "${PACKAGE}" @@ -57,9 +57,9 @@ case "${PACKAGE}" in subnet-evm) export SUBNET_EVM_BINARY="${BINARY_PATH}" ;; esac -export VERSION RPM_ARCH +export VERSION PACKAGE_ARCH -RPM_FILENAME="${PACKAGE}-${TAG}-${RPM_ARCH}.rpm" +RPM_FILENAME="${PACKAGE}-${TAG}-${PACKAGE_ARCH}.rpm" RPM_PATH="${OUTPUT_DIR}/${RPM_FILENAME}" run_nfpm_package \ diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh index 9676aee0bd69..d54249e9bb11 100644 --- a/.github/packaging/scripts/lib-build-common.sh +++ b/.github/packaging/scripts/lib-build-common.sh @@ -42,11 +42,7 @@ build_binary() { ;; subnet-evm) echo "Building subnet-evm..." - SUBNET_EVM_VM_ID=$( - grep '^DEFAULT_VM_ID=' "${REPO_ROOT}/graft/subnet-evm/scripts/constants.sh" \ - | cut -d'"' -f2 - ) - export SUBNET_EVM_VM_ID + resolve_subnet_evm_vm_id echo "Subnet-EVM VM ID: ${SUBNET_EVM_VM_ID}" SUBNET_EVM_BINARY="${REPO_ROOT}/build/subnet-evm" @@ -62,6 +58,20 @@ build_binary() { echo "Binary built at: ${BINARY_PATH}" } +# Resolve the subnet-evm VM ID from the canonical constants file. +# Sets SUBNET_EVM_VM_ID (global) as a side effect. +resolve_subnet_evm_vm_id() { + SUBNET_EVM_VM_ID=$( + grep '^DEFAULT_VM_ID=' "${REPO_ROOT}/graft/subnet-evm/scripts/constants.sh" \ + | cut -d'"' -f2 + ) + if [[ -z "${SUBNET_EVM_VM_ID}" ]]; then + echo "ERROR: could not resolve SUBNET_EVM_VM_ID" >&2 + exit 1 + fi + export SUBNET_EVM_VM_ID +} + # Generate the nfpm changelog file. # # Args: version (semver without "v" prefix) diff --git a/.github/packaging/scripts/validate-rpm.sh b/.github/packaging/scripts/validate-rpm.sh index 290861dd6148..14b220853c2e 100755 --- a/.github/packaging/scripts/validate-rpm.sh +++ b/.github/packaging/scripts/validate-rpm.sh @@ -10,36 +10,35 @@ # GIT_COMMIT - Full git commit hash used to build the binaries # # Optional env vars: -# RPM_ARCH - RPM architecture ("x86_64" or "aarch64"), defaults to host +# PACKAGE_ARCH - RPM architecture ("x86_64" or "aarch64"), defaults to host set -euo pipefail : "${TAG:?TAG must be set}" : "${GIT_COMMIT:?GIT_COMMIT must be set}" -if [[ -z "${RPM_ARCH:-}" ]]; then - arch=$(uname -m) - case "${arch}" in - x86_64) RPM_ARCH="x86_64" ;; - arm64) RPM_ARCH="aarch64" ;; - *) RPM_ARCH="${arch}" ;; - esac -fi - REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" RPM_DIR="${REPO_ROOT}/build/rpm" SCRIPTS_DIR="${REPO_ROOT}/.github/packaging/scripts" -# Source VM ID from constants.sh (canonical definition) -SUBNET_EVM_VM_ID=$( - grep '^DEFAULT_VM_ID=' "${REPO_ROOT}/graft/subnet-evm/scripts/constants.sh" \ - | cut -d'"' -f2 -) +# shellcheck disable=SC1091 +source "${SCRIPTS_DIR}/lib-build-common.sh" + +resolve_subnet_evm_vm_id + +if [[ -z "${PACKAGE_ARCH:-}" ]]; then + arch=$(uname -m) + case "${arch}" in + x86_64) PACKAGE_ARCH="x86_64" ;; + arm64) PACKAGE_ARCH="aarch64" ;; + *) PACKAGE_ARCH="${arch}" ;; + esac +fi # Verify expected files exist for f in \ - "avalanchego-${TAG}-${RPM_ARCH}.rpm" \ - "subnet-evm-${TAG}-${RPM_ARCH}.rpm" \ + "avalanchego-${TAG}-${PACKAGE_ARCH}.rpm" \ + "subnet-evm-${TAG}-${PACKAGE_ARCH}.rpm" \ ; do if [[ ! -f "${RPM_DIR}/${f}" ]]; then echo "ERROR: expected file not found: ${RPM_DIR}/${f}" >&2 @@ -56,15 +55,15 @@ docker run --rm \ # Import GPG key and verify signatures if available if [[ -f /rpms/RPM-GPG-KEY-avalanchego ]]; then rpm --import /rpms/RPM-GPG-KEY-avalanchego - rpm -K "/rpms/avalanchego-'"${TAG}"'-'"${RPM_ARCH}"'.rpm" - rpm -K "/rpms/subnet-evm-'"${TAG}"'-'"${RPM_ARCH}"'.rpm" + rpm -K "/rpms/avalanchego-'"${TAG}"'-'"${PACKAGE_ARCH}"'.rpm" + rpm -K "/rpms/subnet-evm-'"${TAG}"'-'"${PACKAGE_ARCH}"'.rpm" else echo "Skipping GPG verification (unsigned build)" fi # Install both packages - rpm -ivh "/rpms/avalanchego-'"${TAG}"'-'"${RPM_ARCH}"'.rpm" - rpm -ivh "/rpms/subnet-evm-'"${TAG}"'-'"${RPM_ARCH}"'.rpm" + rpm -ivh "/rpms/avalanchego-'"${TAG}"'-'"${PACKAGE_ARCH}"'.rpm" + rpm -ivh "/rpms/subnet-evm-'"${TAG}"'-'"${PACKAGE_ARCH}"'.rpm" # Run shared smoke test bash /smoke-test.sh \ From d3df3a22a2c60e7c2fb38568f53fac2f092d9df8 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 6 Apr 2026 20:01:20 -0700 Subject: [PATCH 09/43] Add lib-validate-common.sh with shared validation helpers Extract detect_host_arch() and assert_files_exist() for reuse by both RPM and DEB validation scripts. --- .../packaging/scripts/lib-validate-common.sh | 45 +++++++++++++++++++ .github/packaging/scripts/validate-rpm.sh | 23 +++------- 2 files changed, 50 insertions(+), 18 deletions(-) create mode 100644 .github/packaging/scripts/lib-validate-common.sh diff --git a/.github/packaging/scripts/lib-validate-common.sh b/.github/packaging/scripts/lib-validate-common.sh new file mode 100644 index 000000000000..eb1cac28fbea --- /dev/null +++ b/.github/packaging/scripts/lib-validate-common.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Shared functions for package validation scripts. +# Sourced by validate-rpm.sh and validate-deb.sh. + +# Detect host architecture for the given package format. +# Sets PACKAGE_ARCH (global) if not already set by the caller. +# +# Args: format ("RPM" or "DEB") +detect_host_arch() { + local format="${1:?format required}" + if [[ -n "${PACKAGE_ARCH:-}" ]]; then + return # already set by caller + fi + local arch + arch=$(uname -m) + case "${format}" in + RPM) + case "${arch}" in + x86_64) PACKAGE_ARCH="x86_64" ;; + arm64) PACKAGE_ARCH="aarch64" ;; + *) PACKAGE_ARCH="${arch}" ;; + esac + ;; + DEB) + case "${arch}" in + x86_64) PACKAGE_ARCH="amd64" ;; + aarch64|arm64) PACKAGE_ARCH="arm64" ;; + *) PACKAGE_ARCH="${arch}" ;; + esac + ;; + esac +} + +# Verify that all expected package files exist in a directory. +# +# Args: pkg_dir file1 [file2 ...] +assert_files_exist() { + local pkg_dir="${1:?package directory required}"; shift + for f in "$@"; do + if [[ ! -f "${pkg_dir}/${f}" ]]; then + echo "ERROR: expected file not found: ${pkg_dir}/${f}" >&2 + exit 1 + fi + done +} diff --git a/.github/packaging/scripts/validate-rpm.sh b/.github/packaging/scripts/validate-rpm.sh index 14b220853c2e..9e4c7b7f0e5a 100755 --- a/.github/packaging/scripts/validate-rpm.sh +++ b/.github/packaging/scripts/validate-rpm.sh @@ -23,28 +23,15 @@ SCRIPTS_DIR="${REPO_ROOT}/.github/packaging/scripts" # shellcheck disable=SC1091 source "${SCRIPTS_DIR}/lib-build-common.sh" +# shellcheck disable=SC1091 +source "${SCRIPTS_DIR}/lib-validate-common.sh" resolve_subnet_evm_vm_id +detect_host_arch RPM -if [[ -z "${PACKAGE_ARCH:-}" ]]; then - arch=$(uname -m) - case "${arch}" in - x86_64) PACKAGE_ARCH="x86_64" ;; - arm64) PACKAGE_ARCH="aarch64" ;; - *) PACKAGE_ARCH="${arch}" ;; - esac -fi - -# Verify expected files exist -for f in \ +assert_files_exist "${RPM_DIR}" \ "avalanchego-${TAG}-${PACKAGE_ARCH}.rpm" \ - "subnet-evm-${TAG}-${PACKAGE_ARCH}.rpm" \ -; do - if [[ ! -f "${RPM_DIR}/${f}" ]]; then - echo "ERROR: expected file not found: ${RPM_DIR}/${f}" >&2 - exit 1 - fi -done + "subnet-evm-${TAG}-${PACKAGE_ARCH}.rpm" echo "=== Validating RPMs in fresh Rocky Linux 9 container ===" docker run --rm \ From 7be532d90f8ab722f422bb3b8a4b4f04d2d81f28 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 6 Apr 2026 20:02:17 -0700 Subject: [PATCH 10/43] Create unified build-package.sh replacing build-rpm.sh Single script handles both RPM and DEB via FORMAT env var. DEB-specific hooks (gpg-agent, dpkg-sig) are conditionally sourced from lib-build-deb.sh when present. FORMAT defaults to RPM so the base branch works standalone. --- .github/packaging/Taskfile.yml | 6 +- .github/packaging/scripts/build-package.sh | 108 +++++++++++++++++++++ .github/packaging/scripts/build-rpm.sh | 71 -------------- 3 files changed, 112 insertions(+), 73 deletions(-) create mode 100644 .github/packaging/scripts/build-package.sh delete mode 100755 .github/packaging/scripts/build-rpm.sh diff --git a/.github/packaging/Taskfile.yml b/.github/packaging/Taskfile.yml index 60fd39182795..f99117bc1de6 100644 --- a/.github/packaging/Taskfile.yml +++ b/.github/packaging/Taskfile.yml @@ -67,6 +67,7 @@ tasks: -v {{.REPO_ROOT}}:/build -v {{.PACKAGING_OUTPUT_DIR}}:/output {{if .RPM_GPG_KEY_FILE}}-v {{.RPM_GPG_KEY_FILE}}:{{.RPM_GPG_KEY_FILE}}:ro{{end}} + -e FORMAT=RPM -e PACKAGE=avalanchego -e VERSION={{trimPrefix "v" .RPM_TAG}} -e TAG={{.RPM_TAG}} @@ -76,7 +77,7 @@ tasks: {{if .RPM_GPG_KEY_FILE}}-e RPM_GPG_KEY_FILE={{.RPM_GPG_KEY_FILE}}{{end}} {{if .NFPM_RPM_PASSPHRASE}}-e NFPM_RPM_PASSPHRASE={{.NFPM_RPM_PASSPHRASE}}{{end}} {{.PACKAGING_DOCKER_IMAGE}} - .github/packaging/scripts/build-rpm.sh + .github/packaging/scripts/build-package.sh build-subnet-evm-rpm: desc: Builds RPM for subnet-evm @@ -91,6 +92,7 @@ tasks: -v {{.REPO_ROOT}}:/build -v {{.PACKAGING_OUTPUT_DIR}}:/output {{if .RPM_GPG_KEY_FILE}}-v {{.RPM_GPG_KEY_FILE}}:{{.RPM_GPG_KEY_FILE}}:ro{{end}} + -e FORMAT=RPM -e PACKAGE=subnet-evm -e VERSION={{trimPrefix "v" .RPM_TAG}} -e TAG={{.RPM_TAG}} @@ -100,7 +102,7 @@ tasks: {{if .RPM_GPG_KEY_FILE}}-e RPM_GPG_KEY_FILE={{.RPM_GPG_KEY_FILE}}{{end}} {{if .NFPM_RPM_PASSPHRASE}}-e NFPM_RPM_PASSPHRASE={{.NFPM_RPM_PASSPHRASE}}{{end}} {{.PACKAGING_DOCKER_IMAGE}} - .github/packaging/scripts/build-rpm.sh + .github/packaging/scripts/build-package.sh test-build-rpms: desc: Builds and validates RPMs end-to-end diff --git a/.github/packaging/scripts/build-package.sh b/.github/packaging/scripts/build-package.sh new file mode 100644 index 000000000000..125654e01045 --- /dev/null +++ b/.github/packaging/scripts/build-package.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash + +# Build and sign a package (RPM or DEB) inside the container. +# +# Required env vars: +# FORMAT - Package format: "RPM" or "DEB" (default: RPM) +# PACKAGE - "avalanchego" or "subnet-evm" +# VERSION - Semantic version without "v" prefix (e.g., "1.14.1") +# TAG - Git tag (e.g., "v1.14.1") +# PACKAGE_ARCH - Architecture (x86_64/aarch64 for RPM, amd64/arm64 for DEB) +# OUTPUT_DIR - Directory for the output package (bind-mounted from host) +# +# Optional env vars (format-prefixed by convention): +# RPM_GPG_KEY_FILE / DEB_GPG_KEY_FILE - Path to GPG private key +# NFPM_RPM_PASSPHRASE / NFPM_DEB_PASSPHRASE - GPG passphrase +# AVALANCHEGO_COMMIT - Git commit hash (auto-detected if not set) + +set -euo pipefail + +: "${PACKAGE:?PACKAGE must be set (avalanchego or subnet-evm)}" +: "${VERSION:?VERSION must be set}" +: "${TAG:?TAG must be set}" +: "${PACKAGE_ARCH:?PACKAGE_ARCH must be set}" +: "${OUTPUT_DIR:?OUTPUT_DIR must be set}" + +FORMAT="${FORMAT:-RPM}" +fmt_lower="${FORMAT,,}" + +REPO_ROOT="/build" +PACKAGING_DIR="${REPO_ROOT}/.github/packaging" + +# shellcheck disable=SC1091 +source "${PACKAGING_DIR}/scripts/lib-build-common.sh" + +# Source format-specific extensions if available (e.g., lib-build-deb.sh) +fmt_extensions="${PACKAGING_DIR}/scripts/lib-build-${fmt_lower}.sh" +if [[ -f "${fmt_extensions}" ]]; then + # shellcheck disable=SC1090 + source "${fmt_extensions}" +fi + +# Well-known paths referenced by nfpm configs +export NFPM_CHANGELOG="${REPO_ROOT}/build/nfpm-changelog.yml" +export NFPM_SIGNING_KEY="${REPO_ROOT}/build/gpg/signing-key.asc" + +echo "=== Building ${PACKAGE} ${FORMAT} for ${PACKAGE_ARCH} (tag: ${TAG}) ===" + +init_build_env +build_binary "${PACKAGE}" +generate_changelog "${VERSION}" + +# ── GPG signing ─────────────────────────────────────────────────── + +# Resolve format-prefixed GPG key file variable +case "${FORMAT}" in + RPM) GPG_KEY_FILE="${RPM_GPG_KEY_FILE:-}" ;; + DEB) GPG_KEY_FILE="${DEB_GPG_KEY_FILE:-}" ;; +esac + +GPG_PUBLIC_KEY="${OUTPUT_DIR}/${FORMAT}-GPG-KEY-avalanchego" + +# DEB needs gpg-agent configured before GPG setup +if [[ "${FORMAT}" == "DEB" ]] && type -t setup_deb_gpg_agent &>/dev/null; then + setup_deb_gpg_agent +fi + +setup_gpg "${GPG_KEY_FILE}" "${GPG_PUBLIC_KEY}" "${FORMAT}" + +# Format-specific post-GPG handling +case "${FORMAT}" in + RPM) + # Ephemeral keys have no passphrase; nfpm needs the variable set empty + if [[ -z "${GPG_KEY_FILE}" ]]; then + export NFPM_RPM_PASSPHRASE="" + fi + ;; + DEB) + # Cache passphrase in gpg-agent for dpkg-sig + if type -t cache_deb_gpg_passphrase &>/dev/null; then + cache_deb_gpg_passphrase "${GPG_KEY_FILE}" + fi + ;; +esac + +# ── Package with nfpm ───────────────────────────────────────────── + +case "${PACKAGE}" in + avalanchego) export AVALANCHEGO_BINARY="${BINARY_PATH}" ;; + subnet-evm) export SUBNET_EVM_BINARY="${BINARY_PATH}" ;; +esac + +export VERSION PACKAGE_ARCH + +PKG_FILENAME="${PACKAGE}-${TAG}-${PACKAGE_ARCH}.${fmt_lower}" +PKG_PATH="${OUTPUT_DIR}/${PKG_FILENAME}" + +run_nfpm_package \ + "${PACKAGING_DIR}/nfpm/${PACKAGE}-${fmt_lower}.yml" \ + "${REPO_ROOT}/build/${PACKAGE}-${fmt_lower}-resolved.yml" \ + "${fmt_lower}" \ + "${PKG_PATH}" + +# DEB post-build signing (dpkg-sig) +if [[ "${FORMAT}" == "DEB" ]] && type -t sign_deb_package &>/dev/null; then + sign_deb_package "${PKG_PATH}" "${PKG_FILENAME}" +fi + +echo "${FORMAT} built: ${PKG_PATH}" diff --git a/.github/packaging/scripts/build-rpm.sh b/.github/packaging/scripts/build-rpm.sh deleted file mode 100755 index 1db666857532..000000000000 --- a/.github/packaging/scripts/build-rpm.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash - -# Build and sign an RPM package inside the container. -# -# Required env vars: -# PACKAGE - "avalanchego" or "subnet-evm" -# VERSION - Semantic version without "v" prefix (e.g., "1.14.1") -# TAG - Git tag (e.g., "v1.14.1") -# PACKAGE_ARCH - RPM architecture name ("x86_64" or "aarch64") -# OUTPUT_DIR - Directory for the output RPM (bind-mounted from host) -# -# Optional env vars: -# RPM_GPG_KEY_FILE - Path to GPG private key for signing -# NFPM_RPM_PASSPHRASE - Passphrase for the GPG key -# AVALANCHEGO_COMMIT - Git commit hash (auto-detected if not set) - -set -euo pipefail - -: "${PACKAGE:?PACKAGE must be set (avalanchego or subnet-evm)}" -: "${VERSION:?VERSION must be set}" -: "${TAG:?TAG must be set}" -: "${PACKAGE_ARCH:?PACKAGE_ARCH must be set}" -: "${OUTPUT_DIR:?OUTPUT_DIR must be set}" - -REPO_ROOT="/build" -PACKAGING_DIR="${REPO_ROOT}/.github/packaging" - -# shellcheck disable=SC1091 -source "${PACKAGING_DIR}/scripts/lib-build-common.sh" - -# Well-known paths referenced by nfpm configs -export NFPM_CHANGELOG="${REPO_ROOT}/build/nfpm-changelog.yml" -export NFPM_SIGNING_KEY="${REPO_ROOT}/build/gpg/signing-key.asc" - -echo "=== Building ${PACKAGE} RPM for ${PACKAGE_ARCH} (tag: ${TAG}) ===" - -init_build_env -build_binary "${PACKAGE}" -generate_changelog "${VERSION}" - -# ── GPG signing ─────────────────────────────────────────────────── - -GPG_PUBLIC_KEY="${OUTPUT_DIR}/RPM-GPG-KEY-avalanchego" -setup_gpg "${RPM_GPG_KEY_FILE:-}" "${GPG_PUBLIC_KEY}" "RPM" - -# Ephemeral keys have no passphrase; nfpm needs the variable set to empty. -# The workflow always exports NFPM_RPM_PASSPHRASE (from secrets) but skips -# RPM_GPG_KEY_FILE on PR builds, so we must clear it unconditionally here. -if [[ -z "${RPM_GPG_KEY_FILE:-}" ]]; then - export NFPM_RPM_PASSPHRASE="" -fi - -# ── Package with nfpm ───────────────────────────────────────────── - -case "${PACKAGE}" in - avalanchego) export AVALANCHEGO_BINARY="${BINARY_PATH}" ;; - subnet-evm) export SUBNET_EVM_BINARY="${BINARY_PATH}" ;; -esac - -export VERSION PACKAGE_ARCH - -RPM_FILENAME="${PACKAGE}-${TAG}-${PACKAGE_ARCH}.rpm" -RPM_PATH="${OUTPUT_DIR}/${RPM_FILENAME}" - -run_nfpm_package \ - "${PACKAGING_DIR}/nfpm/${PACKAGE}-rpm.yml" \ - "${REPO_ROOT}/build/${PACKAGE}-rpm-resolved.yml" \ - rpm \ - "${RPM_PATH}" - -echo "RPM built: ${RPM_PATH}" From 6b113b76c7ba86a0809044608db0cc6fa9cc40e4 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 6 Apr 2026 20:02:58 -0700 Subject: [PATCH 11/43] Add setup-packaging composite action, use in RPM workflow Extract shared workflow steps (overlay, Go setup, tag resolution, GPG key import) into a reusable composite action. --- .github/actions/setup-packaging/action.yml | 62 +++++++++++++++++++ .github/packaging/scripts/lib-build-common.sh | 2 +- .github/workflows/build-rpm-release.yml | 53 +++------------- 3 files changed, 73 insertions(+), 44 deletions(-) create mode 100644 .github/actions/setup-packaging/action.yml diff --git a/.github/actions/setup-packaging/action.yml b/.github/actions/setup-packaging/action.yml new file mode 100644 index 000000000000..d49e0bbf8c86 --- /dev/null +++ b/.github/actions/setup-packaging/action.yml @@ -0,0 +1,62 @@ +name: 'Set up packaging environment' +description: 'Overlay packaging scripts, setup Go, resolve tag, import GPG key' + +inputs: + tag-input: + description: 'Explicit tag from workflow_dispatch (empty for auto-detect)' + required: false + default: '' + gpg-private-key: + description: 'GPG private key content (empty for unsigned PR builds)' + required: false + default: '' + +outputs: + tag: + description: 'Resolved package tag' + value: ${{ steps.get-tag.outputs.tag }} + gpg-key-file: + description: 'Path to temporary GPG key file (empty for unsigned builds)' + value: ${{ steps.import-gpg.outputs.gpg_key_file }} + +runs: + using: composite + steps: + - name: Overlay packaging scripts from workflow branch + if: inputs.tag-input != '' + uses: actions/checkout@v4 + with: + path: .packaging-overlay + sparse-checkout: .github/packaging + + - name: Apply packaging overlay + if: inputs.tag-input != '' + shell: bash + run: | + cp -r .packaging-overlay/.github/packaging .github/packaging + rm -rf .packaging-overlay + + - uses: ./.github/actions/setup-go-for-project + + - name: Get tag + id: get-tag + shell: bash + run: | + if [[ -n "${{ inputs.tag-input }}" ]]; then + TAG="${{ inputs.tag-input }}" + elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then + TAG="${GITHUB_REF/refs\/tags\//}" + else + TAG="v0.0.0-pr.${GITHUB_SHA::8}" + fi + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + + - name: Import GPG key + id: import-gpg + if: inputs.gpg-private-key != '' + shell: bash + run: | + GPG_KEY_FILE="$(mktemp)" + chmod 600 "${GPG_KEY_FILE}" + printf '%s' '${{ inputs.gpg-private-key }}' > "${GPG_KEY_FILE}" + echo "gpg_key_file=${GPG_KEY_FILE}" >> "$GITHUB_OUTPUT" diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh index d54249e9bb11..131429e80676 100644 --- a/.github/packaging/scripts/lib-build-common.sh +++ b/.github/packaging/scripts/lib-build-common.sh @@ -2,7 +2,7 @@ # Shared functions for RPM and DEB package build scripts. # -# Sourced (not executed) by build-rpm.sh and build-deb.sh. +# Sourced (not executed) by build-package.sh. # All functions expect REPO_ROOT to be set by the caller. # Initialize the build environment inside the container. diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index 39d57de389e0..c4efa3f71b5e 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -37,50 +37,17 @@ jobs: with: ref: ${{ github.event.inputs.tag || github.ref }} - # When building an older tag via workflow_dispatch, the tag's tree - # may not contain the packaging scripts. Overlay them from the - # workflow branch so that packaging:test-build-rpms is available. - - name: Overlay packaging scripts from workflow branch - if: github.event.inputs.tag - uses: actions/checkout@v5 + - uses: ./.github/actions/setup-packaging + id: setup with: - path: .packaging-overlay - sparse-checkout: .github/packaging - - name: Apply packaging overlay - if: github.event.inputs.tag - run: | - cp -r .packaging-overlay/.github/packaging .github/packaging - rm -rf .packaging-overlay - shell: bash - - - uses: ./.github/actions/setup-go-for-project - - - name: Get tag - run: | - if [[ -n "${{ github.event.inputs.tag }}" ]]; then - echo "TAG=${{ github.event.inputs.tag }}" >> "$GITHUB_ENV" - elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then - echo "TAG=${GITHUB_REF/refs\/tags\//}" >> "$GITHUB_ENV" - else - # PR builds: synthetic tag for smoke testing the build pipeline - echo "TAG=v0.0.0-pr.${GITHUB_SHA::8}" >> "$GITHUB_ENV" - fi - shell: bash - - - name: Import GPG key - if: github.event_name != 'pull_request' - run: | - GPG_KEY_FILE="$(mktemp)" - chmod 600 "${GPG_KEY_FILE}" - printf '%s' "${{ secrets.RPM_GPG_PRIVATE_KEY }}" > "${GPG_KEY_FILE}" - printf 'GPG_KEY_FILE=%s\n' "${GPG_KEY_FILE}" >> "$GITHUB_ENV" - shell: bash + tag-input: ${{ github.event.inputs.tag }} + gpg-private-key: ${{ github.event_name != 'pull_request' && secrets.RPM_GPG_PRIVATE_KEY || '' }} - name: Build and validate RPMs run: ./scripts/run_task.sh --taskfile .github/packaging/Taskfile.yml test-build-rpms env: - PACKAGING_TAG: ${{ env.TAG }} - RPM_GPG_KEY_FILE: ${{ env.GPG_KEY_FILE }} + PACKAGING_TAG: ${{ steps.setup.outputs.tag }} + RPM_GPG_KEY_FILE: ${{ steps.setup.outputs.gpg-key-file }} NFPM_RPM_PASSPHRASE: ${{ secrets.RPM_GPG_PASSPHRASE }} - name: Upload RPMs as artifacts @@ -89,15 +56,15 @@ jobs: with: name: rpms-${{ matrix.rpm_arch }} path: | - build/rpm/avalanchego-${{ env.TAG }}-${{ matrix.rpm_arch }}.rpm - build/rpm/subnet-evm-${{ env.TAG }}-${{ matrix.rpm_arch }}.rpm + build/rpm/avalanchego-${{ steps.setup.outputs.tag }}-${{ matrix.rpm_arch }}.rpm + build/rpm/subnet-evm-${{ steps.setup.outputs.tag }}-${{ matrix.rpm_arch }}.rpm build/rpm/RPM-GPG-KEY-avalanchego - name: Cleanup if: always() run: | - if [[ -n "${GPG_KEY_FILE:-}" ]]; then - rm -f "${GPG_KEY_FILE}" + if [[ -n "${{ steps.setup.outputs.gpg-key-file }}" ]]; then + rm -f "${{ steps.setup.outputs.gpg-key-file }}" fi rm -rf build/rpm sudo rm -rf build/gpg From f472dc10ec6a9e4748d8103e6224b79cbb05c1d6 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 6 Apr 2026 20:26:37 -0700 Subject: [PATCH 12/43] Make build-package.sh and lib-validate-common.sh executable --- .github/packaging/scripts/build-package.sh | 0 .github/packaging/scripts/lib-validate-common.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .github/packaging/scripts/build-package.sh mode change 100644 => 100755 .github/packaging/scripts/lib-validate-common.sh diff --git a/.github/packaging/scripts/build-package.sh b/.github/packaging/scripts/build-package.sh old mode 100644 new mode 100755 diff --git a/.github/packaging/scripts/lib-validate-common.sh b/.github/packaging/scripts/lib-validate-common.sh old mode 100644 new mode 100755 From ea914944d9facbd0ac77108db850b31520411208 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:20:22 -0700 Subject: [PATCH 13/43] Replace composite action with shared script for old-tag compat Local composite actions resolve from the checked-out workspace, so workflow_dispatch builds for tags that predate this action would fail. Move tag resolution and GPG import to a shared script under .github/packaging/ that gets overlaid from the workflow branch. --- .github/actions/setup-packaging/action.yml | 62 -------------------- .github/packaging/scripts/setup-packaging.sh | 46 +++++++++++++++ .github/workflows/build-rpm-release.yml | 29 +++++++-- 3 files changed, 71 insertions(+), 66 deletions(-) delete mode 100644 .github/actions/setup-packaging/action.yml create mode 100755 .github/packaging/scripts/setup-packaging.sh diff --git a/.github/actions/setup-packaging/action.yml b/.github/actions/setup-packaging/action.yml deleted file mode 100644 index d49e0bbf8c86..000000000000 --- a/.github/actions/setup-packaging/action.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: 'Set up packaging environment' -description: 'Overlay packaging scripts, setup Go, resolve tag, import GPG key' - -inputs: - tag-input: - description: 'Explicit tag from workflow_dispatch (empty for auto-detect)' - required: false - default: '' - gpg-private-key: - description: 'GPG private key content (empty for unsigned PR builds)' - required: false - default: '' - -outputs: - tag: - description: 'Resolved package tag' - value: ${{ steps.get-tag.outputs.tag }} - gpg-key-file: - description: 'Path to temporary GPG key file (empty for unsigned builds)' - value: ${{ steps.import-gpg.outputs.gpg_key_file }} - -runs: - using: composite - steps: - - name: Overlay packaging scripts from workflow branch - if: inputs.tag-input != '' - uses: actions/checkout@v4 - with: - path: .packaging-overlay - sparse-checkout: .github/packaging - - - name: Apply packaging overlay - if: inputs.tag-input != '' - shell: bash - run: | - cp -r .packaging-overlay/.github/packaging .github/packaging - rm -rf .packaging-overlay - - - uses: ./.github/actions/setup-go-for-project - - - name: Get tag - id: get-tag - shell: bash - run: | - if [[ -n "${{ inputs.tag-input }}" ]]; then - TAG="${{ inputs.tag-input }}" - elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then - TAG="${GITHUB_REF/refs\/tags\//}" - else - TAG="v0.0.0-pr.${GITHUB_SHA::8}" - fi - echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - - - name: Import GPG key - id: import-gpg - if: inputs.gpg-private-key != '' - shell: bash - run: | - GPG_KEY_FILE="$(mktemp)" - chmod 600 "${GPG_KEY_FILE}" - printf '%s' '${{ inputs.gpg-private-key }}' > "${GPG_KEY_FILE}" - echo "gpg_key_file=${GPG_KEY_FILE}" >> "$GITHUB_OUTPUT" diff --git a/.github/packaging/scripts/setup-packaging.sh b/.github/packaging/scripts/setup-packaging.sh new file mode 100755 index 000000000000..75688e896547 --- /dev/null +++ b/.github/packaging/scripts/setup-packaging.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# Set up packaging workflow environment: resolve tag and import GPG key. +# +# Called from workflow YAML after the packaging overlay has been applied. +# Writes outputs to $GITHUB_OUTPUT for use by subsequent workflow steps. +# +# Required env vars (provided by GitHub Actions runner): +# GITHUB_OUTPUT - Path to step output file +# GITHUB_REF - Git ref that triggered the workflow +# GITHUB_SHA - Git commit SHA +# +# Optional env vars: +# TAG_INPUT - Explicit tag from workflow_dispatch (empty for auto-detect) +# GPG_PRIVATE_KEY - GPG private key content (empty for unsigned PR builds) + +set -euo pipefail + +# ── Resolve tag ────────────────────────────────────────────────── + +TAG_INPUT="${TAG_INPUT:-}" + +if [[ -n "${TAG_INPUT}" ]]; then + TAG="${TAG_INPUT}" +elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then + TAG="${GITHUB_REF/refs\/tags\//}" +else + TAG="v0.0.0-pr.${GITHUB_SHA::8}" +fi + +echo "tag=${TAG}" >> "${GITHUB_OUTPUT}" +echo "Resolved tag: ${TAG}" + +# ── Import GPG key ─────────────────────────────────────────────── + +GPG_PRIVATE_KEY="${GPG_PRIVATE_KEY:-}" + +if [[ -n "${GPG_PRIVATE_KEY}" ]]; then + GPG_KEY_FILE="$(mktemp)" + chmod 600 "${GPG_KEY_FILE}" + printf '%s' "${GPG_PRIVATE_KEY}" > "${GPG_KEY_FILE}" + echo "gpg-key-file=${GPG_KEY_FILE}" >> "${GITHUB_OUTPUT}" + echo "GPG key written to temporary file" +else + echo "gpg-key-file=" >> "${GITHUB_OUTPUT}" +fi diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index c4efa3f71b5e..3226d1498823 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -37,11 +37,32 @@ jobs: with: ref: ${{ github.event.inputs.tag || github.ref }} - - uses: ./.github/actions/setup-packaging - id: setup + # When building an older tag via workflow_dispatch, the tag's tree + # may not contain the packaging scripts. Overlay them from the + # workflow branch so that packaging:test-build-rpms is available. + - name: Overlay packaging scripts from workflow branch + if: github.event.inputs.tag + uses: actions/checkout@v4 with: - tag-input: ${{ github.event.inputs.tag }} - gpg-private-key: ${{ github.event_name != 'pull_request' && secrets.RPM_GPG_PRIVATE_KEY || '' }} + path: .packaging-overlay + sparse-checkout: .github/packaging + + - name: Apply packaging overlay + if: github.event.inputs.tag + run: | + cp -r .packaging-overlay/.github/packaging .github/packaging + rm -rf .packaging-overlay + shell: bash + + - uses: ./.github/actions/setup-go-for-project + + - name: Set up packaging environment + id: setup + run: .github/packaging/scripts/setup-packaging.sh + shell: bash + env: + TAG_INPUT: ${{ github.event.inputs.tag }} + GPG_PRIVATE_KEY: ${{ github.event_name != 'pull_request' && secrets.RPM_GPG_PRIVATE_KEY || '' }} - name: Build and validate RPMs run: ./scripts/run_task.sh --taskfile .github/packaging/Taskfile.yml test-build-rpms From 639eec84ae950e6da809338915e609f323f5674c Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Sat, 11 Apr 2026 21:48:28 -0700 Subject: [PATCH 14/43] Route setup-packaging through Taskfile for lint compliance The actionlint scripts/ path rule requires all script calls in workflows to go through scripts/run_task.sh. Add a setup-packaging task to the packaging Taskfile and route the workflow through it. The script now writes to ${GITHUB_OUTPUT:-/dev/stdout} so it works both in CI (step outputs) and locally (prints to terminal). --- .github/packaging/Taskfile.yml | 5 ++++ .github/packaging/scripts/setup-packaging.sh | 27 ++++++++++++-------- .github/workflows/build-rpm-release.yml | 2 +- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/packaging/Taskfile.yml b/.github/packaging/Taskfile.yml index f99117bc1de6..7533c31131e8 100644 --- a/.github/packaging/Taskfile.yml +++ b/.github/packaging/Taskfile.yml @@ -37,6 +37,11 @@ tasks: silent: true cmd: task --list | grep packaging + setup-packaging: + desc: Resolves packaging tag and imports GPG key for workflow use + cmds: + - cmd: '{{.REPO_ROOT}}/.github/packaging/scripts/setup-packaging.sh' + build-rpms: desc: Builds RPMs for both avalanchego and subnet-evm cmds: diff --git a/.github/packaging/scripts/setup-packaging.sh b/.github/packaging/scripts/setup-packaging.sh index 75688e896547..c36f3cd98589 100755 --- a/.github/packaging/scripts/setup-packaging.sh +++ b/.github/packaging/scripts/setup-packaging.sh @@ -2,20 +2,25 @@ # Set up packaging workflow environment: resolve tag and import GPG key. # -# Called from workflow YAML after the packaging overlay has been applied. -# Writes outputs to $GITHUB_OUTPUT for use by subsequent workflow steps. +# Called via Taskfile (task packaging:setup-packaging) from workflow YAML +# after the packaging overlay has been applied. # -# Required env vars (provided by GitHub Actions runner): -# GITHUB_OUTPUT - Path to step output file -# GITHUB_REF - Git ref that triggered the workflow -# GITHUB_SHA - Git commit SHA +# Writes key=value outputs to $GITHUB_OUTPUT when running in CI, or to +# stdout when running locally (for inspection / testing). +# +# Required env vars: +# GITHUB_REF - Git ref (set by GitHub Actions; locally: any ref string) +# GITHUB_SHA - Git commit SHA (set by GitHub Actions; locally: any SHA) # # Optional env vars: +# GITHUB_OUTPUT - Path to step output file (CI only; omit for stdout) # TAG_INPUT - Explicit tag from workflow_dispatch (empty for auto-detect) # GPG_PRIVATE_KEY - GPG private key content (empty for unsigned PR builds) set -euo pipefail +OUTPUT="${GITHUB_OUTPUT:-/dev/stdout}" + # ── Resolve tag ────────────────────────────────────────────────── TAG_INPUT="${TAG_INPUT:-}" @@ -28,8 +33,8 @@ else TAG="v0.0.0-pr.${GITHUB_SHA::8}" fi -echo "tag=${TAG}" >> "${GITHUB_OUTPUT}" -echo "Resolved tag: ${TAG}" +echo "tag=${TAG}" >> "${OUTPUT}" +echo "Resolved tag: ${TAG}" >&2 # ── Import GPG key ─────────────────────────────────────────────── @@ -39,8 +44,8 @@ if [[ -n "${GPG_PRIVATE_KEY}" ]]; then GPG_KEY_FILE="$(mktemp)" chmod 600 "${GPG_KEY_FILE}" printf '%s' "${GPG_PRIVATE_KEY}" > "${GPG_KEY_FILE}" - echo "gpg-key-file=${GPG_KEY_FILE}" >> "${GITHUB_OUTPUT}" - echo "GPG key written to temporary file" + echo "gpg-key-file=${GPG_KEY_FILE}" >> "${OUTPUT}" + echo "GPG key written to temporary file" >&2 else - echo "gpg-key-file=" >> "${GITHUB_OUTPUT}" + echo "gpg-key-file=" >> "${OUTPUT}" fi diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index 3226d1498823..3065ba0e8f4f 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -58,7 +58,7 @@ jobs: - name: Set up packaging environment id: setup - run: .github/packaging/scripts/setup-packaging.sh + run: ./scripts/run_task.sh --taskfile .github/packaging/Taskfile.yml setup-packaging shell: bash env: TAG_INPUT: ${{ github.event.inputs.tag }} From 8e4cd35ff7d2f7da6a9f1086b42d14180eed0800 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:55:19 -0700 Subject: [PATCH 15/43] Replace setup-packaging Taskfile target with workflow-*.sh escape hatch setup-packaging.sh is CI orchestration glue (tag resolution, secret materialization), not a developer entrypoint. Wrapping it in a Taskfile target just to satisfy the actionlint policy adds unnecessary indirection. Introduce a workflow-*.sh naming convention for CI-only helper scripts and exempt that pattern from the scripts/run_task.sh lint requirement. This keeps the general policy intact for real developer entrypoints. --- .github/packaging/Taskfile.yml | 5 ----- .../{setup-packaging.sh => workflow-setup-packaging.sh} | 4 ++-- .github/workflows/build-rpm-release.yml | 2 +- scripts/actionlint.sh | 7 +++++-- 4 files changed, 8 insertions(+), 10 deletions(-) rename .github/packaging/scripts/{setup-packaging.sh => workflow-setup-packaging.sh} (92%) diff --git a/.github/packaging/Taskfile.yml b/.github/packaging/Taskfile.yml index 7533c31131e8..f99117bc1de6 100644 --- a/.github/packaging/Taskfile.yml +++ b/.github/packaging/Taskfile.yml @@ -37,11 +37,6 @@ tasks: silent: true cmd: task --list | grep packaging - setup-packaging: - desc: Resolves packaging tag and imports GPG key for workflow use - cmds: - - cmd: '{{.REPO_ROOT}}/.github/packaging/scripts/setup-packaging.sh' - build-rpms: desc: Builds RPMs for both avalanchego and subnet-evm cmds: diff --git a/.github/packaging/scripts/setup-packaging.sh b/.github/packaging/scripts/workflow-setup-packaging.sh similarity index 92% rename from .github/packaging/scripts/setup-packaging.sh rename to .github/packaging/scripts/workflow-setup-packaging.sh index c36f3cd98589..1b544273af8d 100755 --- a/.github/packaging/scripts/setup-packaging.sh +++ b/.github/packaging/scripts/workflow-setup-packaging.sh @@ -2,8 +2,8 @@ # Set up packaging workflow environment: resolve tag and import GPG key. # -# Called via Taskfile (task packaging:setup-packaging) from workflow YAML -# after the packaging overlay has been applied. +# Called directly from workflow YAML (workflow-*.sh scripts are CI glue, +# not developer entrypoints, and are exempt from the run_task.sh policy). # # Writes key=value outputs to $GITHUB_OUTPUT when running in CI, or to # stdout when running locally (for inspection / testing). diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index 3065ba0e8f4f..fb84f9705dd2 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -58,7 +58,7 @@ jobs: - name: Set up packaging environment id: setup - run: ./scripts/run_task.sh --taskfile .github/packaging/Taskfile.yml setup-packaging + run: ./.github/packaging/scripts/workflow-setup-packaging.sh shell: bash env: TAG_INPUT: ${{ github.event.inputs.tag }} diff --git a/scripts/actionlint.sh b/scripts/actionlint.sh index 3d166f7cf757..b6d41bfa6f57 100755 --- a/scripts/actionlint.sh +++ b/scripts/actionlint.sh @@ -11,8 +11,10 @@ for file in "${AVALANCHE_PATH}"/.github/workflows/*.{yml,yaml}; do # Skip if no matches found (in case one of the extensions doesn't exist) [[ -f "$file" ]] || continue - # Search for scripts/* except for scripts/run_task.sh - MATCHES=$(grep -H -n -P "scripts/(?!run_task\.sh)" "$file" || true) + # Search for scripts/* except for: + # - scripts/run_task.sh (the approved launcher for developer entrypoints) + # - workflow-*.sh (CI-only glue scripts, not developer entrypoints) + MATCHES=$(grep -H -n -P "scripts/(?!run_task\.sh|workflow-)" "$file" || true) if [[ -n "${MATCHES}" ]]; then echo "${MATCHES}" SCRIPT_USAGE=1 @@ -21,6 +23,7 @@ done if [[ -n "${SCRIPT_USAGE}" ]]; then echo "Error: the lines listed above must be converted to use scripts/run_task.sh to ensure local reproducibility." + echo " CI-only helpers may use the workflow-*.sh naming convention to bypass this check." exit 1 fi From 06c583352837d4d640b62117e27e122f436c36a3 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 5 May 2026 12:11:46 -0700 Subject: [PATCH 16/43] Drop DEB-specific code paths to keep this PR strictly RPM Reviewer flagged that adding DEB-aware logic in advance of any DEB caller breaks the coherence of a refactor PR. Move the DEB material to #5180 (DEB packaging) and leave only the generic refactor here. Removed from build-package.sh: lib-build-deb.sh extension source hook, DEB branch of the GPG_KEY_FILE case, setup_deb_gpg_agent invocation, DEB passphrase caching for dpkg-sig, and the post-build dpkg-sig signing block. Removed from lib-validate-common.sh: DEB arm of detect_host_arch's format dispatch. FORMAT, PACKAGE_ARCH, and fmt_lower remain as generic scaffolding so docstrings tightened to match the now-RPM-only contract; the abstract helpers in lib-build-common.sh keep their format-agnostic docstrings. Verified locally: task -t .github/packaging/Taskfile.yml test-build-rpms passes end-to-end (RPMs built, GPG signatures verified, install + smoke test green in a fresh rockylinux:9 container). --- .github/packaging/scripts/build-package.sh | 57 ++++--------------- .../packaging/scripts/lib-validate-common.sh | 11 +--- 2 files changed, 14 insertions(+), 54 deletions(-) diff --git a/.github/packaging/scripts/build-package.sh b/.github/packaging/scripts/build-package.sh index 125654e01045..e372bfca9837 100755 --- a/.github/packaging/scripts/build-package.sh +++ b/.github/packaging/scripts/build-package.sh @@ -1,19 +1,19 @@ #!/usr/bin/env bash -# Build and sign a package (RPM or DEB) inside the container. +# Build and sign an RPM package inside the container. # # Required env vars: -# FORMAT - Package format: "RPM" or "DEB" (default: RPM) # PACKAGE - "avalanchego" or "subnet-evm" # VERSION - Semantic version without "v" prefix (e.g., "1.14.1") # TAG - Git tag (e.g., "v1.14.1") -# PACKAGE_ARCH - Architecture (x86_64/aarch64 for RPM, amd64/arm64 for DEB) +# PACKAGE_ARCH - Architecture (x86_64 or aarch64) # OUTPUT_DIR - Directory for the output package (bind-mounted from host) # -# Optional env vars (format-prefixed by convention): -# RPM_GPG_KEY_FILE / DEB_GPG_KEY_FILE - Path to GPG private key -# NFPM_RPM_PASSPHRASE / NFPM_DEB_PASSPHRASE - GPG passphrase -# AVALANCHEGO_COMMIT - Git commit hash (auto-detected if not set) +# Optional env vars: +# FORMAT - Package format identifier (default: RPM) +# RPM_GPG_KEY_FILE - Path to GPG private key +# NFPM_RPM_PASSPHRASE - GPG passphrase +# AVALANCHEGO_COMMIT - Git commit hash (auto-detected if not set) set -euo pipefail @@ -32,13 +32,6 @@ PACKAGING_DIR="${REPO_ROOT}/.github/packaging" # shellcheck disable=SC1091 source "${PACKAGING_DIR}/scripts/lib-build-common.sh" -# Source format-specific extensions if available (e.g., lib-build-deb.sh) -fmt_extensions="${PACKAGING_DIR}/scripts/lib-build-${fmt_lower}.sh" -if [[ -f "${fmt_extensions}" ]]; then - # shellcheck disable=SC1090 - source "${fmt_extensions}" -fi - # Well-known paths referenced by nfpm configs export NFPM_CHANGELOG="${REPO_ROOT}/build/nfpm-changelog.yml" export NFPM_SIGNING_KEY="${REPO_ROOT}/build/gpg/signing-key.asc" @@ -51,36 +44,15 @@ generate_changelog "${VERSION}" # ── GPG signing ─────────────────────────────────────────────────── -# Resolve format-prefixed GPG key file variable -case "${FORMAT}" in - RPM) GPG_KEY_FILE="${RPM_GPG_KEY_FILE:-}" ;; - DEB) GPG_KEY_FILE="${DEB_GPG_KEY_FILE:-}" ;; -esac - +GPG_KEY_FILE="${RPM_GPG_KEY_FILE:-}" GPG_PUBLIC_KEY="${OUTPUT_DIR}/${FORMAT}-GPG-KEY-avalanchego" -# DEB needs gpg-agent configured before GPG setup -if [[ "${FORMAT}" == "DEB" ]] && type -t setup_deb_gpg_agent &>/dev/null; then - setup_deb_gpg_agent -fi - setup_gpg "${GPG_KEY_FILE}" "${GPG_PUBLIC_KEY}" "${FORMAT}" -# Format-specific post-GPG handling -case "${FORMAT}" in - RPM) - # Ephemeral keys have no passphrase; nfpm needs the variable set empty - if [[ -z "${GPG_KEY_FILE}" ]]; then - export NFPM_RPM_PASSPHRASE="" - fi - ;; - DEB) - # Cache passphrase in gpg-agent for dpkg-sig - if type -t cache_deb_gpg_passphrase &>/dev/null; then - cache_deb_gpg_passphrase "${GPG_KEY_FILE}" - fi - ;; -esac +# Ephemeral keys have no passphrase; nfpm needs the variable set empty +if [[ -z "${GPG_KEY_FILE}" ]]; then + export NFPM_RPM_PASSPHRASE="" +fi # ── Package with nfpm ───────────────────────────────────────────── @@ -100,9 +72,4 @@ run_nfpm_package \ "${fmt_lower}" \ "${PKG_PATH}" -# DEB post-build signing (dpkg-sig) -if [[ "${FORMAT}" == "DEB" ]] && type -t sign_deb_package &>/dev/null; then - sign_deb_package "${PKG_PATH}" "${PKG_FILENAME}" -fi - echo "${FORMAT} built: ${PKG_PATH}" diff --git a/.github/packaging/scripts/lib-validate-common.sh b/.github/packaging/scripts/lib-validate-common.sh index eb1cac28fbea..5006d2b39399 100755 --- a/.github/packaging/scripts/lib-validate-common.sh +++ b/.github/packaging/scripts/lib-validate-common.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash # Shared functions for package validation scripts. -# Sourced by validate-rpm.sh and validate-deb.sh. +# Sourced by validate-rpm.sh. # Detect host architecture for the given package format. # Sets PACKAGE_ARCH (global) if not already set by the caller. # -# Args: format ("RPM" or "DEB") +# Args: format ("RPM") detect_host_arch() { local format="${1:?format required}" if [[ -n "${PACKAGE_ARCH:-}" ]]; then @@ -21,13 +21,6 @@ detect_host_arch() { *) PACKAGE_ARCH="${arch}" ;; esac ;; - DEB) - case "${arch}" in - x86_64) PACKAGE_ARCH="amd64" ;; - aarch64|arm64) PACKAGE_ARCH="arm64" ;; - *) PACKAGE_ARCH="${arch}" ;; - esac - ;; esac } From 0e1d9087ec79698526af2e107fad22b53954a293 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Wed, 6 May 2026 00:06:11 -0700 Subject: [PATCH 17/43] Address library-polish review feedback - build-rpm-release.yml: clarify in the comment block that the two- step overlay is split because actions/checkout and the copy cannot be combined, so the comment now visibly applies to both steps. - build-package.sh + Taskfile.yml: rename FORMAT/fmt_lower to PKG_FORMAT/pkg_format_lower. Bare FORMAT collides with nfpm's own field name, and fmt_lower read like a function rather than a derived value. - lib-build-common.sh: annotate the SC2154 disable next to the git_commit echo so a reader sees it comes from the sourced git_commit.sh. - lib-build-common.sh: drop the SUBNET_EVM_BINARY intermediate; pass BINARY_PATH directly to subnet-evm's build script. - lib-build-common.sh: replace exit with return in build_binary's unknown-package case and resolve_subnet_evm_vm_id's empty-result case. Callers' set -e propagates the non-zero return. - lib-build-common.sh: hoist "Ava Labs" / "security@avalabs.org" to PACKAGER_NAME / PACKAGER_EMAIL constants instead of repeating the literals across the changelog template, ephemeral GPG key generation, and key export. Verified locally: task -t .github/packaging/Taskfile.yml test-build-rpms passes end-to-end (RPMs built, GPG signatures verified, install + smoke test green in a fresh rockylinux:9 container). --- .github/packaging/Taskfile.yml | 4 ++-- .github/packaging/scripts/build-package.sh | 22 +++++++++---------- .github/packaging/scripts/lib-build-common.sh | 22 ++++++++++--------- .github/workflows/build-rpm-release.yml | 2 ++ 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/.github/packaging/Taskfile.yml b/.github/packaging/Taskfile.yml index f99117bc1de6..d5f5c3238427 100644 --- a/.github/packaging/Taskfile.yml +++ b/.github/packaging/Taskfile.yml @@ -67,7 +67,7 @@ tasks: -v {{.REPO_ROOT}}:/build -v {{.PACKAGING_OUTPUT_DIR}}:/output {{if .RPM_GPG_KEY_FILE}}-v {{.RPM_GPG_KEY_FILE}}:{{.RPM_GPG_KEY_FILE}}:ro{{end}} - -e FORMAT=RPM + -e PKG_FORMAT=RPM -e PACKAGE=avalanchego -e VERSION={{trimPrefix "v" .RPM_TAG}} -e TAG={{.RPM_TAG}} @@ -92,7 +92,7 @@ tasks: -v {{.REPO_ROOT}}:/build -v {{.PACKAGING_OUTPUT_DIR}}:/output {{if .RPM_GPG_KEY_FILE}}-v {{.RPM_GPG_KEY_FILE}}:{{.RPM_GPG_KEY_FILE}}:ro{{end}} - -e FORMAT=RPM + -e PKG_FORMAT=RPM -e PACKAGE=subnet-evm -e VERSION={{trimPrefix "v" .RPM_TAG}} -e TAG={{.RPM_TAG}} diff --git a/.github/packaging/scripts/build-package.sh b/.github/packaging/scripts/build-package.sh index e372bfca9837..63a144d9f2ad 100755 --- a/.github/packaging/scripts/build-package.sh +++ b/.github/packaging/scripts/build-package.sh @@ -10,7 +10,7 @@ # OUTPUT_DIR - Directory for the output package (bind-mounted from host) # # Optional env vars: -# FORMAT - Package format identifier (default: RPM) +# PKG_FORMAT - Package format identifier (default: RPM) # RPM_GPG_KEY_FILE - Path to GPG private key # NFPM_RPM_PASSPHRASE - GPG passphrase # AVALANCHEGO_COMMIT - Git commit hash (auto-detected if not set) @@ -23,8 +23,8 @@ set -euo pipefail : "${PACKAGE_ARCH:?PACKAGE_ARCH must be set}" : "${OUTPUT_DIR:?OUTPUT_DIR must be set}" -FORMAT="${FORMAT:-RPM}" -fmt_lower="${FORMAT,,}" +PKG_FORMAT="${PKG_FORMAT:-RPM}" +pkg_format_lower="${PKG_FORMAT,,}" REPO_ROOT="/build" PACKAGING_DIR="${REPO_ROOT}/.github/packaging" @@ -36,7 +36,7 @@ source "${PACKAGING_DIR}/scripts/lib-build-common.sh" export NFPM_CHANGELOG="${REPO_ROOT}/build/nfpm-changelog.yml" export NFPM_SIGNING_KEY="${REPO_ROOT}/build/gpg/signing-key.asc" -echo "=== Building ${PACKAGE} ${FORMAT} for ${PACKAGE_ARCH} (tag: ${TAG}) ===" +echo "=== Building ${PACKAGE} ${PKG_FORMAT} for ${PACKAGE_ARCH} (tag: ${TAG}) ===" init_build_env build_binary "${PACKAGE}" @@ -45,9 +45,9 @@ generate_changelog "${VERSION}" # ── GPG signing ─────────────────────────────────────────────────── GPG_KEY_FILE="${RPM_GPG_KEY_FILE:-}" -GPG_PUBLIC_KEY="${OUTPUT_DIR}/${FORMAT}-GPG-KEY-avalanchego" +GPG_PUBLIC_KEY="${OUTPUT_DIR}/${PKG_FORMAT}-GPG-KEY-avalanchego" -setup_gpg "${GPG_KEY_FILE}" "${GPG_PUBLIC_KEY}" "${FORMAT}" +setup_gpg "${GPG_KEY_FILE}" "${GPG_PUBLIC_KEY}" "${PKG_FORMAT}" # Ephemeral keys have no passphrase; nfpm needs the variable set empty if [[ -z "${GPG_KEY_FILE}" ]]; then @@ -63,13 +63,13 @@ esac export VERSION PACKAGE_ARCH -PKG_FILENAME="${PACKAGE}-${TAG}-${PACKAGE_ARCH}.${fmt_lower}" +PKG_FILENAME="${PACKAGE}-${TAG}-${PACKAGE_ARCH}.${pkg_format_lower}" PKG_PATH="${OUTPUT_DIR}/${PKG_FILENAME}" run_nfpm_package \ - "${PACKAGING_DIR}/nfpm/${PACKAGE}-${fmt_lower}.yml" \ - "${REPO_ROOT}/build/${PACKAGE}-${fmt_lower}-resolved.yml" \ - "${fmt_lower}" \ + "${PACKAGING_DIR}/nfpm/${PACKAGE}-${pkg_format_lower}.yml" \ + "${REPO_ROOT}/build/${PACKAGE}-${pkg_format_lower}-resolved.yml" \ + "${pkg_format_lower}" \ "${PKG_PATH}" -echo "${FORMAT} built: ${PKG_PATH}" +echo "${PKG_FORMAT} built: ${PKG_PATH}" diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh index 131429e80676..4125e172edaa 100644 --- a/.github/packaging/scripts/lib-build-common.sh +++ b/.github/packaging/scripts/lib-build-common.sh @@ -5,6 +5,9 @@ # Sourced (not executed) by build-package.sh. # All functions expect REPO_ROOT to be set by the caller. +readonly PACKAGER_NAME="Ava Labs" +readonly PACKAGER_EMAIL="security@avalabs.org" + # Initialize the build environment inside the container. # Marks the bind-mounted source tree as git-safe, sources project # scripts (constants.sh, git_commit.sh), and disables Go VCS stamping. @@ -18,7 +21,7 @@ init_build_env() { # shellcheck disable=SC1091 source "${REPO_ROOT}/scripts/git_commit.sh" - # shellcheck disable=SC2154 + # shellcheck disable=SC2154 # git_commit is set by git_commit.sh sourced above echo "Git commit: ${git_commit}" # Disable Go's automatic VCS stamping — the commit hash is passed @@ -45,13 +48,12 @@ build_binary() { resolve_subnet_evm_vm_id echo "Subnet-EVM VM ID: ${SUBNET_EVM_VM_ID}" - SUBNET_EVM_BINARY="${REPO_ROOT}/build/subnet-evm" - (cd "${REPO_ROOT}/graft/subnet-evm" && ./scripts/build.sh "${SUBNET_EVM_BINARY}") - BINARY_PATH="${SUBNET_EVM_BINARY}" + BINARY_PATH="${REPO_ROOT}/build/subnet-evm" + (cd "${REPO_ROOT}/graft/subnet-evm" && ./scripts/build.sh "${BINARY_PATH}") ;; *) echo "Unknown package: ${package}" >&2 - exit 1 + return 1 ;; esac @@ -67,7 +69,7 @@ resolve_subnet_evm_vm_id() { ) if [[ -z "${SUBNET_EVM_VM_ID}" ]]; then echo "ERROR: could not resolve SUBNET_EVM_VM_ID" >&2 - exit 1 + return 1 fi export SUBNET_EVM_VM_ID } @@ -83,7 +85,7 @@ generate_changelog() { --- - semver: ${version} date: $(date -u +%Y-%m-%dT%H:%M:%SZ) - packager: Ava Labs + packager: ${PACKAGER_NAME} <${PACKAGER_EMAIL}> changes: - note: "See https://github.com/ava-labs/avalanchego/releases/tag/v${version}" EOF @@ -118,14 +120,14 @@ Key-Length: 4096 Subkey-Type: RSA Subkey-Length: 4096 Name-Real: AvalancheGo ${key_label} Signing (ephemeral) -Name-Email: security@avalabs.org +Name-Email: ${PACKAGER_EMAIL} Expire-Date: 1d %commit GPGEOF - gpg --batch --armor --export-secret-keys "security@avalabs.org" > "${NFPM_SIGNING_KEY}" + gpg --batch --armor --export-secret-keys "${PACKAGER_EMAIL}" > "${NFPM_SIGNING_KEY}" fi - gpg --batch --armor --export "security@avalabs.org" > "${public_key_out}" + gpg --batch --armor --export "${PACKAGER_EMAIL}" > "${public_key_out}" echo "GPG public key exported to: ${public_key_out}" } diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index fb84f9705dd2..87dba994f024 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -40,6 +40,8 @@ jobs: # When building an older tag via workflow_dispatch, the tag's tree # may not contain the packaging scripts. Overlay them from the # workflow branch so that packaging:test-build-rpms is available. + # Split into two steps because actions/checkout and the copy + # cannot be combined. - name: Overlay packaging scripts from workflow branch if: github.event.inputs.tag uses: actions/checkout@v4 From 234e92cf45959aa763ecf74fe543162f50f51707 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 11 May 2026 11:41:59 -0700 Subject: [PATCH 18/43] Use BINARY_PATH directly in nfpm templates Drops the per-package ${*_BINARY} envs mapping in build-package.sh; both templates reference ${BINARY_PATH} which the library already sets. --- .github/packaging/nfpm/avalanchego-rpm.yml | 2 +- .github/packaging/nfpm/subnet-evm-rpm.yml | 2 +- .github/packaging/scripts/build-package.sh | 7 +------ 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/packaging/nfpm/avalanchego-rpm.yml b/.github/packaging/nfpm/avalanchego-rpm.yml index dcf6167290bd..2675905ba9a0 100644 --- a/.github/packaging/nfpm/avalanchego-rpm.yml +++ b/.github/packaging/nfpm/avalanchego-rpm.yml @@ -8,7 +8,7 @@ license: "BSD-3-Clause" depends: - "glibc >= 2.34" contents: - - src: "${AVALANCHEGO_BINARY}" + - src: "${BINARY_PATH}" dst: /var/opt/avalanchego/bin/avalanchego expand: true file_info: diff --git a/.github/packaging/nfpm/subnet-evm-rpm.yml b/.github/packaging/nfpm/subnet-evm-rpm.yml index 1ed5f887b008..db101a93ddd5 100644 --- a/.github/packaging/nfpm/subnet-evm-rpm.yml +++ b/.github/packaging/nfpm/subnet-evm-rpm.yml @@ -8,7 +8,7 @@ license: "BSD-3-Clause" depends: - "glibc >= 2.34" contents: - - src: "${SUBNET_EVM_BINARY}" + - src: "${BINARY_PATH}" # SUBNET_EVM_VM_ID is sourced from graft/subnet-evm/scripts/constants.sh dst: /var/opt/avalanchego/plugins/${SUBNET_EVM_VM_ID} expand: true diff --git a/.github/packaging/scripts/build-package.sh b/.github/packaging/scripts/build-package.sh index 63a144d9f2ad..dc4b1c226c77 100755 --- a/.github/packaging/scripts/build-package.sh +++ b/.github/packaging/scripts/build-package.sh @@ -56,12 +56,7 @@ fi # ── Package with nfpm ───────────────────────────────────────────── -case "${PACKAGE}" in - avalanchego) export AVALANCHEGO_BINARY="${BINARY_PATH}" ;; - subnet-evm) export SUBNET_EVM_BINARY="${BINARY_PATH}" ;; -esac - -export VERSION PACKAGE_ARCH +export VERSION PACKAGE_ARCH BINARY_PATH PKG_FILENAME="${PACKAGE}-${TAG}-${PACKAGE_ARCH}.${pkg_format_lower}" PKG_PATH="${OUTPUT_DIR}/${PKG_FILENAME}" From 03825b150145f196fccf05e1378132745a37e64a Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 11 May 2026 16:08:44 -0700 Subject: [PATCH 19/43] Source DEFAULT_VM_ID from pure-data file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract DEFAULT_VM_* into graft/subnet-evm/scripts/default-vm-data.sh — a side-effect-free file safe to source from anywhere. Replaces the grep-based lookup in resolve_subnet_evm_vm_id with a direct source. --- .github/packaging/scripts/lib-build-common.sh | 14 +++++--------- graft/subnet-evm/scripts/constants.sh | 4 ++-- graft/subnet-evm/scripts/default-vm-data.sh | 8 ++++++++ 3 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 graft/subnet-evm/scripts/default-vm-data.sh diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh index 4125e172edaa..c24abe7d2d71 100644 --- a/.github/packaging/scripts/lib-build-common.sh +++ b/.github/packaging/scripts/lib-build-common.sh @@ -63,15 +63,11 @@ build_binary() { # Resolve the subnet-evm VM ID from the canonical constants file. # Sets SUBNET_EVM_VM_ID (global) as a side effect. resolve_subnet_evm_vm_id() { - SUBNET_EVM_VM_ID=$( - grep '^DEFAULT_VM_ID=' "${REPO_ROOT}/graft/subnet-evm/scripts/constants.sh" \ - | cut -d'"' -f2 - ) - if [[ -z "${SUBNET_EVM_VM_ID}" ]]; then - echo "ERROR: could not resolve SUBNET_EVM_VM_ID" >&2 - return 1 - fi - export SUBNET_EVM_VM_ID + # shellcheck disable=SC1091 + source "${REPO_ROOT}/graft/subnet-evm/scripts/default-vm-data.sh" + # shellcheck disable=SC2154 + : "${DEFAULT_VM_ID:?DEFAULT_VM_ID must be set by default-vm-data.sh}" + export SUBNET_EVM_VM_ID="${DEFAULT_VM_ID}" } # Generate the nfpm changelog file. diff --git a/graft/subnet-evm/scripts/constants.sh b/graft/subnet-evm/scripts/constants.sh index 8f418fd31cbc..03f388c0ba3a 100644 --- a/graft/subnet-evm/scripts/constants.sh +++ b/graft/subnet-evm/scripts/constants.sh @@ -24,8 +24,8 @@ source "$AVALANCHE_PATH"/scripts/image_tag.sh # Subnet-EVM specific constants GOPATH="$(go env GOPATH)" DEFAULT_PLUGIN_DIR="${HOME}/.avalanchego/plugins" -DEFAULT_VM_NAME="subnet-evm" -DEFAULT_VM_ID="srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy" +# shellcheck disable=SC1091 +source "$SUBNET_EVM_PATH/scripts/default-vm-data.sh" # Docker image names # Defaults to local to avoid unintentional pushes. diff --git a/graft/subnet-evm/scripts/default-vm-data.sh b/graft/subnet-evm/scripts/default-vm-data.sh new file mode 100644 index 000000000000..4a53bdf06419 --- /dev/null +++ b/graft/subnet-evm/scripts/default-vm-data.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Pure-data VM identification constants for subnet-evm. Safe to source +# from anywhere — no side effects, no path computation, no subprocesses, +# no chained sources, no required environment. +# shellcheck disable=SC2034 + +DEFAULT_VM_NAME="subnet-evm" +DEFAULT_VM_ID="srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy" From dd5ec2afcb46f679b1c99507c01970fde5b80a34 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 12 May 2026 17:49:03 -0700 Subject: [PATCH 20/43] Add precondition guards for required env vars Replace doc-comment with runtime guard on REPO_ROOT at file scope, and add NFPM_CHANGELOG / NFPM_SIGNING_KEY guards at the top of generate_changelog and setup_gpg respectively. Each fires with an explicit message if the caller forgets to set the variable. --- .github/packaging/scripts/lib-build-common.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh index c24abe7d2d71..a5e76c46fb6c 100644 --- a/.github/packaging/scripts/lib-build-common.sh +++ b/.github/packaging/scripts/lib-build-common.sh @@ -3,7 +3,8 @@ # Shared functions for RPM and DEB package build scripts. # # Sourced (not executed) by build-package.sh. -# All functions expect REPO_ROOT to be set by the caller. + +: "${REPO_ROOT:?REPO_ROOT must be set by the caller}" readonly PACKAGER_NAME="Ava Labs" readonly PACKAGER_EMAIL="security@avalabs.org" @@ -75,6 +76,7 @@ resolve_subnet_evm_vm_id() { # Args: version (semver without "v" prefix) generate_changelog() { local version="${1:?version required}" + : "${NFPM_CHANGELOG:?NFPM_CHANGELOG must be set by the caller}" mkdir -p "$(dirname "${NFPM_CHANGELOG}")" cat > "${NFPM_CHANGELOG}" < Date: Tue, 12 May 2026 17:50:36 -0700 Subject: [PATCH 21/43] Address review-feedback cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bump actions/checkout@v4 -> @v5 on the overlay step, restoring parity with the initial checkout step and master. - Drop the elaborate header comment in default-vm-data.sh; the file is self-evidently pure data. - Remove redundant 'expand: true' from both nfpm configs — envsubst preprocesses the YAML, so by the time nfpm reads dst:, there are no ${VAR} references left to expand. - Update stale path in subnet-evm-rpm.yml comment to point at default-vm-data.sh (where DEFAULT_VM_ID now lives). --- .github/packaging/nfpm/avalanchego-rpm.yml | 1 - .github/packaging/nfpm/subnet-evm-rpm.yml | 3 +-- .github/workflows/build-rpm-release.yml | 2 +- graft/subnet-evm/scripts/default-vm-data.sh | 3 --- 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/packaging/nfpm/avalanchego-rpm.yml b/.github/packaging/nfpm/avalanchego-rpm.yml index 2675905ba9a0..34db9ff7f878 100644 --- a/.github/packaging/nfpm/avalanchego-rpm.yml +++ b/.github/packaging/nfpm/avalanchego-rpm.yml @@ -10,7 +10,6 @@ depends: contents: - src: "${BINARY_PATH}" dst: /var/opt/avalanchego/bin/avalanchego - expand: true file_info: mode: 0755 changelog: "${NFPM_CHANGELOG}" diff --git a/.github/packaging/nfpm/subnet-evm-rpm.yml b/.github/packaging/nfpm/subnet-evm-rpm.yml index db101a93ddd5..ebea63825a9a 100644 --- a/.github/packaging/nfpm/subnet-evm-rpm.yml +++ b/.github/packaging/nfpm/subnet-evm-rpm.yml @@ -9,9 +9,8 @@ depends: - "glibc >= 2.34" contents: - src: "${BINARY_PATH}" - # SUBNET_EVM_VM_ID is sourced from graft/subnet-evm/scripts/constants.sh + # SUBNET_EVM_VM_ID is sourced from graft/subnet-evm/scripts/default-vm-data.sh dst: /var/opt/avalanchego/plugins/${SUBNET_EVM_VM_ID} - expand: true file_info: mode: 0755 changelog: "${NFPM_CHANGELOG}" diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index 87dba994f024..2ee8e36bf253 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -44,7 +44,7 @@ jobs: # cannot be combined. - name: Overlay packaging scripts from workflow branch if: github.event.inputs.tag - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: path: .packaging-overlay sparse-checkout: .github/packaging diff --git a/graft/subnet-evm/scripts/default-vm-data.sh b/graft/subnet-evm/scripts/default-vm-data.sh index 4a53bdf06419..65d5f6adb2c4 100644 --- a/graft/subnet-evm/scripts/default-vm-data.sh +++ b/graft/subnet-evm/scripts/default-vm-data.sh @@ -1,7 +1,4 @@ #!/usr/bin/env bash -# Pure-data VM identification constants for subnet-evm. Safe to source -# from anywhere — no side effects, no path computation, no subprocesses, -# no chained sources, no required environment. # shellcheck disable=SC2034 DEFAULT_VM_NAME="subnet-evm" From c821157cdd50cf07d24846852c77f0eb9601b2ff Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 12 May 2026 18:10:01 -0700 Subject: [PATCH 22/43] Address library-polish review feedback - Document where avalanchego_path comes from (set by scripts/constants.sh, sourced in init_build_env). - Sanity-check the generated changelog actually contains the expected semver line; catches silent heredoc substitution failures. - assert_files_exist now collects all missing files and reports them together instead of failing on the first one. --- .github/packaging/scripts/lib-build-common.sh | 8 ++++++++ .github/packaging/scripts/lib-validate-common.sh | 10 ++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh index a5e76c46fb6c..56d331daa7e4 100644 --- a/.github/packaging/scripts/lib-build-common.sh +++ b/.github/packaging/scripts/lib-build-common.sh @@ -41,6 +41,7 @@ build_binary() { avalanchego) echo "Building avalanchego..." "${REPO_ROOT}/scripts/build.sh" + # avalanchego_path is set by scripts/constants.sh, sourced in init_build_env # shellcheck disable=SC2154 BINARY_PATH="${avalanchego_path}" ;; @@ -87,6 +88,13 @@ generate_changelog() { changes: - note: "See https://github.com/ava-labs/avalanchego/releases/tag/v${version}" EOF + + # Sanity-check the heredoc wrote the version line (catches silent + # substitution failures: empty file, unresolved ${version}, etc.) + if ! grep -q "^- semver: ${version}\$" "${NFPM_CHANGELOG}"; then + echo "ERROR: generated changelog ${NFPM_CHANGELOG} missing 'semver: ${version}'" >&2 + return 1 + fi } # Set up GPG signing (import provided key, reuse ephemeral, or generate new). diff --git a/.github/packaging/scripts/lib-validate-common.sh b/.github/packaging/scripts/lib-validate-common.sh index 5006d2b39399..7e444d689c9e 100755 --- a/.github/packaging/scripts/lib-validate-common.sh +++ b/.github/packaging/scripts/lib-validate-common.sh @@ -29,10 +29,12 @@ detect_host_arch() { # Args: pkg_dir file1 [file2 ...] assert_files_exist() { local pkg_dir="${1:?package directory required}"; shift + local missing=() for f in "$@"; do - if [[ ! -f "${pkg_dir}/${f}" ]]; then - echo "ERROR: expected file not found: ${pkg_dir}/${f}" >&2 - exit 1 - fi + [[ -f "${pkg_dir}/${f}" ]] || missing+=("${pkg_dir}/${f}") done + if (( ${#missing[@]} > 0 )); then + printf 'ERROR: expected file not found: %s\n' "${missing[@]}" >&2 + exit 1 + fi } From 70927aab4a7a23df2b09d5b922c11964ea08ce3b Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 18 May 2026 14:38:41 -0700 Subject: [PATCH 23/43] Address review nit. --- .github/packaging/scripts/lib-validate-common.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/packaging/scripts/lib-validate-common.sh b/.github/packaging/scripts/lib-validate-common.sh index 7e444d689c9e..f8b1653bb974 100755 --- a/.github/packaging/scripts/lib-validate-common.sh +++ b/.github/packaging/scripts/lib-validate-common.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash # Shared functions for package validation scripts. -# Sourced by validate-rpm.sh. # Detect host architecture for the given package format. # Sets PACKAGE_ARCH (global) if not already set by the caller. From 49e9695c0fa416ca28a6a4fa88d1680ed8895835 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 18 May 2026 16:14:05 -0700 Subject: [PATCH 24/43] Fix subnet-evm packaging constants --- .github/packaging/nfpm/subnet-evm-rpm.yml | 2 +- .github/packaging/scripts/lib-build-common.sh | 30 ++++++++++++++----- graft/subnet-evm/scripts/constants.sh | 4 +-- graft/subnet-evm/scripts/default-vm-data.sh | 5 ---- 4 files changed, 26 insertions(+), 15 deletions(-) delete mode 100644 graft/subnet-evm/scripts/default-vm-data.sh diff --git a/.github/packaging/nfpm/subnet-evm-rpm.yml b/.github/packaging/nfpm/subnet-evm-rpm.yml index ebea63825a9a..a27a837f9adf 100644 --- a/.github/packaging/nfpm/subnet-evm-rpm.yml +++ b/.github/packaging/nfpm/subnet-evm-rpm.yml @@ -9,7 +9,7 @@ depends: - "glibc >= 2.34" contents: - src: "${BINARY_PATH}" - # SUBNET_EVM_VM_ID is sourced from graft/subnet-evm/scripts/default-vm-data.sh + # SUBNET_EVM_VM_ID is sourced from graft/subnet-evm/scripts/constants.sh dst: /var/opt/avalanchego/plugins/${SUBNET_EVM_VM_ID} file_info: mode: 0755 diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh index 56d331daa7e4..55c1755d501d 100644 --- a/.github/packaging/scripts/lib-build-common.sh +++ b/.github/packaging/scripts/lib-build-common.sh @@ -9,6 +9,13 @@ readonly PACKAGER_NAME="Ava Labs" readonly PACKAGER_EMAIL="security@avalabs.org" +secret_key_file_is_usable() { + local key_file="${1:?key file required}" + + gpg --batch --show-keys --with-colons "${key_file}" \ + | awk -F: '$1 == "sec" && $2 !~ /e/ { found = 1 } END { exit found ? 0 : 1 }' +} + # Initialize the build environment inside the container. # Marks the bind-mounted source tree as git-safe, sources project # scripts (constants.sh, git_commit.sh), and disables Go VCS stamping. @@ -65,11 +72,16 @@ build_binary() { # Resolve the subnet-evm VM ID from the canonical constants file. # Sets SUBNET_EVM_VM_ID (global) as a side effect. resolve_subnet_evm_vm_id() { - # shellcheck disable=SC1091 - source "${REPO_ROOT}/graft/subnet-evm/scripts/default-vm-data.sh" - # shellcheck disable=SC2154 - : "${DEFAULT_VM_ID:?DEFAULT_VM_ID must be set by default-vm-data.sh}" - export SUBNET_EVM_VM_ID="${DEFAULT_VM_ID}" + SUBNET_EVM_VM_ID="$( + ( + # shellcheck disable=SC1091 + source "${REPO_ROOT}/graft/subnet-evm/scripts/constants.sh" + # shellcheck disable=SC2154 + : "${DEFAULT_VM_ID:?DEFAULT_VM_ID must be set by constants.sh}" + echo "${DEFAULT_VM_ID}" + ) + )" + export SUBNET_EVM_VM_ID } # Generate the nfpm changelog file. @@ -115,11 +127,15 @@ setup_gpg() { echo "Using provided GPG key for signing" gpg --batch --import "${gpg_key_file}" cp "${gpg_key_file}" "${NFPM_SIGNING_KEY}" - elif [[ -f "${NFPM_SIGNING_KEY}" ]]; then + elif [[ -f "${NFPM_SIGNING_KEY}" ]] && secret_key_file_is_usable "${NFPM_SIGNING_KEY}"; then echo "Reusing existing ephemeral GPG key" gpg --batch --import "${NFPM_SIGNING_KEY}" else - echo "Generating ephemeral GPG key for signing" + if [[ -f "${NFPM_SIGNING_KEY}" ]]; then + echo "Existing ephemeral GPG key is expired or unusable; generating a new key" + else + echo "Generating ephemeral GPG key for signing" + fi gpg --batch --gen-key < Date: Mon, 18 May 2026 17:29:19 -0700 Subject: [PATCH 25/43] Clarify package build script scope --- .github/packaging/scripts/build-package.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/packaging/scripts/build-package.sh b/.github/packaging/scripts/build-package.sh index dc4b1c226c77..24b90bd91495 100755 --- a/.github/packaging/scripts/build-package.sh +++ b/.github/packaging/scripts/build-package.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash -# Build and sign an RPM package inside the container. +# Build and sign a Linux package inside the container. +# PKG_FORMAT defaults to RPM and is retained for future (DEB and possibly other) +# packaging formats reuse. # # Required env vars: # PACKAGE - "avalanchego" or "subnet-evm" @@ -10,7 +12,7 @@ # OUTPUT_DIR - Directory for the output package (bind-mounted from host) # # Optional env vars: -# PKG_FORMAT - Package format identifier (default: RPM) +# PKG_FORMAT - Package format identifier (default: RPM) # RPM_GPG_KEY_FILE - Path to GPG private key # NFPM_RPM_PASSPHRASE - GPG passphrase # AVALANCHEGO_COMMIT - Git commit hash (auto-detected if not set) From 6f0374572bcda1d8255ce185926f281fddc19e9b Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Mon, 18 May 2026 19:58:21 -0700 Subject: [PATCH 26/43] Use passphrased ephemeral RPM signing key --- .github/packaging/scripts/build-package.sh | 9 ++++--- .github/packaging/scripts/lib-build-common.sh | 27 ++++++++++++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/.github/packaging/scripts/build-package.sh b/.github/packaging/scripts/build-package.sh index 24b90bd91495..56d2d2c341a3 100755 --- a/.github/packaging/scripts/build-package.sh +++ b/.github/packaging/scripts/build-package.sh @@ -49,13 +49,14 @@ generate_changelog "${VERSION}" GPG_KEY_FILE="${RPM_GPG_KEY_FILE:-}" GPG_PUBLIC_KEY="${OUTPUT_DIR}/${PKG_FORMAT}-GPG-KEY-avalanchego" -setup_gpg "${GPG_KEY_FILE}" "${GPG_PUBLIC_KEY}" "${PKG_FORMAT}" - -# Ephemeral keys have no passphrase; nfpm needs the variable set empty +# Ephemeral keys use a known throwaway passphrase so local and CI builds +# exercise passphrase handling without release credentials. if [[ -z "${GPG_KEY_FILE}" ]]; then - export NFPM_RPM_PASSPHRASE="" + use_ephemeral_gpg_passphrase NFPM_RPM_PASSPHRASE fi +setup_gpg "${GPG_KEY_FILE}" "${GPG_PUBLIC_KEY}" "${PKG_FORMAT}" + # ── Package with nfpm ───────────────────────────────────────────── export VERSION PACKAGE_ARCH BINARY_PATH diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh index 55c1755d501d..724e3b319b17 100644 --- a/.github/packaging/scripts/lib-build-common.sh +++ b/.github/packaging/scripts/lib-build-common.sh @@ -8,6 +8,14 @@ readonly PACKAGER_NAME="Ava Labs" readonly PACKAGER_EMAIL="security@avalabs.org" +readonly EPHEMERAL_GPG_PASSPHRASE="avalanchego-ephemeral-gpg-passphrase" +readonly EPHEMERAL_GPG_MARKER_SUFFIX=".ephemeral-passphrase-v1" + +use_ephemeral_gpg_passphrase() { + local passphrase_env="${1:?passphrase env var required}" + + declare -gx "${passphrase_env}=${EPHEMERAL_GPG_PASSPHRASE}" +} secret_key_file_is_usable() { local key_file="${1:?key file required}" @@ -122,22 +130,24 @@ setup_gpg() { : "${NFPM_SIGNING_KEY:?NFPM_SIGNING_KEY must be set by the caller}" mkdir -p "$(dirname "${NFPM_SIGNING_KEY}")" + local ephemeral_marker="${NFPM_SIGNING_KEY}${EPHEMERAL_GPG_MARKER_SUFFIX}" if [[ -n "${gpg_key_file}" ]]; then echo "Using provided GPG key for signing" gpg --batch --import "${gpg_key_file}" cp "${gpg_key_file}" "${NFPM_SIGNING_KEY}" - elif [[ -f "${NFPM_SIGNING_KEY}" ]] && secret_key_file_is_usable "${NFPM_SIGNING_KEY}"; then - echo "Reusing existing ephemeral GPG key" + rm -f "${ephemeral_marker}" + elif [[ -f "${NFPM_SIGNING_KEY}" && -f "${ephemeral_marker}" ]] \ + && secret_key_file_is_usable "${NFPM_SIGNING_KEY}"; then + echo "Reusing existing passphrase-protected ephemeral GPG key" gpg --batch --import "${NFPM_SIGNING_KEY}" else if [[ -f "${NFPM_SIGNING_KEY}" ]]; then - echo "Existing ephemeral GPG key is expired or unusable; generating a new key" + echo "Existing ephemeral GPG key is missing passphrase marker, expired, or unusable; generating a new key" else - echo "Generating ephemeral GPG key for signing" + echo "Generating passphrase-protected ephemeral GPG key for signing" fi - gpg --batch --gen-key < "${NFPM_SIGNING_KEY}" + gpg --batch --pinentry-mode loopback --passphrase "${EPHEMERAL_GPG_PASSPHRASE}" \ + --armor --export-secret-keys "${PACKAGER_EMAIL}" > "${NFPM_SIGNING_KEY}" + printf "passphrase-protected\n" > "${ephemeral_marker}" fi gpg --batch --armor --export "${PACKAGER_EMAIL}" > "${public_key_out}" From 361456fcce85fe74b6402ceb3dbacb745d9c582e Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 19 May 2026 00:15:44 -0700 Subject: [PATCH 27/43] Fix RPM packaging overlay for manual builds --- .github/workflows/build-rpm-release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index 2ee8e36bf253..50cdf911ddd6 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -52,6 +52,8 @@ jobs: - name: Apply packaging overlay if: github.event.inputs.tag run: | + rm -rf .github/packaging + mkdir -p .github cp -r .packaging-overlay/.github/packaging .github/packaging rm -rf .packaging-overlay shell: bash From 50dc30c0d03aedf38c9a050fe34b08f3d8cc8eef Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 19 May 2026 18:41:18 -0700 Subject: [PATCH 28/43] Rename GPG env vars to format-agnostic names RPM_GPG_KEY_FILE -> GPG_KEY_FILE. NFPM_RPM_PASSPHRASE -> GPG_KEY_PASSPHRASE (external); build-package.sh translates this to the nfpm-required NFPM_${PKG_FORMAT}_PASSPHRASE before invoking nfpm. --- .github/packaging/Taskfile.yml | 12 ++++++------ .github/packaging/scripts/build-package.sh | 14 ++++++++++---- .github/workflows/build-rpm-release.yml | 4 ++-- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.github/packaging/Taskfile.yml b/.github/packaging/Taskfile.yml index d5f5c3238427..140534280eda 100644 --- a/.github/packaging/Taskfile.yml +++ b/.github/packaging/Taskfile.yml @@ -66,7 +66,7 @@ tasks: docker run --rm -v {{.REPO_ROOT}}:/build -v {{.PACKAGING_OUTPUT_DIR}}:/output - {{if .RPM_GPG_KEY_FILE}}-v {{.RPM_GPG_KEY_FILE}}:{{.RPM_GPG_KEY_FILE}}:ro{{end}} + {{if .GPG_KEY_FILE}}-v {{.GPG_KEY_FILE}}:{{.GPG_KEY_FILE}}:ro{{end}} -e PKG_FORMAT=RPM -e PACKAGE=avalanchego -e VERSION={{trimPrefix "v" .RPM_TAG}} @@ -74,8 +74,8 @@ tasks: -e PACKAGE_ARCH={{.PACKAGE_ARCH}} -e OUTPUT_DIR=/output -e AVALANCHEGO_COMMIT={{.PACKAGING_GIT_COMMIT}} - {{if .RPM_GPG_KEY_FILE}}-e RPM_GPG_KEY_FILE={{.RPM_GPG_KEY_FILE}}{{end}} - {{if .NFPM_RPM_PASSPHRASE}}-e NFPM_RPM_PASSPHRASE={{.NFPM_RPM_PASSPHRASE}}{{end}} + {{if .GPG_KEY_FILE}}-e GPG_KEY_FILE={{.GPG_KEY_FILE}}{{end}} + {{if .GPG_KEY_PASSPHRASE}}-e GPG_KEY_PASSPHRASE={{.GPG_KEY_PASSPHRASE}}{{end}} {{.PACKAGING_DOCKER_IMAGE}} .github/packaging/scripts/build-package.sh @@ -91,7 +91,7 @@ tasks: docker run --rm -v {{.REPO_ROOT}}:/build -v {{.PACKAGING_OUTPUT_DIR}}:/output - {{if .RPM_GPG_KEY_FILE}}-v {{.RPM_GPG_KEY_FILE}}:{{.RPM_GPG_KEY_FILE}}:ro{{end}} + {{if .GPG_KEY_FILE}}-v {{.GPG_KEY_FILE}}:{{.GPG_KEY_FILE}}:ro{{end}} -e PKG_FORMAT=RPM -e PACKAGE=subnet-evm -e VERSION={{trimPrefix "v" .RPM_TAG}} @@ -99,8 +99,8 @@ tasks: -e PACKAGE_ARCH={{.PACKAGE_ARCH}} -e OUTPUT_DIR=/output -e AVALANCHEGO_COMMIT={{.PACKAGING_GIT_COMMIT}} - {{if .RPM_GPG_KEY_FILE}}-e RPM_GPG_KEY_FILE={{.RPM_GPG_KEY_FILE}}{{end}} - {{if .NFPM_RPM_PASSPHRASE}}-e NFPM_RPM_PASSPHRASE={{.NFPM_RPM_PASSPHRASE}}{{end}} + {{if .GPG_KEY_FILE}}-e GPG_KEY_FILE={{.GPG_KEY_FILE}}{{end}} + {{if .GPG_KEY_PASSPHRASE}}-e GPG_KEY_PASSPHRASE={{.GPG_KEY_PASSPHRASE}}{{end}} {{.PACKAGING_DOCKER_IMAGE}} .github/packaging/scripts/build-package.sh diff --git a/.github/packaging/scripts/build-package.sh b/.github/packaging/scripts/build-package.sh index 56d2d2c341a3..f0b898eac2f5 100755 --- a/.github/packaging/scripts/build-package.sh +++ b/.github/packaging/scripts/build-package.sh @@ -13,8 +13,8 @@ # # Optional env vars: # PKG_FORMAT - Package format identifier (default: RPM) -# RPM_GPG_KEY_FILE - Path to GPG private key -# NFPM_RPM_PASSPHRASE - GPG passphrase +# GPG_KEY_FILE - Path to GPG private key +# GPG_KEY_PASSPHRASE - GPG passphrase # AVALANCHEGO_COMMIT - Git commit hash (auto-detected if not set) set -euo pipefail @@ -46,13 +46,19 @@ generate_changelog "${VERSION}" # ── GPG signing ─────────────────────────────────────────────────── -GPG_KEY_FILE="${RPM_GPG_KEY_FILE:-}" +GPG_KEY_FILE="${GPG_KEY_FILE:-}" GPG_PUBLIC_KEY="${OUTPUT_DIR}/${PKG_FORMAT}-GPG-KEY-avalanchego" +# nfpm reads the signing passphrase from a packager-specific env var +# (NFPM_RPM_PASSPHRASE, NFPM_DEB_PASSPHRASE, ...); mirror our format- +# agnostic GPG_KEY_PASSPHRASE into the name nfpm expects. +nfpm_passphrase_var="NFPM_${PKG_FORMAT}_PASSPHRASE" +export "${nfpm_passphrase_var}=${GPG_KEY_PASSPHRASE:-}" + # Ephemeral keys use a known throwaway passphrase so local and CI builds # exercise passphrase handling without release credentials. if [[ -z "${GPG_KEY_FILE}" ]]; then - use_ephemeral_gpg_passphrase NFPM_RPM_PASSPHRASE + use_ephemeral_gpg_passphrase "${nfpm_passphrase_var}" fi setup_gpg "${GPG_KEY_FILE}" "${GPG_PUBLIC_KEY}" "${PKG_FORMAT}" diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index 50cdf911ddd6..faa3bb819462 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -72,8 +72,8 @@ jobs: run: ./scripts/run_task.sh --taskfile .github/packaging/Taskfile.yml test-build-rpms env: PACKAGING_TAG: ${{ steps.setup.outputs.tag }} - RPM_GPG_KEY_FILE: ${{ steps.setup.outputs.gpg-key-file }} - NFPM_RPM_PASSPHRASE: ${{ secrets.RPM_GPG_PASSPHRASE }} + GPG_KEY_FILE: ${{ steps.setup.outputs.gpg-key-file }} + GPG_KEY_PASSPHRASE: ${{ secrets.RPM_GPG_PASSPHRASE }} - name: Upload RPMs as artifacts if: github.event_name != 'pull_request' From cdc384f3a510e4448738542dd6bed9ac76f4015b Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 19 May 2026 18:43:52 -0700 Subject: [PATCH 29/43] Move docstring comments inline with their definitions Drop the duplicate env-var docstring block from build-package.sh (the asserts and PKG_FORMAT comment now self-document). Move run_nfpm_package's Args block inline above each local declaration. --- .github/packaging/scripts/build-package.sh | 25 +++++-------------- .github/packaging/scripts/lib-build-common.sh | 10 +++----- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/.github/packaging/scripts/build-package.sh b/.github/packaging/scripts/build-package.sh index f0b898eac2f5..01fe4080bca7 100755 --- a/.github/packaging/scripts/build-package.sh +++ b/.github/packaging/scripts/build-package.sh @@ -1,30 +1,17 @@ #!/usr/bin/env bash # Build and sign a Linux package inside the container. -# PKG_FORMAT defaults to RPM and is retained for future (DEB and possibly other) -# packaging formats reuse. -# -# Required env vars: -# PACKAGE - "avalanchego" or "subnet-evm" -# VERSION - Semantic version without "v" prefix (e.g., "1.14.1") -# TAG - Git tag (e.g., "v1.14.1") -# PACKAGE_ARCH - Architecture (x86_64 or aarch64) -# OUTPUT_DIR - Directory for the output package (bind-mounted from host) -# -# Optional env vars: -# PKG_FORMAT - Package format identifier (default: RPM) -# GPG_KEY_FILE - Path to GPG private key -# GPG_KEY_PASSPHRASE - GPG passphrase -# AVALANCHEGO_COMMIT - Git commit hash (auto-detected if not set) set -euo pipefail : "${PACKAGE:?PACKAGE must be set (avalanchego or subnet-evm)}" -: "${VERSION:?VERSION must be set}" -: "${TAG:?TAG must be set}" -: "${PACKAGE_ARCH:?PACKAGE_ARCH must be set}" -: "${OUTPUT_DIR:?OUTPUT_DIR must be set}" +: "${VERSION:?VERSION must be set (semver without v prefix, e.g. 1.14.1)}" +: "${TAG:?TAG must be set (git tag, e.g. v1.14.1)}" +: "${PACKAGE_ARCH:?PACKAGE_ARCH must be set (x86_64 or aarch64)}" +: "${OUTPUT_DIR:?OUTPUT_DIR must be set (bind-mounted output dir)}" +# Package format identifier; defaults to RPM and is retained for +# future (DEB and possibly other) packaging formats reuse. PKG_FORMAT="${PKG_FORMAT:-RPM}" pkg_format_lower="${PKG_FORMAT,,}" diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh index 724e3b319b17..9e3834649025 100644 --- a/.github/packaging/scripts/lib-build-common.sh +++ b/.github/packaging/scripts/lib-build-common.sh @@ -172,16 +172,14 @@ GPGEOF # nfpm does not expand env vars in top-level fields (changelog, # signature.key_file). Preprocess the config template with envsubst # so all ${VAR} references resolve before nfpm sees them. -# -# Args: -# config_template - path to nfpm YAML template (with ${VAR} placeholders) -# resolved_path - output path for the preprocessed config -# packager - nfpm packager name ("rpm" or "deb") -# target_path - output path for the built package run_nfpm_package() { + # path to nfpm YAML template (with ${VAR} placeholders) local config_template="${1:?config template path required}" + # output path for the preprocessed config local resolved_path="${2:?resolved config path required}" + # nfpm packager name ("rpm" or "deb") local packager="${3:?packager name required}" + # output path for the built package local target_path="${4:?target path required}" mkdir -p "$(dirname "${target_path}")" From 6ebb19e6f6faba10fae7016e28b3259a27a16098 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 19 May 2026 18:45:45 -0700 Subject: [PATCH 30/43] Require DOCKERFILE explicitly in build-builder-image.sh Drop the Dockerfile.rpm default; Taskfile entrypoints already pass DOCKERFILE explicitly, so the default just hides accidental misuse. --- .github/packaging/scripts/build-builder-image.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/packaging/scripts/build-builder-image.sh b/.github/packaging/scripts/build-builder-image.sh index 81c5b2ad3c05..f91cb5c40682 100755 --- a/.github/packaging/scripts/build-builder-image.sh +++ b/.github/packaging/scripts/build-builder-image.sh @@ -9,15 +9,14 @@ # GO_VERSION - Go version to install (e.g., "1.24.12") # DOCKER_IMAGE - Name for the built Docker image # CONTEXT_DIR - Path to the Dockerfile directory -# -# Optional env vars: -# DOCKERFILE - Dockerfile name (default: "Dockerfile.rpm") +# DOCKERFILE - Dockerfile name (e.g., "Dockerfile.rpm") set -euo pipefail : "${GO_VERSION:?GO_VERSION must be set}" : "${DOCKER_IMAGE:?DOCKER_IMAGE must be set}" : "${CONTEXT_DIR:?CONTEXT_DIR must be set}" +: "${DOCKERFILE:?DOCKERFILE must be set (e.g. Dockerfile.rpm)}" command -v jq >/dev/null 2>&1 || { echo "ERROR: jq is required but not found on PATH" >&2; exit 1; } @@ -56,6 +55,6 @@ fi docker build "${build_flags[@]}" \ --build-arg GO_VERSION="${GO_VERSION}" \ --build-arg GO_CHECKSUM="${checksum}" \ - -f "${CONTEXT_DIR}/${DOCKERFILE:-Dockerfile.rpm}" \ + -f "${CONTEXT_DIR}/${DOCKERFILE}" \ -t "${DOCKER_IMAGE}" \ "${CONTEXT_DIR}" From ef8bd3f1093f983cd88531d7e2cb371da46df7be Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 19 May 2026 18:47:52 -0700 Subject: [PATCH 31/43] Drop format-prefix from GPG public key filename ${PKG_FORMAT}-GPG-KEY-avalanchego -> GPG-KEY-avalanchego. Both RPM and DEB share the same signing key; the format prefix was cosmetic. --- .github/packaging/scripts/build-package.sh | 2 +- .github/packaging/scripts/validate-rpm.sh | 4 ++-- .github/workflows/build-rpm-release.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/packaging/scripts/build-package.sh b/.github/packaging/scripts/build-package.sh index 01fe4080bca7..1166a1a0f570 100755 --- a/.github/packaging/scripts/build-package.sh +++ b/.github/packaging/scripts/build-package.sh @@ -34,7 +34,7 @@ generate_changelog "${VERSION}" # ── GPG signing ─────────────────────────────────────────────────── GPG_KEY_FILE="${GPG_KEY_FILE:-}" -GPG_PUBLIC_KEY="${OUTPUT_DIR}/${PKG_FORMAT}-GPG-KEY-avalanchego" +GPG_PUBLIC_KEY="${OUTPUT_DIR}/GPG-KEY-avalanchego" # nfpm reads the signing passphrase from a packager-specific env var # (NFPM_RPM_PASSPHRASE, NFPM_DEB_PASSPHRASE, ...); mirror our format- diff --git a/.github/packaging/scripts/validate-rpm.sh b/.github/packaging/scripts/validate-rpm.sh index 9e4c7b7f0e5a..39177f4a9b09 100755 --- a/.github/packaging/scripts/validate-rpm.sh +++ b/.github/packaging/scripts/validate-rpm.sh @@ -40,8 +40,8 @@ docker run --rm \ rockylinux:9 \ bash -euxc ' # Import GPG key and verify signatures if available - if [[ -f /rpms/RPM-GPG-KEY-avalanchego ]]; then - rpm --import /rpms/RPM-GPG-KEY-avalanchego + if [[ -f /rpms/GPG-KEY-avalanchego ]]; then + rpm --import /rpms/GPG-KEY-avalanchego rpm -K "/rpms/avalanchego-'"${TAG}"'-'"${PACKAGE_ARCH}"'.rpm" rpm -K "/rpms/subnet-evm-'"${TAG}"'-'"${PACKAGE_ARCH}"'.rpm" else diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index faa3bb819462..1599f23e9629 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -83,7 +83,7 @@ jobs: path: | build/rpm/avalanchego-${{ steps.setup.outputs.tag }}-${{ matrix.rpm_arch }}.rpm build/rpm/subnet-evm-${{ steps.setup.outputs.tag }}-${{ matrix.rpm_arch }}.rpm - build/rpm/RPM-GPG-KEY-avalanchego + build/rpm/GPG-KEY-avalanchego - name: Cleanup if: always() From 9836e685fe950cc62b57bc027473a09b0b7a2dbf Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 19 May 2026 18:51:12 -0700 Subject: [PATCH 32/43] Simplify ephemeral GPG key reuse in setup_gpg Drop the marker-file + secret_key_file_is_usable check; reuse the existing ephemeral key when the file exists, generate a new one otherwise. The marker/usability machinery added state without materially helping the multi-build flow (where two RPM builds in the same task invocation must share one signing key). --- .github/packaging/scripts/lib-build-common.sh | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh index 9e3834649025..b760e3c7193d 100644 --- a/.github/packaging/scripts/lib-build-common.sh +++ b/.github/packaging/scripts/lib-build-common.sh @@ -9,7 +9,6 @@ readonly PACKAGER_NAME="Ava Labs" readonly PACKAGER_EMAIL="security@avalabs.org" readonly EPHEMERAL_GPG_PASSPHRASE="avalanchego-ephemeral-gpg-passphrase" -readonly EPHEMERAL_GPG_MARKER_SUFFIX=".ephemeral-passphrase-v1" use_ephemeral_gpg_passphrase() { local passphrase_env="${1:?passphrase env var required}" @@ -17,13 +16,6 @@ use_ephemeral_gpg_passphrase() { declare -gx "${passphrase_env}=${EPHEMERAL_GPG_PASSPHRASE}" } -secret_key_file_is_usable() { - local key_file="${1:?key file required}" - - gpg --batch --show-keys --with-colons "${key_file}" \ - | awk -F: '$1 == "sec" && $2 !~ /e/ { found = 1 } END { exit found ? 0 : 1 }' -} - # Initialize the build environment inside the container. # Marks the bind-mounted source tree as git-safe, sources project # scripts (constants.sh, git_commit.sh), and disables Go VCS stamping. @@ -130,23 +122,16 @@ setup_gpg() { : "${NFPM_SIGNING_KEY:?NFPM_SIGNING_KEY must be set by the caller}" mkdir -p "$(dirname "${NFPM_SIGNING_KEY}")" - local ephemeral_marker="${NFPM_SIGNING_KEY}${EPHEMERAL_GPG_MARKER_SUFFIX}" if [[ -n "${gpg_key_file}" ]]; then echo "Using provided GPG key for signing" gpg --batch --import "${gpg_key_file}" cp "${gpg_key_file}" "${NFPM_SIGNING_KEY}" - rm -f "${ephemeral_marker}" - elif [[ -f "${NFPM_SIGNING_KEY}" && -f "${ephemeral_marker}" ]] \ - && secret_key_file_is_usable "${NFPM_SIGNING_KEY}"; then - echo "Reusing existing passphrase-protected ephemeral GPG key" + elif [[ -f "${NFPM_SIGNING_KEY}" ]]; then + echo "Reusing existing ephemeral GPG key" gpg --batch --import "${NFPM_SIGNING_KEY}" else - if [[ -f "${NFPM_SIGNING_KEY}" ]]; then - echo "Existing ephemeral GPG key is missing passphrase marker, expired, or unusable; generating a new key" - else - echo "Generating passphrase-protected ephemeral GPG key for signing" - fi + echo "Generating ephemeral GPG key for signing" gpg --batch --pinentry-mode loopback --gen-key < "${NFPM_SIGNING_KEY}" - printf "passphrase-protected\n" > "${ephemeral_marker}" fi gpg --batch --armor --export "${PACKAGER_EMAIL}" > "${public_key_out}" From 4ea5591e19b351e6009bf28f810f71954c77365d Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 19 May 2026 18:53:02 -0700 Subject: [PATCH 33/43] Remove host arch detection from validate-rpm.sh Taskfile now passes PACKAGE_ARCH to validate-rpms (defaulting to PACKAGING_HOST_ARCH). validate-rpm.sh asserts the var is set instead of duplicating the uname mapping; lib-validate-common.sh loses detect_host_arch entirely. --- .github/packaging/Taskfile.yml | 1 + .../packaging/scripts/lib-validate-common.sh | 22 ------------------- .github/packaging/scripts/validate-rpm.sh | 2 +- 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/.github/packaging/Taskfile.yml b/.github/packaging/Taskfile.yml index 140534280eda..68e45e97265a 100644 --- a/.github/packaging/Taskfile.yml +++ b/.github/packaging/Taskfile.yml @@ -115,5 +115,6 @@ tasks: env: TAG: '{{.PACKAGING_TAG}}' GIT_COMMIT: '{{.PACKAGING_GIT_COMMIT}}' + PACKAGE_ARCH: '{{.PACKAGE_ARCH | default .PACKAGING_HOST_ARCH}}' cmds: - cmd: '{{.REPO_ROOT}}/.github/packaging/scripts/validate-rpm.sh' diff --git a/.github/packaging/scripts/lib-validate-common.sh b/.github/packaging/scripts/lib-validate-common.sh index f8b1653bb974..cbb57221b790 100755 --- a/.github/packaging/scripts/lib-validate-common.sh +++ b/.github/packaging/scripts/lib-validate-common.sh @@ -1,28 +1,6 @@ #!/usr/bin/env bash # Shared functions for package validation scripts. -# Detect host architecture for the given package format. -# Sets PACKAGE_ARCH (global) if not already set by the caller. -# -# Args: format ("RPM") -detect_host_arch() { - local format="${1:?format required}" - if [[ -n "${PACKAGE_ARCH:-}" ]]; then - return # already set by caller - fi - local arch - arch=$(uname -m) - case "${format}" in - RPM) - case "${arch}" in - x86_64) PACKAGE_ARCH="x86_64" ;; - arm64) PACKAGE_ARCH="aarch64" ;; - *) PACKAGE_ARCH="${arch}" ;; - esac - ;; - esac -} - # Verify that all expected package files exist in a directory. # # Args: pkg_dir file1 [file2 ...] diff --git a/.github/packaging/scripts/validate-rpm.sh b/.github/packaging/scripts/validate-rpm.sh index 39177f4a9b09..4ade3ca808fc 100755 --- a/.github/packaging/scripts/validate-rpm.sh +++ b/.github/packaging/scripts/validate-rpm.sh @@ -16,6 +16,7 @@ set -euo pipefail : "${TAG:?TAG must be set}" : "${GIT_COMMIT:?GIT_COMMIT must be set}" +: "${PACKAGE_ARCH:?PACKAGE_ARCH must be set (x86_64 or aarch64)}" REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" RPM_DIR="${REPO_ROOT}/build/rpm" @@ -27,7 +28,6 @@ source "${SCRIPTS_DIR}/lib-build-common.sh" source "${SCRIPTS_DIR}/lib-validate-common.sh" resolve_subnet_evm_vm_id -detect_host_arch RPM assert_files_exist "${RPM_DIR}" \ "avalanchego-${TAG}-${PACKAGE_ARCH}.rpm" \ From 0d1e1aac5fbb6549bd2bb9469fce3a7b808a2b83 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 19 May 2026 18:54:35 -0700 Subject: [PATCH 34/43] Gate GPG_KEY_PASSPHRASE on non-pull_request event Mirrors the GPG_PRIVATE_KEY condition in the setup step; secrets are unavailable on PR builds, so the passphrase shouldn't be referenced unconditionally. --- .github/workflows/build-rpm-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index 1599f23e9629..5a4d422767f3 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -73,7 +73,7 @@ jobs: env: PACKAGING_TAG: ${{ steps.setup.outputs.tag }} GPG_KEY_FILE: ${{ steps.setup.outputs.gpg-key-file }} - GPG_KEY_PASSPHRASE: ${{ secrets.RPM_GPG_PASSPHRASE }} + GPG_KEY_PASSPHRASE: ${{ github.event_name != 'pull_request' && secrets.RPM_GPG_PASSPHRASE || '' }} - name: Upload RPMs as artifacts if: github.event_name != 'pull_request' From 052a9c56a18e4faf0356483899d5204b46735701 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 19 May 2026 20:49:10 -0700 Subject: [PATCH 35/43] Fail release builds without a signing key workflow-setup-packaging.sh accepts a new RELEASE flag; when set and GPG_PRIVATE_KEY is empty, the script exits 1 instead of publishing an empty key path. The workflow passes RELEASE=true on the same condition that gates GPG_PRIVATE_KEY (github.event_name != 'pull_request'), preventing silent ephemeral-key fallback on tag pushes / workflow_dispatch runs. --- .github/packaging/scripts/workflow-setup-packaging.sh | 7 +++++++ .github/workflows/build-rpm-release.yml | 1 + 2 files changed, 8 insertions(+) diff --git a/.github/packaging/scripts/workflow-setup-packaging.sh b/.github/packaging/scripts/workflow-setup-packaging.sh index 1b544273af8d..88c304664843 100755 --- a/.github/packaging/scripts/workflow-setup-packaging.sh +++ b/.github/packaging/scripts/workflow-setup-packaging.sh @@ -16,6 +16,9 @@ # GITHUB_OUTPUT - Path to step output file (CI only; omit for stdout) # TAG_INPUT - Explicit tag from workflow_dispatch (empty for auto-detect) # GPG_PRIVATE_KEY - GPG private key content (empty for unsigned PR builds) +# RELEASE - Non-empty marks the run as a release build; missing +# GPG_PRIVATE_KEY is then fatal (instead of falling back +# to ephemeral signing as for PR/local). set -euo pipefail @@ -39,6 +42,7 @@ echo "Resolved tag: ${TAG}" >&2 # ── Import GPG key ─────────────────────────────────────────────── GPG_PRIVATE_KEY="${GPG_PRIVATE_KEY:-}" +RELEASE="${RELEASE:-}" if [[ -n "${GPG_PRIVATE_KEY}" ]]; then GPG_KEY_FILE="$(mktemp)" @@ -46,6 +50,9 @@ if [[ -n "${GPG_PRIVATE_KEY}" ]]; then printf '%s' "${GPG_PRIVATE_KEY}" > "${GPG_KEY_FILE}" echo "gpg-key-file=${GPG_KEY_FILE}" >> "${OUTPUT}" echo "GPG key written to temporary file" >&2 +elif [[ -n "${RELEASE}" ]]; then + echo "ERROR: release build requires GPG_PRIVATE_KEY but none was provided" >&2 + exit 1 else echo "gpg-key-file=" >> "${OUTPUT}" fi diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index 5a4d422767f3..271a1130d677 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -67,6 +67,7 @@ jobs: env: TAG_INPUT: ${{ github.event.inputs.tag }} GPG_PRIVATE_KEY: ${{ github.event_name != 'pull_request' && secrets.RPM_GPG_PRIVATE_KEY || '' }} + RELEASE: ${{ github.event_name != 'pull_request' && 'true' || '' }} - name: Build and validate RPMs run: ./scripts/run_task.sh --taskfile .github/packaging/Taskfile.yml test-build-rpms From e0ce137191dba69738d855dd5e495c9377e5b27c Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 19 May 2026 21:04:50 -0700 Subject: [PATCH 36/43] Stop inlining GPG signing passphrase into docker run Pass GPG_KEY_PASSPHRASE through the task's env block and use Docker's value-less -e form. Whitespace and shell metachars in the passphrase no longer corrupt the command or expose secret content via argv. Applied to both RPM build tasks. --- .github/packaging/Taskfile.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/packaging/Taskfile.yml b/.github/packaging/Taskfile.yml index 68e45e97265a..43c332221039 100644 --- a/.github/packaging/Taskfile.yml +++ b/.github/packaging/Taskfile.yml @@ -59,6 +59,8 @@ tasks: vars: PACKAGE_ARCH: '{{.PACKAGE_ARCH | default .PACKAGING_HOST_ARCH}}' RPM_TAG: '{{.PACKAGING_TAG}}' + env: + GPG_KEY_PASSPHRASE: '{{.GPG_KEY_PASSPHRASE}}' deps: [build-builder-docker-image] cmds: - cmd: mkdir -p {{.PACKAGING_OUTPUT_DIR}} @@ -75,7 +77,7 @@ tasks: -e OUTPUT_DIR=/output -e AVALANCHEGO_COMMIT={{.PACKAGING_GIT_COMMIT}} {{if .GPG_KEY_FILE}}-e GPG_KEY_FILE={{.GPG_KEY_FILE}}{{end}} - {{if .GPG_KEY_PASSPHRASE}}-e GPG_KEY_PASSPHRASE={{.GPG_KEY_PASSPHRASE}}{{end}} + {{if .GPG_KEY_PASSPHRASE}}-e GPG_KEY_PASSPHRASE{{end}} {{.PACKAGING_DOCKER_IMAGE}} .github/packaging/scripts/build-package.sh @@ -84,6 +86,8 @@ tasks: vars: PACKAGE_ARCH: '{{.PACKAGE_ARCH | default .PACKAGING_HOST_ARCH}}' RPM_TAG: '{{.PACKAGING_TAG}}' + env: + GPG_KEY_PASSPHRASE: '{{.GPG_KEY_PASSPHRASE}}' deps: [build-builder-docker-image] cmds: - cmd: mkdir -p {{.PACKAGING_OUTPUT_DIR}} @@ -100,7 +104,7 @@ tasks: -e OUTPUT_DIR=/output -e AVALANCHEGO_COMMIT={{.PACKAGING_GIT_COMMIT}} {{if .GPG_KEY_FILE}}-e GPG_KEY_FILE={{.GPG_KEY_FILE}}{{end}} - {{if .GPG_KEY_PASSPHRASE}}-e GPG_KEY_PASSPHRASE={{.GPG_KEY_PASSPHRASE}}{{end}} + {{if .GPG_KEY_PASSPHRASE}}-e GPG_KEY_PASSPHRASE{{end}} {{.PACKAGING_DOCKER_IMAGE}} .github/packaging/scripts/build-package.sh From 3a4607f278add5a1c7622a1717c59b43e213c30b Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Tue, 19 May 2026 23:10:07 -0700 Subject: [PATCH 37/43] Discard sourced constants.sh stdout in resolve_subnet_evm_vm_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Source the file inside a {} command group whose stdout is sent to stderr; the explicit echo of ${DEFAULT_VM_ID} is the only thing the outer $() captures. Prevents the v1.14.1-style 'Using branch: …' line from being concatenated into SUBNET_EVM_VM_ID when overlaid packaging scripts process an older tag via workflow_dispatch. --- .github/packaging/scripts/lib-build-common.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh index b760e3c7193d..9b1268cd6da6 100644 --- a/.github/packaging/scripts/lib-build-common.sh +++ b/.github/packaging/scripts/lib-build-common.sh @@ -73,13 +73,13 @@ build_binary() { # Sets SUBNET_EVM_VM_ID (global) as a side effect. resolve_subnet_evm_vm_id() { SUBNET_EVM_VM_ID="$( - ( + { # shellcheck disable=SC1091 source "${REPO_ROOT}/graft/subnet-evm/scripts/constants.sh" # shellcheck disable=SC2154 : "${DEFAULT_VM_ID:?DEFAULT_VM_ID must be set by constants.sh}" - echo "${DEFAULT_VM_ID}" - ) + } >&2 + echo "${DEFAULT_VM_ID}" )" export SUBNET_EVM_VM_ID } From c85716d418f5902cc5f560eb5f4bf3b85574083f Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Fri, 22 May 2026 00:08:19 -0700 Subject: [PATCH 38/43] Drop PKG_FORMAT default in build-package.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every caller (Taskfile RPM tasks here, DEB tasks in #5180) passes PKG_FORMAT explicitly. Replace the RPM default with a `${VAR:?…}` assert so the script fails fast on misuse, mirroring the DOCKERFILE treatment in 37f63aac92. Addresses https://github.com/ava-labs/avalanchego/pull/5179/changes#r3266314592 --- .github/packaging/scripts/build-package.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/packaging/scripts/build-package.sh b/.github/packaging/scripts/build-package.sh index 1166a1a0f570..37c0f6123b01 100755 --- a/.github/packaging/scripts/build-package.sh +++ b/.github/packaging/scripts/build-package.sh @@ -10,9 +10,7 @@ set -euo pipefail : "${PACKAGE_ARCH:?PACKAGE_ARCH must be set (x86_64 or aarch64)}" : "${OUTPUT_DIR:?OUTPUT_DIR must be set (bind-mounted output dir)}" -# Package format identifier; defaults to RPM and is retained for -# future (DEB and possibly other) packaging formats reuse. -PKG_FORMAT="${PKG_FORMAT:-RPM}" +: "${PKG_FORMAT:?PKG_FORMAT must be set (RPM or DEB)}" pkg_format_lower="${PKG_FORMAT,,}" REPO_ROOT="/build" From 74c67a4e8cf742491f08f87bee6f0af5726732af Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Fri, 22 May 2026 00:10:15 -0700 Subject: [PATCH 39/43] Fold lib-validate-common.sh into lib-build-common.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After removing detect_host_arch in ea5364ab03, lib-validate-common.sh held a single assert_files_exist function — not enough of a boundary to justify a separate file. Move it into lib-build-common.sh (which the validator already sources) and drop the now-unused file plus its second source line in validate-rpm.sh. Also document the cross-container key-sharing constraint that motivates the ephemeral GPG key reuse branch in setup_gpg: the two RPM builds run as separate docker invocations and share the bind- mounted key so the validator's single exported public key verifies both packages. Addresses - https://github.com/ava-labs/avalanchego/pull/5179/changes#r3276193371 - https://github.com/ava-labs/avalanchego/pull/5179/changes#r3267417392 --- .github/packaging/scripts/lib-build-common.sh | 18 ++++++++++++++++++ .../packaging/scripts/lib-validate-common.sh | 17 ----------------- .github/packaging/scripts/validate-rpm.sh | 2 -- 3 files changed, 18 insertions(+), 19 deletions(-) delete mode 100755 .github/packaging/scripts/lib-validate-common.sh diff --git a/.github/packaging/scripts/lib-build-common.sh b/.github/packaging/scripts/lib-build-common.sh index 9b1268cd6da6..0242e7925afd 100644 --- a/.github/packaging/scripts/lib-build-common.sh +++ b/.github/packaging/scripts/lib-build-common.sh @@ -128,6 +128,9 @@ setup_gpg() { gpg --batch --import "${gpg_key_file}" cp "${gpg_key_file}" "${NFPM_SIGNING_KEY}" elif [[ -f "${NFPM_SIGNING_KEY}" ]]; then + # avalanchego and subnet-evm builds run in separate docker invocations + # but share the on-disk key via the bind-mounted build/gpg directory so + # the validator's single exported public key verifies both RPMs. echo "Reusing existing ephemeral GPG key" gpg --batch --import "${NFPM_SIGNING_KEY}" else @@ -176,3 +179,18 @@ run_nfpm_package() { --packager "${packager}" \ --target "${target_path}" } + +# Verify that all expected package files exist in a directory. +# +# Args: pkg_dir file1 [file2 ...] +assert_files_exist() { + local pkg_dir="${1:?package directory required}"; shift + local missing=() + for f in "$@"; do + [[ -f "${pkg_dir}/${f}" ]] || missing+=("${pkg_dir}/${f}") + done + if (( ${#missing[@]} > 0 )); then + printf 'ERROR: expected file not found: %s\n' "${missing[@]}" >&2 + exit 1 + fi +} diff --git a/.github/packaging/scripts/lib-validate-common.sh b/.github/packaging/scripts/lib-validate-common.sh deleted file mode 100755 index cbb57221b790..000000000000 --- a/.github/packaging/scripts/lib-validate-common.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -# Shared functions for package validation scripts. - -# Verify that all expected package files exist in a directory. -# -# Args: pkg_dir file1 [file2 ...] -assert_files_exist() { - local pkg_dir="${1:?package directory required}"; shift - local missing=() - for f in "$@"; do - [[ -f "${pkg_dir}/${f}" ]] || missing+=("${pkg_dir}/${f}") - done - if (( ${#missing[@]} > 0 )); then - printf 'ERROR: expected file not found: %s\n' "${missing[@]}" >&2 - exit 1 - fi -} diff --git a/.github/packaging/scripts/validate-rpm.sh b/.github/packaging/scripts/validate-rpm.sh index 4ade3ca808fc..ee6be9aaa49b 100755 --- a/.github/packaging/scripts/validate-rpm.sh +++ b/.github/packaging/scripts/validate-rpm.sh @@ -24,8 +24,6 @@ SCRIPTS_DIR="${REPO_ROOT}/.github/packaging/scripts" # shellcheck disable=SC1091 source "${SCRIPTS_DIR}/lib-build-common.sh" -# shellcheck disable=SC1091 -source "${SCRIPTS_DIR}/lib-validate-common.sh" resolve_subnet_evm_vm_id From bd1320f156ed3df16cbeb124baffba85fd91460d Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Fri, 22 May 2026 00:12:01 -0700 Subject: [PATCH 40/43] Tighten validate-rpm.sh: fatal on missing key + inline asserts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Convert the "Skipping GPG verification" else branch to a fatal error. The current pipeline always produces a signed key (ephemeral for PR/local, provided for releases per the release-key gate in dec7b29f75), so a missing GPG-KEY-avalanchego is now a real anomaly. - Drop the "Required/Optional env vars" header block. The ${VAR:?…} assert messages now carry per-var role descriptions inline. This also removes the stale "PACKAGE_ARCH defaults to host" claim (PACKAGE_ARCH was promoted to required in ea5364ab03). Addresses - https://github.com/ava-labs/avalanchego/pull/5179/changes#r3276233720 - https://github.com/ava-labs/avalanchego/pull/5179/changes#r3276331218 - https://github.com/ava-labs/avalanchego/pull/5179/changes#r3276336503 --- .github/packaging/scripts/validate-rpm.sh | 25 ++++++++--------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/.github/packaging/scripts/validate-rpm.sh b/.github/packaging/scripts/validate-rpm.sh index ee6be9aaa49b..36bc2d17ad60 100755 --- a/.github/packaging/scripts/validate-rpm.sh +++ b/.github/packaging/scripts/validate-rpm.sh @@ -4,18 +4,11 @@ # # Validates locally-built RPMs by running a fresh rockylinux:9 # container to verify signature, install, and smoke test. -# -# Required env vars: -# TAG - Git tag (e.g., "v1.14.1") -# GIT_COMMIT - Full git commit hash used to build the binaries -# -# Optional env vars: -# PACKAGE_ARCH - RPM architecture ("x86_64" or "aarch64"), defaults to host set -euo pipefail -: "${TAG:?TAG must be set}" -: "${GIT_COMMIT:?GIT_COMMIT must be set}" +: "${TAG:?TAG must be set (Git tag, e.g. v1.14.1)}" +: "${GIT_COMMIT:?GIT_COMMIT must be set (full git commit hash used to build the binaries)}" : "${PACKAGE_ARCH:?PACKAGE_ARCH must be set (x86_64 or aarch64)}" REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" @@ -37,14 +30,14 @@ docker run --rm \ -v "${SCRIPTS_DIR}/smoke-test.sh:/smoke-test.sh:ro" \ rockylinux:9 \ bash -euxc ' - # Import GPG key and verify signatures if available - if [[ -f /rpms/GPG-KEY-avalanchego ]]; then - rpm --import /rpms/GPG-KEY-avalanchego - rpm -K "/rpms/avalanchego-'"${TAG}"'-'"${PACKAGE_ARCH}"'.rpm" - rpm -K "/rpms/subnet-evm-'"${TAG}"'-'"${PACKAGE_ARCH}"'.rpm" - else - echo "Skipping GPG verification (unsigned build)" + # Import GPG key and verify signatures (always produced by the build). + if [[ ! -f /rpms/GPG-KEY-avalanchego ]]; then + echo "ERROR: GPG-KEY-avalanchego not found; build did not export a key" >&2 + exit 1 fi + rpm --import /rpms/GPG-KEY-avalanchego + rpm -K "/rpms/avalanchego-'"${TAG}"'-'"${PACKAGE_ARCH}"'.rpm" + rpm -K "/rpms/subnet-evm-'"${TAG}"'-'"${PACKAGE_ARCH}"'.rpm" # Install both packages rpm -ivh "/rpms/avalanchego-'"${TAG}"'-'"${PACKAGE_ARCH}"'.rpm" From ef16400c00222da5ee3d65b749101012fbd463f6 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Fri, 22 May 2026 00:13:15 -0700 Subject: [PATCH 41/43] Inline env-var asserts in workflow-setup-packaging.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `${GITHUB_REF:?…}` / `${GITHUB_SHA:?…}` asserts so the script fails fast when invoked outside GitHub Actions without supplying them, mirroring the build-package.sh pattern from 43de5249ff. Drop the duplicate "Required/Optional env vars" header block; the optional vars (GITHUB_OUTPUT, TAG_INPUT, GPG_PRIVATE_KEY, RELEASE) keep a brief inline annotation above their ${VAR:-…} defaults. Addresses https://github.com/ava-labs/avalanchego/pull/5179/changes#r3276254348 --- .../scripts/workflow-setup-packaging.sh | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/.github/packaging/scripts/workflow-setup-packaging.sh b/.github/packaging/scripts/workflow-setup-packaging.sh index 88c304664843..c7becef4efa3 100755 --- a/.github/packaging/scripts/workflow-setup-packaging.sh +++ b/.github/packaging/scripts/workflow-setup-packaging.sh @@ -7,21 +7,18 @@ # # Writes key=value outputs to $GITHUB_OUTPUT when running in CI, or to # stdout when running locally (for inspection / testing). -# -# Required env vars: -# GITHUB_REF - Git ref (set by GitHub Actions; locally: any ref string) -# GITHUB_SHA - Git commit SHA (set by GitHub Actions; locally: any SHA) -# -# Optional env vars: -# GITHUB_OUTPUT - Path to step output file (CI only; omit for stdout) -# TAG_INPUT - Explicit tag from workflow_dispatch (empty for auto-detect) -# GPG_PRIVATE_KEY - GPG private key content (empty for unsigned PR builds) -# RELEASE - Non-empty marks the run as a release build; missing -# GPG_PRIVATE_KEY is then fatal (instead of falling back -# to ephemeral signing as for PR/local). set -euo pipefail +: "${GITHUB_REF:?GITHUB_REF must be set (Git ref, e.g. refs/tags/v1.14.1; locally: any ref string)}" +: "${GITHUB_SHA:?GITHUB_SHA must be set (Git commit SHA; locally: any SHA)}" + +# Optional env vars (defaulted below): +# GITHUB_OUTPUT - step-output file path; empty/unset writes to stdout +# TAG_INPUT - explicit tag from workflow_dispatch (empty: auto-detect) +# GPG_PRIVATE_KEY - signing key content (empty: ephemeral PR/local signing) +# RELEASE - non-empty marks a release build (missing key is then +# fatal instead of falling back to ephemeral signing) OUTPUT="${GITHUB_OUTPUT:-/dev/stdout}" # ── Resolve tag ────────────────────────────────────────────────── From a93995931ff752d63c3db762ca358725f808c502 Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Fri, 22 May 2026 00:13:53 -0700 Subject: [PATCH 42/43] Clarify overlay-step semantics + drop redundant mkdir - Reword the comment block to make the replacement semantics explicit: the tag's .github/packaging tree (which may be stale or absent) is wholesale replaced with the workflow branch's version before running task test-build-rpms. - Remove `mkdir -p .github`. The directory is guaranteed to exist in the checked-out tree (the workflow yaml itself lives at .github/workflows/build-rpm-release.yml), and rm -rf .github/packaging removes only the subdir. Addresses - https://github.com/ava-labs/avalanchego/pull/5179/changes#r3276034324 - https://github.com/ava-labs/avalanchego/pull/5179/changes#r3276081744 --- .github/workflows/build-rpm-release.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-rpm-release.yml b/.github/workflows/build-rpm-release.yml index 271a1130d677..ee52a6be11a5 100644 --- a/.github/workflows/build-rpm-release.yml +++ b/.github/workflows/build-rpm-release.yml @@ -37,11 +37,13 @@ jobs: with: ref: ${{ github.event.inputs.tag || github.ref }} - # When building an older tag via workflow_dispatch, the tag's tree - # may not contain the packaging scripts. Overlay them from the - # workflow branch so that packaging:test-build-rpms is available. - # Split into two steps because actions/checkout and the copy - # cannot be combined. + # When building an older tag via workflow_dispatch, the tag's + # .github/packaging tree may be stale or absent. Replace it + # wholesale with the workflow branch's version so that the build + # uses the current packaging:test-build-rpms task and scripts. + # Split into two steps because actions/checkout (which provides + # the master-side tree) and the cp (which applies it) cannot be + # combined. - name: Overlay packaging scripts from workflow branch if: github.event.inputs.tag uses: actions/checkout@v5 @@ -53,7 +55,6 @@ jobs: if: github.event.inputs.tag run: | rm -rf .github/packaging - mkdir -p .github cp -r .packaging-overlay/.github/packaging .github/packaging rm -rf .packaging-overlay shell: bash From 5f653ff759628c7865aa15bd1b5707d1fb50c93c Mon Sep 17 00:00:00 2001 From: Platform Core <240789909+PlatCore@users.noreply.github.com> Date: Fri, 22 May 2026 00:15:44 -0700 Subject: [PATCH 43/43] Simplify smoke-test.sh contract: drop Args header + take plugin path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drop the duplicate `# Args:` header docstring. The ${N:?…} positional-arg asserts now carry per-arg roles inline, matching the pattern from build-package.sh in 43de5249ff. - Shrink the arg surface from 4 to 3: caller passes the composed subnet-evm plugin binary path directly. The smoke test no longer needs to know how to join PLUGIN_DIR with SUBNET_EVM_VM_ID, and validate-rpm.sh (which already resolves the VM ID) composes the path inline. Addresses - https://github.com/ava-labs/avalanchego/pull/5179/changes#r3276183928 - https://github.com/ava-labs/avalanchego/pull/5179/changes#r3276202809 --- .github/packaging/scripts/smoke-test.sh | 18 +++++------------- .github/packaging/scripts/validate-rpm.sh | 5 ++--- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/.github/packaging/scripts/smoke-test.sh b/.github/packaging/scripts/smoke-test.sh index e65977b21a7e..bcb69db5361d 100755 --- a/.github/packaging/scripts/smoke-test.sh +++ b/.github/packaging/scripts/smoke-test.sh @@ -5,19 +5,12 @@ # Runs inside a validation container after packages have been installed. # Verifies that binaries are present, executable, and report the expected # version/commit information. -# -# Args: -# $1 - path to avalanchego binary -# $2 - plugin directory path (subnet-evm VM ID appended by this script) -# $3 - expected full git commit hash -# $4 - subnet-evm VM ID set -euxo pipefail AVAGO_BIN="${1:?avalanchego binary path required}" -PLUGIN_DIR="${2:?plugin directory path required}" -GIT_COMMIT="${3:?git commit hash required}" -SUBNET_EVM_VM_ID="${4:?subnet-evm VM ID required}" +SUBNET_EVM_BIN="${2:?subnet-evm plugin binary path required}" +GIT_COMMIT="${3:?expected full git commit hash required}" # ── Smoke test avalanchego ──────────────────────────────────────── @@ -35,13 +28,12 @@ fi # ── Verify subnet-evm plugin ───────────────────────────────────── -plugin="${PLUGIN_DIR}/${SUBNET_EVM_VM_ID}" -if [[ ! -x "${plugin}" ]]; then - echo "ERROR: subnet-evm plugin not found or not executable at ${plugin}" >&2 +if [[ ! -x "${SUBNET_EVM_BIN}" ]]; then + echo "ERROR: subnet-evm plugin not found or not executable at ${SUBNET_EVM_BIN}" >&2 exit 1 fi -evm_output=$("${plugin}" --version) +evm_output=$("${SUBNET_EVM_BIN}" --version) echo "subnet-evm --version: ${evm_output}" if [[ "${evm_output}" != *"${GIT_COMMIT}"* ]]; then echo "ERROR: subnet-evm --version output does not contain expected commit ${GIT_COMMIT}" >&2 diff --git a/.github/packaging/scripts/validate-rpm.sh b/.github/packaging/scripts/validate-rpm.sh index 36bc2d17ad60..8b5a1dd30384 100755 --- a/.github/packaging/scripts/validate-rpm.sh +++ b/.github/packaging/scripts/validate-rpm.sh @@ -46,9 +46,8 @@ docker run --rm \ # Run shared smoke test bash /smoke-test.sh \ /var/opt/avalanchego/bin/avalanchego \ - /var/opt/avalanchego/plugins \ - "'"${GIT_COMMIT}"'" \ - "'"${SUBNET_EVM_VM_ID}"'" + "/var/opt/avalanchego/plugins/'"${SUBNET_EVM_VM_ID}"'" \ + "'"${GIT_COMMIT}"'" ' echo "=== RPM validation complete ==="