|
| 1 | +#!/bin/sh |
| 2 | +# |
| 3 | +# Promote a microsoft/git release into the microsoft/homebrew-git tap. |
| 4 | +# |
| 5 | +# Usage: |
| 6 | +# .github/release-homebrew.sh [--force] [<TAG_NAME>] |
| 7 | +# |
| 8 | +# If TAG_NAME is omitted, the latest microsoft/git release is used. |
| 9 | +# |
| 10 | +# Downgrades require `--force`. |
| 11 | +# |
| 12 | +# Prerequisites: |
| 13 | +# - `gh` authenticated (via `gh auth login`) as a user with push |
| 14 | +# access to microsoft/homebrew-git. |
| 15 | +# - `git`, `jq`, and `sed` on PATH. |
| 16 | +# |
| 17 | +# Given a release tag on microsoft/git (e.g. v2.54.0.vfs.0.4), this |
| 18 | +# script looks up the macOS installer asset for that tag, extracts the |
| 19 | +# SHA-256 digest reported by the GitHub Releases API (deliberately not |
| 20 | +# re-hashed locally; see microsoft/homebrew-git#102), edits the |
| 21 | +# `microsoft-git` cask in place preserving its indentation and quote |
| 22 | +# style, pushes the update to the tap under a datetime-keyed branch, |
| 23 | +# and opens a PR. |
| 24 | +# |
| 25 | +# This mirrors the behaviour of mjcheetham/update-homebrew@v1.5.1 |
| 26 | +# invoked with `type: cask`, `alwaysUsePullRequest: true`. |
| 27 | + |
| 28 | +set -eu |
| 29 | + |
| 30 | +die () { |
| 31 | + echo "error: $*" >&2 |
| 32 | + exit 1 |
| 33 | +} |
| 34 | + |
| 35 | +case "$1" in |
| 36 | +--force) force=t; shift;; |
| 37 | +*) force=;; |
| 38 | +esac |
| 39 | + |
| 40 | +TAG_NAME=${1-} |
| 41 | +if [ -z "$TAG_NAME" ]; then |
| 42 | + echo "==> No tag given; resolving latest microsoft/git release" |
| 43 | + TAG_NAME=$(gh release view -R microsoft/git \ |
| 44 | + --json tagName --jq .tagName) |
| 45 | + test -n "$TAG_NAME" || die "could not determine latest release tag" |
| 46 | +fi |
| 47 | + |
| 48 | +echo "==> Tag: $TAG_NAME" |
| 49 | + |
| 50 | +version=${TAG_NAME#v} |
| 51 | +echo "==> Version: $version" |
| 52 | + |
| 53 | +# Refuse to downgrade the cask and short-circuit no-op runs. Look up |
| 54 | +# the version currently in the tap and compare before fetching the |
| 55 | +# release JSON or cloning. |
| 56 | +current_content=$(gh api \ |
| 57 | + repos/microsoft/homebrew-git/contents/Casks/microsoft-git.rb \ |
| 58 | + -H "Accept: application/vnd.github.raw") |
| 59 | +current_version=$(printf '%s\n' "$current_content" | sed -nE \ |
| 60 | + "s/^[[:space:]]*version *['\"]([^'\"]*)['\"].*/\\1/p") |
| 61 | +test -n "$current_version" || die "could not parse current cask version" |
| 62 | +echo "==> Current: $current_version" |
| 63 | + |
| 64 | +if [ "$version" = "$current_version" ]; then |
| 65 | + echo "warning: cask is already at $version; nothing to do." >&2 |
| 66 | + exit 0 |
| 67 | +fi |
| 68 | +lowest=$(printf '%s\n%s\n' "$version" "$current_version" | |
| 69 | + sort -V | sed 1q) |
| 70 | +if [ "$lowest" = "$version" ]; then |
| 71 | + test -n "$force" || |
| 72 | + die "regression: cask is at $current_version," \ |
| 73 | + "refusing to downgrade to $version" |
| 74 | + echo "warning: **downgrading** from $current_version to $version" >&2 |
| 75 | +fi |
| 76 | + |
| 77 | +echo "==> Fetching release metadata" |
| 78 | +release_json=$(gh api \ |
| 79 | + -H "Accept: application/vnd.github+json" \ |
| 80 | + -H "X-GitHub-Api-Version: 2022-11-28" \ |
| 81 | + "repos/microsoft/git/releases/tags/$TAG_NAME") |
| 82 | + |
| 83 | +asset_pattern='git-(.*)\.pkg' |
| 84 | +asset_json=$(jq -n \ |
| 85 | + --argjson release "$release_json" \ |
| 86 | + --arg pat "$asset_pattern" ' |
| 87 | + [ $release.assets[] | select(.name | test($pat)) ] as $matches |
| 88 | + | if ($matches | length) == 0 then |
| 89 | + error("no asset matches pattern \($pat)") |
| 90 | + elif ($matches | length) > 1 then |
| 91 | + error("multiple assets match pattern \($pat): " + |
| 92 | + ([$matches[].name] | join(", "))) |
| 93 | + else $matches[0] end') |
| 94 | + |
| 95 | +digest=$(jq -n -r --argjson a "$asset_json" '$a.digest // ""') |
| 96 | +case "$digest" in |
| 97 | +sha256:*) sha256=${digest#sha256:} ;; |
| 98 | +"") die "asset has no 'digest' field" ;; |
| 99 | +*) die "asset digest is not sha256: $digest" ;; |
| 100 | +esac |
| 101 | + |
| 102 | +# Enforce 64 lowercase hex chars without spawning grep. |
| 103 | + *[!0-9a-f]*|"") |
| 104 | +esac |
| 105 | +test ${#sha256} -eq 64 || |
| 106 | + die "asset digest is not 64 chars long: $sha256" |
| 107 | + |
| 108 | +echo "==> Asset: $(jq -n -r --argjson a "$asset_json" '$a.name')" |
| 109 | +echo "==> SHA-256: $sha256" |
| 110 | + |
| 111 | +workdir=$(mktemp -d) |
| 112 | +trap 'rm -rf "$workdir"' EXIT |
| 113 | + |
| 114 | +echo "==> Cloning microsoft/homebrew-git" |
| 115 | +REPO=microsoft/homebrew-git |
| 116 | +gh repo clone "$REPO" "$workdir/homebrew-git" -- \ |
| 117 | + --depth=1 --quiet |
| 118 | + |
| 119 | +cd "$workdir/homebrew-git" |
| 120 | + |
| 121 | +# Preserve existing indentation and quote style, replacing only the |
| 122 | +# value; matches mjcheetham/update-homebrew's setField regex. The |
| 123 | +# `<file >file.new && mv -f file.new file` idiom sidesteps the |
| 124 | +# incompatible `sed -i` spellings between GNU and BSD sed. |
| 125 | +f=Casks/microsoft-git.rb |
| 126 | +# Capture opening quote as \2, match value up to the matching quote. |
| 127 | +q='(['\''"])[^'\''"]+\2' |
| 128 | +sed -E \ |
| 129 | + -e "s/^([[:space:]]*)version +$q/\\1version \\2$version\\2/" \ |
| 130 | + -e "s/^([[:space:]]*)sha256 +$q/\\1sha256 \\2$sha256\\2/" \ |
| 131 | + <"$f" >"$f.new" && |
| 132 | +mv -f "$f.new" "$f" |
| 133 | + |
| 134 | +if git diff --quiet -- Casks/microsoft-git.rb; then |
| 135 | + echo "==> No changes needed; cask is already at $version." |
| 136 | + exit 0 |
| 137 | +fi |
| 138 | + |
| 139 | +git --no-pager diff -- Casks/microsoft-git.rb |
| 140 | + |
| 141 | +BRANCH=update-$(date +%Y-%m-%d-%H-%M-%S) |
| 142 | +git switch -c $BRANCH |
| 143 | +TITLE="microsoft-git: update to $version" |
| 144 | +git commit -m "$TITLE" \ |
| 145 | + -- Casks/microsoft-git.rb |
| 146 | + |
| 147 | +git push origin HEAD |
| 148 | + |
| 149 | +echo "==> Pushed: $(git log -1 --format='%h %s')" |
| 150 | + |
| 151 | +pr_url=$(gh pr create \ |
| 152 | + --repo "$REPO" \ |
| 153 | + --head "$BRANCH" \ |
| 154 | + --title "$TITLE") |
| 155 | + |
| 156 | +echo "==> Created: $pr_url" |
0 commit comments