v29.4-roots.4 · Commit 7: [PATCH 07/37] release: establish Roots maintenance and assurance
ci/release/prepare-release.sh
Before: /dev/null · After: ci/release/prepare-release.sh
File metadata
new file mode 100755 index 0000000000000000000000000000000000000000..782832f4099d78b2edc19bf366629e9ad4eace0e
Download original patch · Release notes
Diff rows 1–100 of 100. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.
ci/release/prepare-release.sh
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# Copyright (c) 2026-present The Bitcoin Roots developers
|
| 3 |
+
# Distributed under the MIT software license, see the accompanying
|
| 4 |
+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
| 5 |
+
|
| 6 |
+
export LC_ALL=C
|
| 7 |
+
set -Eeuo pipefail
|
| 8 |
+
|
| 9 |
+
if [[ "$#" -ne 5 ]]; then
|
| 10 |
+
printf 'Usage: %s DOWNLOAD_DIR OUTPUT_DIR PUBLIC_KEY_FILE RELEASE_NAME EXPECTED_PACKAGE_COUNT\n' "$0" >&2
|
| 11 |
+
exit 2
|
| 12 |
+
fi
|
| 13 |
+
|
| 14 |
+
download_dir=$1
|
| 15 |
+
output_dir=$2
|
| 16 |
+
public_key_file=$3
|
| 17 |
+
release_name=$4
|
| 18 |
+
expected_package_count=$5
|
| 19 |
+
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
| 20 |
+
archive_tool="$script_dir/archive.py"
|
| 21 |
+
|
| 22 |
+
if [[ ! -d "$download_dir" ]]; then
|
| 23 |
+
printf 'Download directory does not exist: %s\n' "$download_dir" >&2
|
| 24 |
+
exit 1
|
| 25 |
+
fi
|
| 26 |
+
if [[ ! -f "$public_key_file" ]]; then
|
| 27 |
+
printf 'Public key does not exist: %s\n' "$public_key_file" >&2
|
| 28 |
+
exit 1
|
| 29 |
+
fi
|
| 30 |
+
if [[ ! "$expected_package_count" =~ ^[1-9][0-9]*$ ]]; then
|
| 31 |
+
printf 'Expected package count must be a positive integer: %s\n' "$expected_package_count" >&2
|
| 32 |
+
exit 1
|
| 33 |
+
fi
|
| 34 |
+
|
| 35 |
+
mkdir -p "$output_dir"
|
| 36 |
+
if find "$output_dir" -mindepth 1 -print -quit | grep -q .; then
|
| 37 |
+
printf 'Output directory is not empty: %s\n' "$output_dir" >&2
|
| 38 |
+
exit 1
|
| 39 |
+
fi
|
| 40 |
+
|
| 41 |
+
mapfile -d '' package_paths < <(
|
| 42 |
+
find "$download_dir" -type f \( -name '*.tar.gz' -o -name '*.zip' \) -print0 | sort -z
|
| 43 |
+
)
|
| 44 |
+
if [[ "${#package_paths[@]}" -ne "$expected_package_count" ]]; then
|
| 45 |
+
printf 'Expected %s release packages, found %s\n' "$expected_package_count" "${#package_paths[@]}" >&2
|
| 46 |
+
exit 1
|
| 47 |
+
fi
|
| 48 |
+
|
| 49 |
+
declare -A package_names=()
|
| 50 |
+
for package_path in "${package_paths[@]}"; do
|
| 51 |
+
package_name=${package_path##*/}
|
| 52 |
+
if [[ "$package_name" == *$'\n'* ]]; then
|
| 53 |
+
printf 'Release package names must not contain newlines\n' >&2
|
| 54 |
+
exit 1
|
| 55 |
+
fi
|
| 56 |
+
if [[ -n "${package_names[$package_name]:-}" ]]; then
|
| 57 |
+
printf 'Duplicate release package name: %s\n' "$package_name" >&2
|
| 58 |
+
exit 1
|
| 59 |
+
fi
|
| 60 |
+
package_names[$package_name]=1
|
| 61 |
+
done
|
| 62 |
+
|
| 63 |
+
mapfile -d '' patch_paths < <(find "$download_dir" -type f -name '*.patch' -print0 | sort -z)
|
| 64 |
+
if [[ "${#patch_paths[@]}" -ne 1 ]]; then
|
| 65 |
+
printf 'Expected one release patch series, found %s\n' "${#patch_paths[@]}" >&2
|
| 66 |
+
exit 1
|
| 67 |
+
fi
|
| 68 |
+
patch_path=${patch_paths[0]}
|
| 69 |
+
patch_name=${patch_path##*/}
|
| 70 |
+
if [[ "$patch_name" == *$'\n'* || -n "${package_names[$patch_name]:-}" ]]; then
|
| 71 |
+
printf 'Invalid or duplicate release patch name: %s\n' "$patch_name" >&2
|
| 72 |
+
exit 1
|
| 73 |
+
fi
|
| 74 |
+
if [[ ! -s "$patch_path" ]]; then
|
| 75 |
+
printf 'Release patch series is empty: %s\n' "$patch_path" >&2
|
| 76 |
+
exit 1
|
| 77 |
+
fi
|
| 78 |
+
package_names[$patch_name]=1
|
| 79 |
+
|
| 80 |
+
archive_root="$(RELEASE_TAG="$release_name" python3 "$archive_tool" root-name)"
|
| 81 |
+
for package_path in "${package_paths[@]}"; do
|
| 82 |
+
package_name=${package_path##*/}
|
| 83 |
+
python3 "$archive_tool" validate "$package_path" "$archive_root"
|
| 84 |
+
cp -- "$package_path" "$output_dir/$package_name"
|
| 85 |
+
done
|
| 86 |
+
cp -- "$patch_path" "$output_dir/$patch_name"
|
| 87 |
+
|
| 88 |
+
mapfile -d '' package_names_sorted < <(printf '%s\0' "${!package_names[@]}" | sort -z)
|
| 89 |
+
manifest="$output_dir/SHA512SUMS"
|
| 90 |
+
{
|
| 91 |
+
printf '# Bitcoin Roots release: %s\n' "$release_name"
|
| 92 |
+
printf '# This manifest is signed using the following OpenPGP public key:\n'
|
| 93 |
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
| 94 |
+
printf '# %s\n' "$line"
|
| 95 |
+
done < "$public_key_file"
|
| 96 |
+
printf '#\n'
|
| 97 |
+
for package_name in "${package_names_sorted[@]}"; do
|
| 98 |
+
(cd "$output_dir" && sha512sum -- "$package_name")
|
| 99 |
+
done
|
| 100 |
+
} > "$manifest"
|