#!/bin/bash # Apply the s3-gateway patch series for the target versitygw tag onto # the checked-out source tree (run ci/clone-source.sh first). # # Patch sets live in patches//. The directory for a given tag # comes from ci/support-matrix.yaml (the `patches:` field, defaulting # to the tag name). Patches are git-format-patch files applied with # `git am --3way` in filename order — same model as scoutfs-notify. # # The matrix is an ALLOWLIST: a tag that is not listed is refused, so a # stray tag push or a mistyped dispatch cannot publish a build of a # superseded (and possibly vulnerable) upstream release. Superseded # series stay on disk under patches/archive// and can be rebuilt # deliberately by passing PATCH_DIR. # # Environment: # VGW_SRC_DIR source tree (default: src; set by clone-source.sh) # VGW_TAG upstream tag being built (required) # PATCH_DIR build this directory instead of consulting the matrix; # the escape hatch for patches/archive/ (optional) set -euo pipefail VGW_SRC_DIR="${VGW_SRC_DIR:-src}" : "${VGW_TAG:?VGW_TAG must be set}" REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SRC="$(cd "${VGW_SRC_DIR}" && pwd)" MATRIX="${REPO_ROOT}/ci/support-matrix.yaml" # Emit " " for every row of the support matrix. Prefer # yq, but fall back to a small awk parse of the fixed shape this repo # writes: the stock golang CI container ships no yq, and the matrix is # the allowlist, so it has to be readable either way. matrix_entries() { if command -v yq >/dev/null 2>&1; then yq -r '.versitygw[] | "\(.tag) \(.patches // .tag)"' "${MATRIX}" 2>/dev/null && return fi sed 's/#.*//' "${MATRIX}" | awk ' /^[[:space:]]*-[[:space:]]*tag:[[:space:]]*[^[:space:]]/ { if (tag != "") print tag, (pat != "" ? pat : tag) tag = $NF; pat = ""; next } /^[[:space:]]*patches:[[:space:]]*[^[:space:]]/ { if (tag != "") pat = $NF } END { if (tag != "") print tag, (pat != "" ? pat : tag) } ' } # An explicit PATCH_DIR is a deliberate override (rebuilding something # out of patches/archive/). Otherwise the matrix decides, and a tag it # does not list is refused — never silently built as vanilla upstream. if [[ -z "${PATCH_DIR:-}" ]]; then PATCH_NAME="$(matrix_entries | awk -v t="${VGW_TAG}" '$1 == t { print $2; exit }')" if [[ -z "${PATCH_NAME}" ]]; then { echo "ERROR: ${VGW_TAG} is not listed in ci/support-matrix.yaml." echo " Supported: $(matrix_entries | awk '{print $1}' | tr '\n' ' ')" echo " Superseded series are archived under patches/archive/ and" echo " are deliberately not rebuilt — they predate the fixes in" echo " the supported release. To build one anyway, pass it" echo " explicitly:" echo " PATCH_DIR=patches/archive/${VGW_TAG} bash ci/apply-patches.sh" } >&2 exit 1 fi PATCH_DIR="${REPO_ROOT}/patches/${PATCH_NAME}" fi # git am runs from inside the source tree, so the patch paths have to be # absolute: a relative PATCH_DIR (the documented archive invocation is # one) would still glob here but no longer resolve after the cd below. if [[ ! -d "${PATCH_DIR}" ]]; then echo "ERROR: patch directory ${PATCH_DIR} does not exist." >&2 exit 1 fi PATCH_DIR="$(cd "${PATCH_DIR}" && pwd)" echo ">>> target versitygw tag: ${VGW_TAG}" echo ">>> patch directory: ${PATCH_DIR}" shopt -s nullglob patches=( "${PATCH_DIR}"/*.patch ) shopt -u nullglob if (( ${#patches[@]} == 0 )); then echo "ERROR: no *.patch files in ${PATCH_DIR}." >&2 echo " Refusing to continue — that would publish an un-de-branded" >&2 echo " vanilla upstream build under this project's release name." >&2 exit 1 fi echo ">>> applying ${#patches[@]} patch(es) with git am --3way:" printf ' %s\n' "${patches[@]##*/}" cd "${SRC}" git am --whitespace=nowarn --3way "${patches[@]}" echo ">>> applied commits:" git log --oneline "${VGW_TAG}..HEAD" if [[ -n "${GITHUB_ENV:-}" ]]; then { echo "S3GW_PATCH_DIR=${PATCH_DIR}" echo "S3GW_PATCH_COUNT=${#patches[@]}" } >> "${GITHUB_ENV}" fi echo ">>> patches applied"