aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/brew-release
blob: dbaf526ab88370b844e584909075928f272006ca (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/bin/sh
# brew-release -- point the Homebrew formula at a commit or tag, with the
# sha256 that the cgit snapshot actually has right now.
#
#   contrib/brew-release                 # HEAD of origin/master
#   contrib/brew-release v0.2.0          # a tag (must be pushed already)
#   contrib/brew-release 1368b7f         # any ref the server can resolve
#   contrib/brew-release --tap ~/src/homebrew-slosh v0.2.0
#
# It edits contrib/homebrew/slosh.rb in place (url, version, sha256) and, with
# --tap, copies the result into <tap>/Formula/slosh.rb. It never commits or
# pushes: read the diff, then commit both repos yourself.
#
# Publishing a new version is therefore:
#
#   1. bump VERSION in the Makefile, commit, tag, push (tag included)
#   2. contrib/brew-release --tap ../homebrew-slosh v0.2.0
#   3. brew install --build-from-source contrib/homebrew/slosh.rb   # prove it
#   4. commit the formula here and in the tap, push both

set -eu

REPO_URL="${SLOSH_REPO_URL:-https://git.sl0p.foo/slosh.git}"
SNAP_BASE="${SLOSH_SNAPSHOT_BASE:-https://git.sl0p.foo/slosh.git/snapshot/slosh}"
root="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
FORMULA="$root/contrib/homebrew/slosh.rb"
TAP=""

fail() { printf 'brew-release: %s\n' "$*" >&2; exit 1; }
say()  { printf '  %s\n' "$*"; }

# Neither brew nor a mac is needed to run this -- it resolves a ref, fetches a
# tarball and hashes it -- so it has to work on the Linux box a release is cut
# from. `shasum` is perl's and merely usually present there; `sha256sum` is
# coreutils and always is. Take whichever exists.
sha256_of() {
    if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1
    elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1
    elif command -v openssl >/dev/null 2>&1; then openssl dgst -sha256 "$1" | sed 's/.*= *//'
    else fail "no sha256sum, shasum or openssl here -- cannot hash the snapshot"
    fi
}

while [ $# -gt 0 ]; do
    case "$1" in
        --tap) [ $# -ge 2 ] || fail "--tap needs a directory"; TAP="$2"; shift 2 ;;
        --tap=*) TAP="${1#--tap=}"; shift ;;
        -h|--help) sed -n '2,26p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
        -*) fail "unknown option $1" ;;
        *) break ;;
    esac
done

REF="${1:-}"
[ -f "$FORMULA" ] || fail "no formula at $FORMULA"

# Resolve whatever was asked for to a full commit id, so the URL names one
# immutable tree. A tag keeps its own name in the version string; a commit
# gets the short id.
if [ -z "$REF" ]; then
    REF="$(git -C "$root" rev-parse origin/master)"
    label=""
else
    label="$REF"
fi

full="$(git -C "$root" ls-remote "$REPO_URL" "refs/tags/$REF^{}" "refs/tags/$REF" \
        "refs/heads/$REF" 2>/dev/null | head -1 | cut -f1)"
if [ -z "$full" ]; then
    # Not a remote ref: assume it is a commit id, and let the local clone
    # expand a short one. Then check the server really has it.
    full="$(git -C "$root" rev-parse --verify "${REF}^{commit}" 2>/dev/null || echo "$REF")"
fi
case "$full" in
    ????????????????????????????????????????) ;;
    *) fail "could not resolve '$REF' to a commit id (got '$full')" ;;
esac

# The version brew shows. A tag is the honest answer when there is one;
# otherwise the Makefile's VERSION, the number of commits, and the short id --
# in that order so that `brew upgrade` sees a later build as later. Without the
# count, "0.1.0-b744a90" vs "0.1.0-1368b7f" would compare alphabetically, which
# has nothing to do with which one came first.
# A tag must look like a version (digits with a dot); "1368b7f" starts with a
# digit but is a commit, and calling it version 1368b7f would be a lie brew
# could never climb down from.
# The base/floor version. It lives in ./VERSION (one line, e.g. 0.1.0), which
# is also what the Makefile bakes into a source build that has no git and the
# floor it builds tag-less versions on top of. It used to be grepped out of the
# Makefile's `VERSION :=` line; that line is now a $(shell ...) derivation, so
# read the file both of them trust instead.
mkver="$(sed -n '1s/[[:space:]]*$//p' "$root/VERSION" 2>/dev/null | head -1)"
[ -n "$mkver" ] || fail "no base version in $root/VERSION"
short="$(printf '%s' "$full" | cut -c1-7)"
# The count must be a real count. Falling back to 0 when the commit is not in
# this clone would mint "0.1.0-0-<short>", which brew reads as older than every
# version already published -- so a release cut from a clone that had not
# fetched yet would look like a downgrade and never install.
count="$(git -C "$root" rev-list --count "$full" 2>/dev/null || true)"
[ -n "$count" ] || fail "commit $full is not in this clone, so its position in
history is unknown and the version would be a lie. Fetch it first:
  git -C $root fetch origin"
case "$label" in
    v[0-9]*.[0-9]*) version="${label#v}" ;;
    [0-9]*.[0-9]*)  version="$label" ;;
    *)              version="$mkver-$count-$short" ;;
esac

url="$SNAP_BASE-$full.tar.gz"
say "ref      $REF -> $full"
say "version  $version"
say "url      $url"

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT INT TERM

code="$(curl -sL -o "$tmp/snap.tar.gz" -w '%{http_code}' "$url")"
[ "$code" = 200 ] || fail "snapshot fetch returned HTTP $code -- is the ref pushed?"
tar tzf "$tmp/snap.tar.gz" >/dev/null 2>&1 || fail "snapshot is not a valid tarball"
sha="$(sha256_of "$tmp/snap.tar.gz")"
say "sha256   $sha"

# Rewrite exactly the three stanzas, leaving every comment in place.
awk -v url="$url" -v version="$version" -v sha="$sha" '
    /^  url "/     { print "  url \"" url "\""; next }
    /^  version "/ { print "  version \"" version "\""; next }
    /^  sha256 "/  { print "  sha256 \"" sha "\""; next }
    { print }
' "$FORMULA" > "$tmp/slosh.rb"
grep -q "$sha" "$tmp/slosh.rb" || fail "rewrite did not take -- formula stanzas changed shape?"
mv "$tmp/slosh.rb" "$FORMULA"
say "wrote    $FORMULA"

if [ -n "$TAP" ]; then
    [ -d "$TAP" ] || fail "no such tap directory: $TAP"
    mkdir -p "$TAP/Formula"
    cp "$FORMULA" "$TAP/Formula/slosh.rb"
    say "copied   $TAP/Formula/slosh.rb"
fi

printf '\nnow prove it builds:\n  brew install --build-from-source %s\n' \
    "contrib/homebrew/slosh.rb"
printf 'then commit the formula here%s\n' \
    "${TAP:+ and in $TAP}"