| #!/usr/bin/env bash | |
| set -o errexit -o nounset -o pipefail | |
| # --- begin runfiles.bash initialization --- | |
| if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then | |
| if [[ -f "$0.runfiles_manifest" ]]; then | |
| export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest" | |
| elif [[ -f "$0.runfiles/MANIFEST" ]]; then | |
| export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST" | |
| elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then | |
| export RUNFILES_DIR="$0.runfiles" | |
| fi | |
| fi | |
| if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then | |
| source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash" | |
| elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then | |
| source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \ | |
| "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)" | |
| else | |
| # Not running under Bazel; fall back to co-located script | |
| rlocation() { echo "$(cd "$(dirname "$0")" && pwd)/$(basename "$1")"; } | |
| fi | |
| # --- end runfiles.bash initialization --- | |
| RELEASE_PREP=$(rlocation _main/.github/workflows/release_prep.sh) | |
| TEST_DIR=$(mktemp -d) | |
| trap 'rm -rf "$TEST_DIR"' EXIT | |
| TAG="v99.0" | |
| PREFIX="protobuf-99.0" | |
| ############################## | |
| # Fixture: a git repo with a tag and the placeholder integrity file | |
| ############################## | |
| cd "$TEST_DIR" | |
| git init -q | |
| git config user.email "test@test.com" | |
| git config user.name "Test" | |
| mkdir -p bazel/private/oss/toolchains/prebuilt | |
| cat > bazel/private/oss/toolchains/prebuilt/tool_integrity.bzl <<'BZL' | |
| "Placeholder" | |
| RELEASE_VERSION = "v0.0.0" | |
| RELEASED_BINARY_INTEGRITY = {} | |
| BZL | |
| echo "compatibility/ export-ignore" > .gitattributes | |
| mkdir -p compatibility | |
| echo "should be excluded" > compatibility/README | |
| echo "# protobuf" > README.md | |
| git add -A | |
| git commit -q -m "initial" | |
| git tag "$TAG" | |
| ############################## | |
| # Fixture: the script needs GNU tar (--delete/--append); shim it | |
| ############################## | |
| TAR=$(command -v gtar || command -v tar) | |
| mkdir -p "$TEST_DIR/.mock_bin" | |
| ln -sf "$TAR" "$TEST_DIR/.mock_bin/tar" | |
| ############################## | |
| # Fixture: put jq (from Bazel toolchain) on the PATH | |
| ############################## | |
| ln -sf "$(rlocation ${JQ_BIN#"external/"})" "$TEST_DIR/.mock_bin/jq" | |
| ############################## | |
| # Fixture: mock curl returning GitHub Releases API response & handling asset downloads | |
| # Handles two cases: | |
| # 1) When -o flag is provided, mocks downloading the integrity file .bzl, | |
| # writing to path specified by the flag | |
| # 2) Otherwise, mocks retrieving the GitHub Releases API JSON, writing to stdout | |
| ############################## | |
| cat > "$TEST_DIR/.mock_bin/curl" <<'MOCK' | |
| #!/usr/bin/env bash | |
| outfile="" | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -o) | |
| outfile="$2" | |
| shift 2 | |
| ;; | |
| *) | |
| shift | |
| ;; | |
| esac | |
| done | |
| if [[ -n "$outfile" ]]; then | |
| cat <<'BZL' > "$outfile" | |
| RELEASE_VERSION="v99.0" | |
| RELEASED_BINARY_INTEGRITY = { | |
| "protoc-99.0-linux-x86_64.zip": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", | |
| "protoc-99.0-osx-aarch_64.zip": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", | |
| "protoc-99.0-win64.zip": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", | |
| } | |
| BZL | |
| else | |
| cat <<'JSON' | |
| { | |
| "assets": [ | |
| { | |
| "name": "protoc-99.0-linux-x86_64.zip", | |
| "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" | |
| }, | |
| { | |
| "name": "protoc-99.0-osx-aarch_64.zip", | |
| "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" | |
| }, | |
| { | |
| "name": "protoc-99.0-win64.zip", | |
| "digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" | |
| }, | |
| { | |
| "name": "tool_integrity.bzl", | |
| "browser_download_url": "https://github.com/protocolbuffers/protobuf/releases/download/v99.0/tool_integrity.bzl" | |
| } | |
| ] | |
| } | |
| JSON | |
| fi | |
| MOCK | |
| chmod +x "$TEST_DIR/.mock_bin/curl" | |
| export PATH="$TEST_DIR/.mock_bin:$PATH" | |
| ############################## | |
| # Run the script under test | |
| ############################## | |
| bash "$RELEASE_PREP" "$TAG" | |
| ############################## | |
| # Assertions | |
| ############################## | |
| ARCHIVE="$PREFIX.bazel.tar.gz" | |
| FAILURES=0 | |
| fail() { | |
| echo "FAIL: $1" | |
| FAILURES=$((FAILURES + 1)) | |
| } | |
| pass() { | |
| echo "PASS: $1" | |
| } | |
| assert_file_exists() { | |
| [[ -f "$1" ]] && pass "$2" || fail "$2" | |
| } | |
| assert_file_absent() { | |
| [[ ! -e "$1" ]] && pass "$2" || fail "$2" | |
| } | |
| assert_contains() { | |
| if echo "$3" | grep -qF -- "$2"; then | |
| pass "$1" | |
| else | |
| fail "$1 — expected to find: $2" | |
| fi | |
| } | |
| # 1. Archive is produced with the expected name | |
| assert_file_exists "$ARCHIVE" "archive file $ARCHIVE exists" | |
| # 2. Archive is gzip-compressed | |
| if file "$ARCHIVE" | grep -q gzip; then | |
| pass "archive is gzip" | |
| else | |
| fail "archive is gzip" | |
| fi | |
| # 3. Extract and inspect | |
| EXTRACT_DIR=$(mktemp -d) | |
| tar xzf "$ARCHIVE" -C "$EXTRACT_DIR" | |
| # 4. Patched tool_integrity.bzl is present | |
| INTEGRITY="$EXTRACT_DIR/$PREFIX/bazel/private/oss/toolchains/prebuilt/tool_integrity.bzl" | |
| assert_file_exists "$INTEGRITY" "tool_integrity.bzl present in archive" | |
| CONTENT=$(cat "$INTEGRITY") | |
| # 5. RELEASE_VERSION matches the tag | |
| assert_contains "RELEASE_VERSION matches tag" "RELEASE_VERSION=\"$TAG\"" "$CONTENT" | |
| # 6. RELEASED_BINARY_INTEGRITY is populated from mock curl | |
| assert_contains "integrity map present" "RELEASED_BINARY_INTEGRITY =" "$CONTENT" | |
| assert_contains "linux hash" \ | |
| '"protoc-99.0-linux-x86_64.zip": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"' \ | |
| "$CONTENT" | |
| assert_contains "osx hash" \ | |
| '"protoc-99.0-osx-aarch_64.zip": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"' \ | |
| "$CONTENT" | |
| assert_contains "win hash" \ | |
| '"protoc-99.0-win64.zip": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"' \ | |
| "$CONTENT" | |
| # 7. Original placeholder content is NOT in the patched file | |
| if echo "$CONTENT" | grep -qF "v0.0.0"; then | |
| fail "placeholder content should be replaced" | |
| else | |
| pass "placeholder content replaced" | |
| fi | |
| # 8. compatibility/ excluded from archive (via .gitattributes export-ignore) | |
| assert_file_absent "$EXTRACT_DIR/$PREFIX/compatibility" "compatibility/ excluded from archive" | |
| # 9. Other repo files are included | |
| assert_file_exists "$EXTRACT_DIR/$PREFIX/README.md" "README.md included in archive" | |
| # 10. All archive entries live under the expected prefix directory | |
| BAD_ENTRIES=$(tar tzf "$ARCHIVE" | grep -cv "^${PREFIX}/") || true | |
| if [[ "$BAD_ENTRIES" -eq 0 ]]; then | |
| pass "all entries under $PREFIX/ prefix" | |
| else | |
| fail "found $BAD_ENTRIES entries outside $PREFIX/ prefix" | |
| fi | |
| rm -rf "$EXTRACT_DIR" | |
| ############################## | |
| echo | |
| if [[ "$FAILURES" -gt 0 ]]; then | |
| echo "$FAILURES test(s) FAILED" | |
| exit 1 | |
| else | |
| echo "All tests passed" | |
| fi |