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
|
#!/bin/bash
: <<'COMMENT'
Releasing turbo-golo is tagging it. Pushing the tag starts the Release workflow,
which builds the binaries and publishes the release page with them.
1. Set TAG and ABOUT in release.env
2. Run this script: ./01-release.tag.sh (make check, commit, push, tag, push the tag)
3. Watch the "Release" workflow on Rickub (Actions tab): the tag push starts
it; it cross-compiles the binaries with ./02-build-releases.sh and publishes
the release page with them, using the job's own token. No personal token is
needed, and nothing else has to be run by hand.
COMMENT
# Without this, a failing step is ignored and the next one runs anyway. That is
# not theoretical: `git tag` refusing a tag that already existed was skipped in
# silence, and the `git push` below then pushed the OLD tag — so a release was
# cut from a commit nobody meant, and 02 was left to notice.
set -euo pipefail
if [ ! -f release.env ]; then
echo "❌ release.env is missing"
echo "💡 Create it with the version you are publishing:"
echo ' TAG=v1.0.0'
echo ' ABOUT="Turbo Golo"'
exit 1
fi
set -o allexport
# shellcheck source=/dev/null
source release.env
set +o allexport
echo "Releasing turbo-golo ${TAG}: ${ABOUT}"
# A tag that is not vMAJOR.MINOR.PATCH[-prerelease] is a typo — and worse than
# a typo: the workflow only starts on v*, and the module proxy will not serve
# `go install …@TAG` for a tag it cannot read as a version.
if ! [[ "${TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo "❌ TAG must look like v1.2.3 or v1.2.3-rc.1, got '${TAG}' (check release.env)"
exit 1
fi
# The whole suite has to pass before a version exists that people will
# download. A tag the proxy has cached is the wrong place to find out.
#
# The marker matters: the suite includes tests that run *this script* against a
# throwaway clone, and without it `make check` would run those, which would run
# this script, which would run `make check`. They skip themselves when they see
# it. Exported, so it reaches the test binary through make and go test.
export TURBO_GOLO_RELEASING=1
echo "→ make check"
make --no-print-directory check
# tagExists reports whether TAG is already taken, here or on the remote. The
# remote matters on its own: a tag deleted locally after a failed attempt still
# exists there, and pushing a new one at a different commit is rejected.
tagExists() {
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
printf 'locally, on %s\n' "$(git rev-parse --short "${TAG}^{commit}")"
return 0
fi
if ! remote="$(git ls-remote --tags origin "refs/tags/${TAG}" 2>/dev/null)"; then
return 1 # the remote is unreachable; the push below will say so
fi
if [ -n "${remote}" ]; then
# No commit is named here on purpose: for an annotated tag ls-remote
# gives the tag object, not the commit, and printing that as if it were
# the commit sends the reader looking for a SHA they will never find.
printf 'on origin\n'
return 0
fi
return 1
}
if where="$(tagExists)"; then
echo "❌ ${TAG} already exists ${where}"
echo "💡 A published version is not yours to move: the module proxy caches"
echo " what it fetched for \`go install\`, and the release page already"
echo " carries binaries with that number. Bump TAG in release.env instead."
exit 1
fi
# The editor must not be published with a replace directive in it: the module
# proxy serves the go.mod as written, and `go install …@TAG` would then look
# for turbo-core in a directory that does not exist on the installer's machine.
if grep -qE '^[[:space:]]*replace[[:space:]]' go.mod; then
echo "❌ go.mod has a replace directive, which a published module must not"
grep -nE '^[[:space:]]*replace[[:space:]]' go.mod
exit 1
fi
find . -name '.DS_Store' -type f -delete
git add .
# Nothing to commit is not a failure — the work may already be committed — but
# under `set -e` a plain `git commit` would stop the release right here.
if git diff --cached --quiet; then
echo "Nothing to commit; releasing what is already on HEAD"
else
git commit -m "📦 ${ABOUT}"
fi
git push origin "$(git rev-parse --abbrev-ref HEAD)"
# The tag goes on after the push, so a rejected push never leaves a tag behind
# pointing at a commit the remote has never seen.
git tag -a "${TAG}" -m "${ABOUT}"
git push origin "${TAG}"
echo "✅ turbo-golo ${TAG} published"
echo "💡 The tag push started the Release workflow; it builds the binaries and"
echo " creates the release page. Watch it on the repository's Actions tab."
echo "💡 To see what it will build without publishing anything:"
echo " ./02-build-releases.sh ${TAG}"
|