bots-garden/hellopublic Fork 0
v0.1.0
Commits
Clone
git clone https://git.rickub.com/bots-garden/hello.git
git clone ssh://git@rickub.com/bots-garden/hello.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

📦 Hello 0368b85 · on v0.1.0 · k33g · 3h ago
01-release.tag.sh · 111 lines · 4.0 KBBash Blame HistoryRaw
  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
#!/bin/bash
: <<'COMMENT'
Publishing hello is tagging it. There is no binary to build and nothing to
upload: a Go module is published by a tag being reachable from its repository.

1. Set TAG and ABOUT in release.env
2. Run this script: ./01-release.tag.sh   (test, commit, push, tag, push the tag)
3. Watch the "Release" workflow (Actions tab): the tag push starts it, and it
   creates the release page with the job's own token. Nothing else to run.
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 in a sibling repository, and the `git push` below then pushed the OLD
# tag — so a release was cut from a commit nobody meant.
set -euo pipefail

if [ ! -f release.env ]; then
	echo "❌ release.env is missing"
	echo "💡 Create it with the version you are publishing:"
	echo '     TAG="v0.1.0"'
	echo '     ABOUT="Hello"'
	exit 1
fi

set -o allexport
# shellcheck source=/dev/null
source release.env
set +o allexport

: "${TAG:?TAG is not set in release.env}"
ABOUT="${ABOUT:-Hello ${TAG}}"

# A tag that is not vMAJOR.MINOR.PATCH[-prerelease] is worse than a typo here:
# the module proxy will not serve a tag it cannot read as a version, so the
# release would look fine and fail at every `go get`.
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

echo "Releasing hello ${TAG}: ${ABOUT}"

# 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: somebody may already pin"
	echo "   it, and the module proxy caches what it fetched. Bump TAG in"
	echo "   release.env instead."
	exit 1
fi

# A published library must not carry a replace directive: the proxy serves the
# go.mod as written, and a consumer would be told to look for hello in a
# directory that does not exist on their 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

# A version somebody may pin is the wrong place to find out the suite was red.
echo "→ go vet ./..."
go vet ./...
echo "→ go test ./..."
go test ./... -count=1

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

# --show-current rather than `rev-parse --abbrev-ref HEAD`, which fails on a
# branch with no commits yet — exactly the state a first release starts from.
branch="$(git branch --show-current)"
git push -u origin "${branch}"

# 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 "✅ hello ${TAG} published"
echo "💡 The tag push started the Release workflow; watch it on the Actions tab."