| 1 | #!/usr/bin/env bash |
| 2 | # Create or recover one immutable Desktop GitHub release. Existing metadata and |
| 3 | # assets must match the candidate; interrupted runs may only fill missing assets. |
| 4 | set -euo pipefail |
| 5 | |
| 6 | if [ "$#" -ne 5 ]; then |
| 7 | echo "usage: $0 TAG VERSION PRERELEASE NOTES_FILE ASSET_DIRECTORY" >&2 |
| 8 | exit 2 |
| 9 | fi |
| 10 | |
| 11 | tag="$1" |
| 12 | version="$2" |
| 13 | prerelease="$3" |
| 14 | notes_file="$4" |
| 15 | asset_dir="${5%/}" |
| 16 | repository="${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is required}" |
| 17 | script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" |
| 18 | directory_verifier="$script_dir/verify-desktop-release-directory.sh" |
| 19 | title="Reasonix Desktop $version" |
| 20 | |
| 21 | case "$prerelease" in |
| 22 | true | false) ;; |
| 23 | *) |
| 24 | echo "Desktop GitHub release prerelease flag must be true or false: $prerelease" >&2 |
| 25 | exit 2 |
| 26 | ;; |
| 27 | esac |
| 28 | if [ ! -s "$notes_file" ]; then |
| 29 | echo "Desktop GitHub release notes are missing or empty: $notes_file" >&2 |
| 30 | exit 2 |
| 31 | fi |
| 32 | if [ ! -d "$asset_dir" ]; then |
| 33 | echo "Desktop GitHub release asset directory is missing: $asset_dir" >&2 |
| 34 | exit 2 |
| 35 | fi |
| 36 | if find "$asset_dir" -mindepth 2 -print -quit | grep -q .; then |
| 37 | echo "Desktop GitHub release assets must be top-level files" >&2 |
| 38 | exit 2 |
| 39 | fi |
| 40 | if ! find "$asset_dir" -mindepth 1 -maxdepth 1 -type f -print -quit | grep -q .; then |
| 41 | echo "Desktop GitHub release asset directory is empty: $asset_dir" >&2 |
| 42 | exit 2 |
| 43 | fi |
| 44 | |
| 45 | work_dir="$(mktemp -d)" |
| 46 | cleanup() { |
| 47 | case "$work_dir" in |
| 48 | */tmp.* | */reasonix-desktop-release.*) rm -rf -- "$work_dir" ;; |
| 49 | *) echo "refusing to clean unexpected Desktop release temp directory: $work_dir" >&2 ;; |
| 50 | esac |
| 51 | } |
| 52 | trap cleanup EXIT |
| 53 | |
| 54 | release_json="$work_dir/release.json" |
| 55 | release_error="$work_dir/release.error" |
| 56 | release_endpoint="repos/${repository}/releases/tags/${tag}" |
| 57 | if ! gh api "$release_endpoint" >"$release_json" 2>"$release_error"; then |
| 58 | if ! grep -Eiq '404|Not Found' "$release_error"; then |
| 59 | cat "$release_error" >&2 |
| 60 | exit 1 |
| 61 | fi |
| 62 | args=(--title "$title" --notes-file "$notes_file") |
| 63 | if [ "$prerelease" = "true" ]; then |
| 64 | args+=(--prerelease --latest=false) |
| 65 | else |
| 66 | args+=(--latest) |
| 67 | fi |
| 68 | gh release create "$tag" -R "$repository" "${args[@]}" |
| 69 | gh api "$release_endpoint" >"$release_json" |
| 70 | fi |
| 71 | |
| 72 | jq -e \ |
| 73 | --arg tag "$tag" \ |
| 74 | --arg title "$title" \ |
| 75 | --rawfile body "$notes_file" \ |
| 76 | --argjson prerelease "$prerelease" ' |
| 77 | (type == "object") and |
| 78 | (.tag_name == $tag) and |
| 79 | (.name == $title) and |
| 80 | (.body == $body) and |
| 81 | (.draft == false) and |
| 82 | (.prerelease == $prerelease) and |
| 83 | (.assets | type == "array") and |
| 84 | ((.assets | map(.name) | length) == (.assets | map(.name) | unique | length)) |
| 85 | ' "$release_json" >/dev/null |
| 86 | |
| 87 | existing_dir="$work_dir/existing" |
| 88 | mkdir -p "$existing_dir" |
| 89 | while IFS= read -r name; do |
| 90 | if [ -z "$name" ] || [ ! -f "$asset_dir/$name" ]; then |
| 91 | echo "Desktop GitHub release has unexpected asset: ${name:-<empty>}" >&2 |
| 92 | exit 1 |
| 93 | fi |
| 94 | gh release download "$tag" -R "$repository" --pattern "$name" --dir "$existing_dir" |
| 95 | done < <(jq -r '.assets[].name' "$release_json") |
| 96 | |
| 97 | bash "$directory_verifier" --allow-missing "$asset_dir" "$existing_dir" |
| 98 | |
| 99 | while IFS= read -r -d '' asset; do |
| 100 | name="$(basename "$asset")" |
| 101 | if [ ! -f "$existing_dir/$name" ]; then |
| 102 | gh release upload "$tag" -R "$repository" "$asset" |
| 103 | fi |
| 104 | done < <(find "$asset_dir" -mindepth 1 -maxdepth 1 -type f -print0) |
| 105 | |
| 106 | published_dir="$work_dir/published" |
| 107 | mkdir -p "$published_dir" |
| 108 | gh release download "$tag" -R "$repository" --dir "$published_dir" |
| 109 | bash "$directory_verifier" "$asset_dir" "$published_dir" |
| 110 | echo "Desktop GitHub release verified: tag=$tag assets=$(find "$asset_dir" -mindepth 1 -maxdepth 1 -type f | wc -l | tr -d ' ')" |
| 111 |