Pushing Tags Scripts
Tags are not sent by a plain git push, so a release script has to push them deliberately.
Tags are the one thing git push does not send. A plain push transfers
commits; tags stay on your machine until you name them. That is deliberate — a
tag is a published claim about a point in history, so sending it should be a
decision rather than a side effect.
This is the step most release scripts forget, and the symptom is always the same: CI cannot find the version tag, because it was never pushed.
What to run
git push origin --tags # every tag
git push origin v1.4.0 # just one
git push --follow-tags # tags for the commits being pushed
--follow-tags is usually the right choice in a release script, because it
sends the tags reachable from the commits being pushed and nothing older.
Bash
#!/usr/bin/env bash
# push-tags.sh
#
# Push a tag to the remote, refusing to overwrite an existing one.
set -euo pipefail
tag="${1:-}"
remote="${2:-origin}"
if [ -z "$tag" ]; then
echo "Usage: $0 <tag> [remote]" >&2
exit 1
fi
if ! git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
echo "No such tag locally: $tag" >&2
exit 1
fi
if git ls-remote --exit-code --tags "$remote" "refs/tags/$tag" >/dev/null 2>&1; then
echo "Tag $tag already exists on $remote; refusing to overwrite it." >&2
exit 1
fi
git push "$remote" "refs/tags/$tag"
echo "Pushed $tag to $remote"
Deleting a tag
Tags are not meant to move, so both sides need telling:
git tag -d v1.4.0 # local
git push origin --delete v1.4.0 # remote
Doing only the first leaves a tag that exists in one place and not the other, which is the more confusing state of the two.
Verifying afterwards
git ls-remote --tags origin
If a CI job reports a missing version, this is almost always why. See Pushing Tags for the same commands without the scripting, and GitHub Releases for attaching binaries to a tag.