tools/release-mac.sh
Ref: Size: 4.5 KiB History
#!/bin/sh
# Cut and publish the macOS half of a release, from a Mac builder.
#
# There is no cross-compiling the Mac binary (README, "macOS"), so a Mac
# release is made on a Mac. This script is the whole of that: it checks that
# the tag exists here and is HEAD, pushes the tag, and then hands ONE remote
# shell script to the builder, which fetches that tag, detaches onto it, runs
# `make release` — the same recipe as everywhere, whose RELEASE_TARGET
# follows the host and so names the tarball aarch64-macos — and publishes it.
#
# The builder is MAC_BUILDER, the same box and the same name the macOS gates
# take (test/mac.sh, test/xos.sh), and MAC_BUILDER_REPO the path to this repo
# under its $HOME. There is no default for MAC_BUILDER: a release published
# to whatever machine happened to be in someone's ssh config is worse than a
# refusal.
#
# What this does NOT do: touch main, push anything but the tag, or run
# git-collab locally. The publish is the builder's, because the builder is
# the box holding the bytes.
#
# The remote script rides ssh's STDIN, so this ssh cannot take `-n` — that
# flag closes stdin and the remote `sh -s` would read EOF, do nothing and
# exit 0, which is a release that silently did not happen. box_lib.sh's
# `box_ssh` is the same shape for the same reason. The consequence is the
# rule that shape carries: nothing inside the remote script may read stdin,
# or it eats the lines after it. Nothing here does.
set -eu
die() {
echo "release-mac: $*" >&2
exit "${RC:-1}"
}
# Before any ssh, so an unset name costs no connection and no tag push.
if [ -z "${MAC_BUILDER:-}" ]; then
echo "release-mac: MAC_BUILDER is unset, and this recipe has no default for it (a Mac holding this repo with zig and deps/mac-sdk)" >&2
exit 2
fi
REPO=${MAC_BUILDER_REPO:-code/rad/mux}
# The same single source the Makefile greps, so the two can never disagree
# about which version is being cut.
V=$(sed -n 's/^[[:space:]]*const version = "\(.*\)";/\1/p' build.zig | head -1)
[ -n "$V" ] || die "no version found in build.zig"
TAG="v$V"
TAR="mux-$TAG-aarch64-macos.tar.gz"
# A release is cut from a tag, never from a branch tip: the builder checks
# out the tag, so anything not tagged is not what ships.
git rev-parse -q --verify "refs/tags/$TAG" >/dev/null 2>&1 ||
die "tag: $TAG does not exist here — tag the commit first (git tag $TAG)"
if [ "$(git rev-parse "refs/tags/$TAG^{commit}")" != "$(git rev-parse HEAD)" ]; then
die "tag: $TAG does not point at HEAD — tag this commit first (git tag $TAG)"
fi
# Pushing a tag already on the remote is a no-op, so this is safe to re-run;
# a tag that MOVED is refused by git itself, which is the check we want.
#
# `--no-follow-tags` and an explicit destination refspec, because ONE tag is
# the whole of what this script is allowed to push. A developer with
# `push.followTags = true` in their ~/.gitconfig — which is a common setting
# and was set on the box this was written on — otherwise sends every
# annotated tag reachable from what is being pushed, so a bare
# `git push origin refs/tags/vN` put five other tags on the remote in the
# hand check.
git push --no-follow-tags origin "refs/tags/$TAG:refs/tags/$TAG" ||
die "push: could not push $TAG to origin"
# One connection. `$V` and `$REPO` are expanded HERE (unquoted heredoc word)
# because the remote shell has neither.
ssh -o BatchMode=yes "$MAC_BUILDER" /bin/sh -s <<EOF || die "builder: $MAC_BUILDER refused or failed — see its output above"
set -eu
# git-collab lives in the product's own prefix, and a bare ssh to a Mac has
# a PATH of /usr/bin:/bin:/usr/sbin:/sbin and nothing else.
PATH="\$PATH:\$HOME/.local/bin"
cd "\$HOME/$REPO"
# A tag fetch and not a pull: the builder's checkout may be on any branch,
# and this must leave that branch exactly where it was.
git fetch -q origin "refs/tags/$TAG:refs/tags/$TAG"
git checkout -q --detach "$TAG"
make release
git-collab release publish "$TAG" "dist/$TAR"
EOF
# The URL the tarball is now at, derived from this checkout's own remote so
# a fork prints its own server and not this one's. Four strips, in order:
# the scheme, any `user@`, the scp-style colon that stands in for the path
# separator, and the `.git` suffix — what is left is what the release server
# serves under.
BASE=$(git remote get-url origin |
sed -e 's,^[A-Za-z][A-Za-z0-9+.-]*://,,' \
-e 's,^[^/@]*@,,' \
-e 's,^\([^/:]*\):,\1/,' \
-e 's,\.git$,,' \
-e 's,/*$,,')
echo "published: https://$BASE/releases/$TAG/$TAR"