All checks were successful
check / check (pull_request) Successful in 2m37s
No tag could be cut from this repo at all. Three independent blockers. goreleaser was configured for GitHub while the repo lives on Gitea: .goreleaser.yaml had a release: block but no gitea_urls:, so goreleaser defaulted to the GitHub API and a release would have failed or published somewhere nobody is looking. It now points at https://git.eeqj.de/api/v1. The version was a hardcoded Makefile constant, VERSION := 1.0.0-rc.1, so every local build claimed to be a release candidate that had never been tagged and did not exist, while git tag -l was empty and internal/globals defaulted to dev. The version now comes from git, via the new script/version: the exact tag with a leading v stripped when HEAD is on one (so a make build and a goreleaser build of the same commit report the same string, and it matches the archive names), otherwise dev-<12-char sha>, with -dirty appended in either case when tracked files are modified. Untracked files are not counted, matching git describe --dirty. goreleaser's snapshot template gets the same treatment: it was {{ incpatch .Version }}-next, which manufactures a release number from the last tag and, with no tags at all, from goreleaser's fabricated v0.0.0. That change had one non-obvious consequence. internal/cli/version.go gated its "this is a development build" notice on the version being exactly "dev", so as soon as untagged builds carried a commit sha the notice would have gone silent and an unreleased binary would have read as a release. The gate is now globals.IsDevVersion, a predicate over a string rather than a comparison against a global so that it can be tested, and it is tested at the boundary that matters: dev-<sha> and its -dirty variant are development builds, 1.0.0-dev and 1.0.0-rc.1 are not. The command writes to cmd.OutOrStdout() so its output can be asserted on at all. Releases now come from CI rather than a workstation: a tag-triggered .gitea/workflows/release.yml, with fetch-depth: 0 because a shallow checkout has no tags and would silently mislabel the release, and with the RELEASE_TOKEN repository secret passed as GITEA_TOKEN (documented in README.md; the runner's automatic token is deliberately not used, since it is not guaranteed to carry release write scope). script/release unsets any GITHUB_TOKEN or GITLAB_TOKEN it finds, because goreleaser picks its forge from whichever token variable is set and refuses to run when it sees more than one -- an unrelated runner token must not get to decide where these artifacts are published. make release and make release-snapshot were the last two Makefile targets that were not shims; they now call script/release and script/release-snapshot, which resolve goreleaser the way script/lint resolves the linter -- a PATH binary is accepted only at the pinned version, never as a silent fallback. script/bootstrap installs it from a sha256-verified GitHub release archive per REPO_POLICIES.md, through a separate script/install-goreleaser: separate because script/bootstrap hard-fails without a usable Docker daemon by design, and the release runner needs goreleaser without needing Docker. dist/ and .tool/ are gitignored and excluded from the Docker build context. Verified by running it: make release-snapshot produces the four linux,darwin x amd64,arm64 archives plus checksums.txt, and the binary from dist/ reports dev-<sha> with the development-build notice. Tag handling was exercised in a throwaway repository; no tag was created here, since that is the owner's call. Signing, SBOM, reproducible builds, shell completions and a man page remain out of scope.
93 lines
3.1 KiB
Bash
Executable File
93 lines
3.1 KiB
Bash
Executable File
#!/bin/sh
|
|
# script/release: build and publish the release artifacts with the
|
|
# pinned goreleaser. Our own extension to scripts-to-rule-them-all.
|
|
#
|
|
# Normally invoked by a tag push through .gitea/workflows/release.yml,
|
|
# not by hand: a release cut from a workstation is a release nobody can
|
|
# reproduce. Any arguments are passed through to `goreleaser release`,
|
|
# which is how script/release-snapshot adds --snapshot.
|
|
set -eu
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
|
|
|
|
# Keep in sync with script/install-goreleaser, which owns the pin.
|
|
GORELEASER_VERSION="2.17.1"
|
|
|
|
goreleaser_version() {
|
|
[ -x "$1" ] || return 0
|
|
"$1" --version 2>/dev/null |
|
|
sed -n 's/^ *GitVersion: *//p' |
|
|
head -n 1
|
|
}
|
|
|
|
# Resolve the goreleaser to run, on the same rule script/lint uses for
|
|
# golangci-lint: a binary on PATH is accepted only when it is exactly
|
|
# the pinned version, because a differently versioned tool would
|
|
# produce a differently built release from the same tag. Anything else
|
|
# comes from .tool/bin, and a missing one is a loud failure naming the
|
|
# script that installs it rather than a silent fallback.
|
|
resolve_goreleaser() {
|
|
path_bin="$(command -v goreleaser || true)"
|
|
if [ -n "$path_bin" ] &&
|
|
[ "$(goreleaser_version "$path_bin")" = "$GORELEASER_VERSION" ]; then
|
|
echo "$path_bin"
|
|
return 0
|
|
fi
|
|
if [ "$(goreleaser_version "$ROOT/.tool/bin/goreleaser")" \
|
|
= "$GORELEASER_VERSION" ]; then
|
|
echo "$ROOT/.tool/bin/goreleaser"
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
main() {
|
|
cd "$ROOT"
|
|
|
|
if ! bin="$(resolve_goreleaser)"; then
|
|
cat >&2 <<EOF
|
|
release: goreleaser $GORELEASER_VERSION is not available.
|
|
|
|
Run script/bootstrap (or script/install-goreleaser directly) to install
|
|
it. A goreleaser already on PATH is used only when it reports exactly
|
|
$GORELEASER_VERSION; any other version is refused rather than used,
|
|
because the released binaries must come from a known build of a known
|
|
tool.
|
|
EOF
|
|
exit 1
|
|
fi
|
|
|
|
snapshot=0
|
|
for arg in "$@"; do
|
|
[ "$arg" = "--snapshot" ] && snapshot=1
|
|
done
|
|
|
|
if [ "$snapshot" -eq 0 ]; then
|
|
# Publishing needs a Gitea token. Check it here so the failure
|
|
# names the secret, rather than after several minutes of
|
|
# cross-compiling.
|
|
if [ -z "${GITEA_TOKEN:-}" ]; then
|
|
cat >&2 <<'EOF'
|
|
release: GITEA_TOKEN is not set.
|
|
|
|
Publishing needs a Gitea API token with write access to this
|
|
repository's releases. In CI it comes from the RELEASE_TOKEN repository
|
|
secret (see .gitea/workflows/release.yml and the Releasing section of
|
|
README.md). To build without publishing, use script/release-snapshot.
|
|
EOF
|
|
exit 1
|
|
fi
|
|
# goreleaser picks its forge from whichever token variable is
|
|
# set and refuses to run when it finds more than one. A CI
|
|
# runner may export a GITHUB_TOKEN of its own; this repo lives
|
|
# on Gitea and releases only there, so an unrelated token must
|
|
# not be allowed to decide where the artifacts are published.
|
|
unset GITHUB_TOKEN GITLAB_TOKEN
|
|
fi
|
|
|
|
exec "$bin" release --clean "$@"
|
|
}
|
|
|
|
main "$@"
|