| 1 | #!/usr/bin/env bash |
| 2 | set -euo pipefail |
| 3 | # Regenerate Hmbown.CodeWhale winget manifests for a given release version. |
| 4 | # Usage: ./packaging/winget/generate-winget-manifest.sh X.Y.Z [release-assets-dir] |
| 5 | # X.Y.Z — version without leading v (e.g. 0.9.5) |
| 6 | # release-assets-dir — directory containing codewhale-artifacts-sha256.txt and the four |
| 7 | # Windows assets (defaults to ./release-assets if present). |
| 8 | # The script rewrites both packaging/winget/Hmbown.CodeWhale.yaml and .winget/Hmbown.CodeWhale.yaml. |
| 9 | |
| 10 | if [[ $# -lt 1 || $# -gt 2 ]]; then |
| 11 | echo "usage: $0 X.Y.Z [release-assets-dir]" >&2 |
| 12 | exit 2 |
| 13 | fi |
| 14 | |
| 15 | version="$1" |
| 16 | assets_dir="${2:-release-assets}" |
| 17 | |
| 18 | if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then |
| 19 | echo "version must be X.Y.Z without leading v, got: $version" >&2 |
| 20 | exit 2 |
| 21 | fi |
| 22 | |
| 23 | if [[ ! -f "$assets_dir/codewhale-artifacts-sha256.txt" ]]; then |
| 24 | echo "release assets dir must contain codewhale-artifacts-sha256.txt: $assets_dir" >&2 |
| 25 | exit 1 |
| 26 | fi |
| 27 | |
| 28 | require_sha() { |
| 29 | local name="$1" |
| 30 | local sha |
| 31 | sha="$(grep -E " ${name}\$" "$assets_dir/codewhale-artifacts-sha256.txt" | awk '{print $1}' || true)" |
| 32 | if [[ -z "$sha" ]]; then |
| 33 | echo "SHA not found for $name in $assets_dir/codewhale-artifacts-sha256.txt" >&2 |
| 34 | exit 1 |
| 35 | fi |
| 36 | if [[ ! "$sha" =~ ^[0-9a-fA-F]{64}$ ]]; then |
| 37 | echo "invalid SHA for $name: $sha" >&2 |
| 38 | exit 1 |
| 39 | fi |
| 40 | printf '%s' "$sha" | tr 'A-F' 'a-f' |
| 41 | } |
| 42 | |
| 43 | installer_sha="$(require_sha "CodeWhaleSetup.exe")" |
| 44 | x64_sha="$(require_sha "codewhale-windows-x64.zip")" |
| 45 | x64_portable_sha="$(require_sha "codewhale-windows-x64-portable.zip")" |
| 46 | arm64_sha="$(require_sha "codewhale-windows-arm64.zip")" |
| 47 | arm64_portable_sha="$(require_sha "codewhale-windows-arm64-portable.zip")" |
| 48 | today="$(date -u +%Y-%m-%d)" |
| 49 | |
| 50 | repo_root="$(cd "$(dirname "$0")/../.." && pwd)" |
| 51 | winget_primary="$repo_root/packaging/winget/Hmbown.CodeWhale.yaml" |
| 52 | winget_mirror="$repo_root/.winget/Hmbown.CodeWhale.yaml" |
| 53 | |
| 54 | bump_manifest() { |
| 55 | local file="$1" |
| 56 | if [[ ! -f "$file" ]]; then |
| 57 | echo "manifest not found: $file" >&2 |
| 58 | exit 1 |
| 59 | fi |
| 60 | # PackageVersion + ReleaseNotes |
| 61 | sed -i.bak -E "s/^PackageVersion:.*/PackageVersion: ${version}/" "$file" |
| 62 | sed -i.bak -E "s|ReleaseNotes:.*|ReleaseNotes: https://github.com/Hmbown/CodeWhale/releases/tag/v${version}|" "$file" |
| 63 | sed -i.bak -E "s|ReleaseNotesUrl:.*|ReleaseNotesUrl: https://github.com/Hmbown/CodeWhale/releases/tag/v${version}|" "$file" |
| 64 | # Installer URLs (all five) |
| 65 | sed -i.bak -E "s|https://github.com/Hmbown/CodeWhale/releases/download/v[0-9.]+/CodeWhaleSetup.exe|https://github.com/Hmbown/CodeWhale/releases/download/v${version}/CodeWhaleSetup.exe|g" "$file" |
| 66 | sed -i.bak -E "s|https://github.com/Hmbown/CodeWhale/releases/download/v[0-9.]+/codewhale-windows-x64\.zip|https://github.com/Hmbown/CodeWhale/releases/download/v${version}/codewhale-windows-x64.zip|g" "$file" |
| 67 | sed -i.bak -E "s|https://github.com/Hmbown/CodeWhale/releases/download/v[0-9.]+/codewhale-windows-x64-portable\.zip|https://github.com/Hmbown/CodeWhale/releases/download/v${version}/codewhale-windows-x64-portable.zip|g" "$file" |
| 68 | sed -i.bak -E "s|https://github.com/Hmbown/CodeWhale/releases/download/v[0-9.]+/codewhale-windows-arm64\.zip|https://github.com/Hmbown/CodeWhale/releases/download/v${version}/codewhale-windows-arm64.zip|g" "$file" |
| 69 | sed -i.bak -E "s|https://github.com/Hmbown/CodeWhale/releases/download/v[0-9.]+/codewhale-windows-arm64-portable\.zip|https://github.com/Hmbown/CodeWhale/releases/download/v${version}/codewhale-windows-arm64-portable.zip|g" "$file" |
| 70 | sed -i.bak -E "s/^ ReleaseDate:.*/ ReleaseDate: ${today}/g" "$file" |
| 71 | rm -f "$file.bak" |
| 72 | |
| 73 | # Patch SHAs in order of appearance: CodeWhaleSetup.exe, x64 zip, x64 portable, arm64 zip, arm64 portable. |
| 74 | # Use awk for ordered replacement to avoid sed ambiguity with identical sha lengths. |
| 75 | local tmp |
| 76 | tmp="$(mktemp)" |
| 77 | awk -v a="$installer_sha" -v b="$x64_sha" -v c="$x64_portable_sha" -v d="$arm64_sha" -v e="$arm64_portable_sha" ' |
| 78 | BEGIN { n=0 } |
| 79 | /InstallerSha256:/ { |
| 80 | n++ |
| 81 | if (n==1) sub(/InstallerSha256:.*/, "InstallerSha256: " a) |
| 82 | else if (n==2) sub(/InstallerSha256:.*/, "InstallerSha256: " b) |
| 83 | else if (n==3) sub(/InstallerSha256:.*/, "InstallerSha256: " c) |
| 84 | else if (n==4) sub(/InstallerSha256:.*/, "InstallerSha256: " d) |
| 85 | else if (n==5) sub(/InstallerSha256:.*/, "InstallerSha256: " e) |
| 86 | } |
| 87 | { print } |
| 88 | ' "$file" > "$tmp" |
| 89 | mv "$tmp" "$file" |
| 90 | } |
| 91 | |
| 92 | bump_manifest "$winget_primary" |
| 93 | # The README promises a verbatim structural mirror. Generate the canonical |
| 94 | # manifest once, then copy those exact bytes so comments and optional fields |
| 95 | # cannot drift while versions and hashes still look current. |
| 96 | cp "$winget_primary" "$winget_mirror" |
| 97 | |
| 98 | echo "winget manifests bumped to v$version ($today)" |
| 99 | echo " CodeWhaleSetup.exe: $installer_sha" |
| 100 | echo " codewhale-windows-x64.zip: $x64_sha" |
| 101 | echo " codewhale-windows-x64-portable.zip: $x64_portable_sha" |
| 102 | echo " codewhale-windows-arm64.zip: $arm64_sha" |
| 103 | echo " codewhale-windows-arm64-portable.zip: $arm64_portable_sha" |
| 104 | echo "Validate: winget validate --manifest $winget_primary" |
| 105 |