make release: build a stamped linux-arm64 binary into releases/
Releases are published from a tag now, so the stamp a binary reports from GET /api/version should be that tag. git describe --tags --always resolves to it when the build sits on one and falls back to the short sha it used to carry when it does not, so nothing is lost. The artifact is named releases/<app>-linux-arm64 because amadan release create uploads a file under its basename, and a release's files cannot be deleted once they exist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4 files changed,
+66
−6
.gitignore+1 −0Makefile+23 −0cmd/docs/release_test.go+20 −4scripts/build-release.sh+22 −2
CI passed — run details
diff --git a/.gitignore b/.gitignore| index d2d15e6..1334a9c 100644 |
| --- a/.gitignore |
| +++ b/.gitignore |
| @@ -48,3 +48,4 @@ Thumbs.db |
| # per-developer overrides are machine-local state, not decisions. |
| .impeccable/hook.cache.json |
| .impeccable/config.local.json |
| +/releases/ |
diff --git a/Makefile b/Makefile| new file mode 100644 |
| index 0000000..2e12f11 |
| --- /dev/null |
| +++ b/Makefile |
| @@ -0,0 +1,23 @@ |
| +# docs is script-driven, not make-driven: the gate is .amadan/ci.d/10-gate |
| +# and the release build is scripts/build-release.sh, which builds from a |
| +# `git archive` of the revision rather than from the working tree — so a |
| +# release can never pick up an uncommitted edit, and there is no |
| +# dirty-tree case to refuse. |
| +# |
| +# This Makefile exists so that `make release` means the same thing in |
| +# docs as it does in calendar, home, meet, memoria, sheets and slides. |
| +# It delegates rather than restating the build: two definitions of how |
| +# docs ships would drift, and the one in the script is the one CI uses. |
| + |
| +APP := docs |
| +RELEASE_GOOS ?= linux |
| +RELEASE_GOARCH ?= arm64 |
| +RELEASE_BIN := releases/$(APP)-$(RELEASE_GOOS)-$(RELEASE_GOARCH) |
| + |
| +.PHONY: release |
| + |
| +release: |
| + @mkdir -p releases |
| + GOOS=$(RELEASE_GOOS) GOARCH=$(RELEASE_GOARCH) ./scripts/build-release.sh $(RELEASE_BIN) $(REVISION) |
| + @echo "publish it with:" |
| + @echo " amadan release create -repo oficina/$(APP) -notes-file NOTES.md <tag> $(RELEASE_BIN)" |
diff --git a/cmd/docs/release_test.go b/cmd/docs/release_test.go| index f3d594f..a4e079d 100644 |
| --- a/cmd/docs/release_test.go |
| +++ b/cmd/docs/release_test.go |
| @@ -15,15 +15,31 @@ import ( |
| "time" |
| ) |
| -func TestReleaseBinaryReportsItsSourceCommit(t *testing.T) { |
| +// TestReleaseBinaryReportsItsVersion builds the real release artifact and |
| +// asks the running process what it is. The failure this guards against is |
| +// a release that answers "dev": rastrillo's BuildVersion default, which |
| +// the platform's deploy check compares against the release it believes it |
| +// adopted — so an unstamped binary makes that check structurally unable to |
| +// fail while still reporting green. |
| +// |
| +// The expected value is whatever scripts/build-release.sh stamps, computed |
| +// the same way it computes it: the tag when this revision carries one, and |
| +// the abbreviated sha when it does not. Asserting the full sha instead |
| +// would pass only for untagged builds, which is the case that matters |
| +// least. |
| +func TestReleaseBinaryReportsItsVersion(t *testing.T) { |
| root, err := filepath.Abs("../..") |
| if err != nil { |
| t.Fatal(err) |
| } |
| - commit, err := exec.Command("git", "-C", root, "rev-parse", "HEAD").Output() |
| + described, err := exec.Command("git", "-C", root, "describe", "--tags", "--always", "HEAD").Output() |
| if err != nil { |
| t.Fatal(err) |
| } |
| + want := strings.TrimSpace(string(described)) |
| + if want == "" || want == "dev" { |
| + t.Fatalf("git describe gave %q, which is not a version this test can check for", want) |
| + } |
| dir := t.TempDir() |
| binary := filepath.Join(dir, "docs") |
| ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) |
| @@ -64,8 +80,8 @@ func TestReleaseBinaryReportsItsSourceCommit(t *testing.T) { |
| if readErr != nil { |
| t.Fatal(readErr) |
| } |
| - if response.StatusCode != http.StatusOK || strings.TrimSpace(string(body)) != strings.TrimSpace(string(commit)) { |
| - t.Fatalf("version response %d %q; want commit %q", response.StatusCode, body, strings.TrimSpace(string(commit))) |
| + if response.StatusCode != http.StatusOK || strings.TrimSpace(string(body)) != want { |
| + t.Fatalf("version response %d %q; want %q", response.StatusCode, body, want) |
| } |
| return |
| } |
diff --git a/scripts/build-release.sh b/scripts/build-release.sh| index 1869b7e..5ecafa1 100755 |
| --- a/scripts/build-release.sh |
| +++ b/scripts/build-release.sh |
| @@ -4,14 +4,34 @@ export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}amadan.net/oficina/estilo.git" |
| test "$#" -ge 1 |
| test "$#" -le 2 |
| -docs_output=$(realpath -m -- "$1") |
| +# The output path has to be absolute before the cd into the archive |
| +# below, and it has to resolve even though the file does not exist yet. |
| +# `realpath -m` does that on Linux, where this has always run, but BSD |
| +# realpath has no -m, so it fails on a Mac. dirname + pwd is the same |
| +# thing in POSIX, once the directory is there to cd into. |
| +docs_output_dir=$(dirname -- "$1") |
| +mkdir -p -- "$docs_output_dir" |
| +docs_output=$(cd -- "$docs_output_dir" && pwd)/$(basename -- "$1") |
| cd "$(dirname "$0")/.." |
| docs_revision=$(git rev-parse --verify "${2:-HEAD}^{commit}") |
| +# What the built binary answers GET /api/version with. rastrillo's |
| +# BuildVersion defaults to "dev", and the platform's deploy check |
| +# compares that field against the release it believes it adopted — so an |
| +# unstamped binary makes the check incapable of failing. |
| +# |
| +# git describe --tags --always resolves to the tag when the revision |
| +# carries one and to the same abbreviated sha when it does not, so a |
| +# published release reports the name it was published under and an |
| +# ordinary build still says something true about itself. Name it |
| +# yourself for a one-off: DOCS_VERSION=v0.1.0-wip scripts/build-release.sh out |
| +docs_version="${DOCS_VERSION:-$(git describe --tags --always "$docs_revision")}" |
| docs_source=$(mktemp -d) |
| trap 'rm -rf -- "$docs_source"' EXIT |
| git archive "$docs_revision" | tar -x -C "$docs_source" |
| cd "$docs_source" |
| CGO_ENABLED=0 GOOS="${GOOS:-linux}" GOARCH="${GOARCH:-arm64}" \ |
| go build -mod=readonly -trimpath \ |
| - -ldflags "-X github.com/carlosframework/rastrillo.BuildVersion=$docs_revision" \ |
| + -ldflags "-s -w -X github.com/carlosframework/rastrillo.BuildVersion=$docs_version" \ |
| -o "$docs_output" ./cmd/docs |
| + |
| +echo "built $docs_output reporting version $docs_version" |