|
| 1 | +#!/usr/bin/env bash |
| 2 | + |
| 3 | +set -euo pipefail |
| 4 | + |
| 5 | +repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) |
| 6 | +publisher="${repo_root}/scripts/publish-sprig-rolling-release.sh" |
| 7 | +sprig_workflow="${repo_root}/.github/workflows/sprig.yml" |
| 8 | +ci_workflow="${repo_root}/.github/workflows/ci.yml" |
| 9 | + |
| 10 | +fail() { |
| 11 | + echo "sprig rolling release contract failed: $*" >&2 |
| 12 | + exit 1 |
| 13 | +} |
| 14 | + |
| 15 | +[[ -x "${publisher}" ]] || fail "missing executable ${publisher}" |
| 16 | +grep -F -q 'scripts/publish-sprig-rolling-release.sh' "${sprig_workflow}" \ |
| 17 | + || fail "Sprig workflow must invoke the checked-in publisher" |
| 18 | +grep -F -q 'scripts/test-publish-sprig-rolling-release.sh' "${ci_workflow}" \ |
| 19 | + || fail "CI must execute the Sprig rolling release contract" |
| 20 | + |
| 21 | +tmp=$(mktemp -d) |
| 22 | +trap 'rm -rf "${tmp}"' EXIT |
| 23 | +mkdir -p "${tmp}/bin" "${tmp}/dist" |
| 24 | +touch \ |
| 25 | + "${tmp}/dist/sprig-aarch64-unknown-linux-musl.tar.gz" \ |
| 26 | + "${tmp}/dist/sprig-aarch64-unknown-linux-musl.tar.gz.sha256" \ |
| 27 | + "${tmp}/dist/sprig-x86_64-unknown-linux-musl.tar.gz" \ |
| 28 | + "${tmp}/dist/sprig-x86_64-unknown-linux-musl.tar.gz.sha256" |
| 29 | + |
| 30 | +cat >"${tmp}/bin/gh" <<'EOF' |
| 31 | +#!/usr/bin/env bash |
| 32 | +
|
| 33 | +set -euo pipefail |
| 34 | +
|
| 35 | +command_name=${1-} |
| 36 | +shift || true |
| 37 | +{ |
| 38 | + printf '%s' "${command_name}" |
| 39 | + for argument in "$@"; do |
| 40 | + printf '\t%s' "${argument}" |
| 41 | + done |
| 42 | + printf '\n' |
| 43 | +} >>"${GH_LOG:?}" |
| 44 | +
|
| 45 | +case "${command_name}" in |
| 46 | + api) |
| 47 | + case "${GH_API_MODE:?}" in |
| 48 | + existing) |
| 49 | + printf 'HTTP/2.0 200 OK\n\n{"tag_name":"sprig-latest"}\n' |
| 50 | + ;; |
| 51 | + missing) |
| 52 | + printf 'HTTP/2.0 404 Not Found\n\n{"message":"Not Found"}\n' |
| 53 | + exit 1 |
| 54 | + ;; |
| 55 | + error) |
| 56 | + printf 'HTTP/2.0 500 Internal Server Error\n\n{"message":"failure"}\n' |
| 57 | + exit 1 |
| 58 | + ;; |
| 59 | + *) |
| 60 | + echo "unexpected GH_API_MODE=${GH_API_MODE}" >&2 |
| 61 | + exit 2 |
| 62 | + ;; |
| 63 | + esac |
| 64 | + ;; |
| 65 | + release) |
| 66 | + exit "${GH_RELEASE_STATUS:-0}" |
| 67 | + ;; |
| 68 | + *) |
| 69 | + echo "unexpected gh command: ${command_name}" >&2 |
| 70 | + exit 2 |
| 71 | + ;; |
| 72 | +esac |
| 73 | +EOF |
| 74 | +chmod +x "${tmp}/bin/gh" |
| 75 | + |
| 76 | +run_publisher() { |
| 77 | + local mode=$1 |
| 78 | + local log=$2 |
| 79 | + GH_API_MODE="${mode}" \ |
| 80 | + GH_LOG="${log}" \ |
| 81 | + GITHUB_REPOSITORY='BrianInAz/buzz' \ |
| 82 | + GITHUB_SHA='0123456789abcdef' \ |
| 83 | + PATH="${tmp}/bin:${PATH}" \ |
| 84 | + SPRIG_DIST_DIR="${tmp}/dist" \ |
| 85 | + "${publisher}" |
| 86 | +} |
| 87 | + |
| 88 | +missing_log="${tmp}/missing.log" |
| 89 | +run_publisher missing "${missing_log}" |
| 90 | +grep -F -q $'api\t--include\trepos/BrianInAz/buzz/releases/tags/sprig-latest' "${missing_log}" \ |
| 91 | + || fail "missing-release path must query the exact tag" |
| 92 | +grep -F -q $'release\tcreate\tsprig-latest' "${missing_log}" \ |
| 93 | + || fail "missing-release path must create sprig-latest" |
| 94 | +grep -F -q $'\t--prerelease\t--target\t0123456789abcdef' "${missing_log}" \ |
| 95 | + || fail "create must publish a prerelease at the triggering SHA" |
| 96 | +grep -F -q $'\t--repo\tBrianInAz/buzz' "${missing_log}" \ |
| 97 | + || fail "create must target the triggering repository explicitly" |
| 98 | +if grep -F -q $'release\tedit\t' "${missing_log}" \ |
| 99 | + || grep -F -q $'release\tupload\t' "${missing_log}"; then |
| 100 | + fail "missing-release path must not edit or separately upload" |
| 101 | +fi |
| 102 | + |
| 103 | +existing_log="${tmp}/existing.log" |
| 104 | +run_publisher existing "${existing_log}" |
| 105 | +grep -F -q $'release\tedit\tsprig-latest' "${existing_log}" \ |
| 106 | + || fail "existing-release path must edit sprig-latest" |
| 107 | +grep -F -q $'\t--prerelease\t--target\t0123456789abcdef' "${existing_log}" \ |
| 108 | + || fail "edit must retarget the prerelease to the triggering SHA" |
| 109 | +grep -F -q $'release\tupload\tsprig-latest' "${existing_log}" \ |
| 110 | + || fail "existing-release path must replace rolling assets" |
| 111 | +grep -F -q $'\t--clobber\t--repo\tBrianInAz/buzz' "${existing_log}" \ |
| 112 | + || fail "asset replacement must be explicit and repository-scoped" |
| 113 | +if grep -F -q $'release\tcreate\t' "${existing_log}"; then |
| 114 | + fail "existing-release path must not create a duplicate release" |
| 115 | +fi |
| 116 | + |
| 117 | +error_log="${tmp}/error.log" |
| 118 | +if run_publisher error "${error_log}" >"${tmp}/error.out" 2>&1; then |
| 119 | + fail "non-404 API failure must stop publication" |
| 120 | +fi |
| 121 | +grep -F -q '500 Internal Server Error' "${tmp}/error.out" \ |
| 122 | + || fail "non-404 API failure must remain visible" |
| 123 | +if grep -F -q $'release\t' "${error_log}"; then |
| 124 | + fail "non-404 API failure must not attempt release mutation" |
| 125 | +fi |
| 126 | + |
| 127 | +release_error_log="${tmp}/release-error.log" |
| 128 | +if GH_RELEASE_STATUS=23 run_publisher missing "${release_error_log}" >/dev/null 2>&1; then |
| 129 | + fail "release command failure must remain fatal" |
| 130 | +fi |
| 131 | + |
| 132 | +echo "sprig rolling release contract passed" |
0 commit comments