#!/usr/bin/env bash # The Swift package as a release attaches it: WaiFFI.xcframework zipped, and a copy of this package # whose binary target names that zip by URL and checksum. # # scripts/package-release.sh # # is where the release serves its assets (…/releases/download/wai-v). # Needs build/WaiFFI.xcframework, every slice scripts/build-xcframework.sh builds (or those of # WAI_SINK_SLICES), and the tests' staged corpus. Writes to : # WaiFFI-.xcframework.zip the framework, its folder at the zip's top # WaiFFI-.xcframework.zip.sha256 its SHA-256, which is SwiftPM's checksum of it # wai-sink-swift-.zip Package.swift, Sources/, Tests/, README.md and LICENSE # under wai-sink-swift/, Package.swift naming the # framework's zip by and that checksum # wai-sink-swift-.zip.sha256 # An application adds the package from that folder (a local package, or its own copy in a # repository of its own); SwiftPM fetches the framework and refuses it unless its checksum is the # one named. scripts/check-release-package.sh holds the result to that. set -euo pipefail die() { echo "package-release: $*" >&2; exit 1; } [ $# -eq 3 ] || { echo "usage: scripts/package-release.sh " >&2; exit 2; } V="$1" URL_BASE="${2%/}" HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" OUT=$(mkdir -p "$3" && cd "$3" && pwd) FW="$HERE/build/WaiFFI.xcframework" [ -d "$FW" ] || die "no $FW: run scripts/build-xcframework.sh" [ -f "$HERE/Tests/WaiSinkTests/Resources/vectors.json" ] || die "the tests' corpus is not staged: run scripts/build-xcframework.sh" case "$URL_BASE" in https://*) ;; *) die "SwiftPM fetches a binary target over https only: $URL_BASE" ;; esac # Every slice asked for is in the framework. python3 -I - "$FW/Info.plist" "${WAI_SINK_SLICES:-macos ios ios-sim tvos tvos-sim}" <<'PY' || die "the framework does not hold every slice" import plistlib, sys libs = plistlib.load(open(sys.argv[1], "rb"))["AvailableLibraries"] have = {(l["SupportedPlatform"], l.get("SupportedPlatformVariant", "")) for l in libs} want = {"macos": ("macos", ""), "ios": ("ios", ""), "ios-sim": ("ios", "simulator"), "tvos": ("tvos", ""), "tvos-sim": ("tvos", "simulator")} missing = [s for s in sys.argv[2].split() if want[s] not in have] if missing: sys.exit(f"package-release: no slice for {missing} in {sorted(have)}") print(f"WaiFFI.xcframework: {len(libs)} slices, {sorted(have)}") PY ZIP="WaiFFI-$V.xcframework.zip" rm -f "$OUT/$ZIP" "$OUT/$ZIP.sha256" ( cd "$HERE/build" && ditto -c -k --norsrc --keepParent WaiFFI.xcframework "$OUT/$ZIP" ) SUM=$(shasum -a 256 "$OUT/$ZIP" | cut -d' ' -f1) # SwiftPM's checksum is the archive's SHA-256: the two agree, or nothing below is trusted. T=$(mktemp -d) trap 'rm -rf "$T"' EXIT SPM=$(cd "$T" && swift package compute-checksum "$OUT/$ZIP") [ "$SPM" = "$SUM" ] || die "swift package compute-checksum says $SPM, the SHA-256 is $SUM" ( cd "$OUT" && echo "$SUM $ZIP" > "$ZIP.sha256" ) P="$T/wai-sink-swift" mkdir -p "$P" cp -R "$HERE/Package.swift" "$HERE/README.md" "$HERE/Sources" "$HERE/Tests" "$P/" cp "$HERE/../LICENSE" "$P/LICENSE" python3 -I - "$P/Package.swift" "$URL_BASE/$ZIP" "$SUM" <<'PY' || die "Package.swift's release line could not be written" import sys path, url, checksum = sys.argv[1:4] src = open(path, encoding="utf-8").read() old = 'let release: (url: String, checksum: String)? = nil\n' if src.count(old) != 1: sys.exit(f"package-release: Package.swift holds {src.count(old)} release lines, not one") new = f'let release: (url: String, checksum: String)? = (url: "{url}", checksum: "{checksum}")\n' open(path, "w", encoding="utf-8").write(src.replace(old, new)) PY PKG="wai-sink-swift-$V.zip" rm -f "$OUT/$PKG" "$OUT/$PKG.sha256" ( cd "$T" && ditto -c -k --norsrc --keepParent wai-sink-swift "$OUT/$PKG" ) ( cd "$OUT" && shasum -a 256 "$PKG" > "$PKG.sha256" ) echo "package-release: $OUT/$ZIP (checksum $SUM, $(du -h "$OUT/$ZIP" | cut -f1)); $OUT/$PKG names it at $URL_BASE/$ZIP"