diff --git a/BUILD.md b/BUILD.md index 4092101..47f0b3a 100644 --- a/BUILD.md +++ b/BUILD.md @@ -7,8 +7,10 @@ are in [README.md](README.md). ## Requirements - Go 1.24 or later -- PowerShell for the supplied Windows build script, or a POSIX shell for manual - cross-compilation +- PowerShell for the supplied Windows build script, or GNU Make and a POSIX + shell for the Make targets +- The Gitea `tea` CLI, authenticated for the repository, when publishing a + release - Windows 11 for runtime integration testing The project uses pure Go and sets `CGO_ENABLED=0`. No C compiler, Windows SDK, @@ -65,6 +67,18 @@ startup failures in detached mode are shown with `MessageBoxW`. ## Cross-compiling +The standard x64 build can be produced from the repository root with: + +```sh +make build +``` + +This runs the tests, cross-compiles `dist/fancywin.exe`, and copies the example +configuration to `dist/fancywin.yaml`. Override `GOARCH` for another Windows +architecture, for example `make build GOARCH=arm64`. + +The equivalent commands are shown below for environments without GNU Make. + Build Windows x64 from Linux, macOS, or another Go-supported host: ```sh @@ -287,16 +301,30 @@ state, UI surfaces, or lower-level input handling. ## Release preparation -For a distributable release: +The release version is read from the `version` constant in +`cmd/fancywin/main.go`. After committing and pushing that version, publish it +with: + +```sh +make release +``` + +The target performs a fresh x64 build, refuses to publish from a dirty or +unpushed working tree, and creates a Gitea release and corresponding `vVERSION` +tag through the authenticated `tea` CLI. The executable and its SHA-256 checksum +are attached to the release; the YAML is not published, and users obtain the +documented example configuration from the repository. `REMOTE` defaults to +`origin`, and `GOARCH` defaults to `amd64`; either can be overridden on the +command line. + +For a distributable release, ensure that you have also: 1. Run all tests, vet, both architecture builds, and the Windows smoke-test checklist. 2. Update the `version` constant in `cmd/fancywin/main.go`. -3. Build x64 and ARM64 artifacts with `CGO_ENABLED=0`, `-trimpath`, and - `-ldflags='-s -w'`. -4. Include an example renamed to `fancywin.yaml` beside each executable. -5. Record SHA-256 hashes with `Get-FileHash` or `sha256sum`. -6. Code-sign public binaries if a trusted signing certificate and release +3. Build and test ARM64 separately when it is part of the release scope. +4. Complete the Windows smoke-test checklist. +5. Code-sign public binaries if a trusted signing certificate and release process are available. Signing is not required for portability, but unsigned downloads may receive stronger Microsoft Defender SmartScreen warnings. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..71ec9de --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +SHELL := /bin/sh + +GO ?= go +TEA ?= tea +GOARCH ?= amd64 +REMOTE ?= origin + +VERSION := $(shell sed -n 's/^const version = "\([^"]*\)".*/\1/p' cmd/fancywin/main.go) +HOST_GOOS := $(shell $(GO) env GOHOSTOS) +HOST_GOARCH := $(shell $(GO) env GOHOSTARCH) +TAG := v$(VERSION) +BINARY := dist/fancywin.exe +CONFIG := dist/fancywin.yaml +CHECKSUM := $(BINARY).sha256 + +.PHONY: build release + +build: + @test -n "$(VERSION)" || { echo "Could not read the FancyWin version" >&2; exit 1; } + mkdir -p dist + GOOS=$(HOST_GOOS) GOARCH=$(HOST_GOARCH) $(GO) test -buildvcs=false ./... + CGO_ENABLED=0 GOOS=windows GOARCH=$(GOARCH) $(GO) build \ + -buildvcs=false \ + -trimpath \ + -ldflags='-s -w' \ + -o $(BINARY) \ + ./cmd/fancywin + cp fancywin.example.yaml $(CONFIG) + @echo "Built $(BINARY) for windows/$(GOARCH) (FancyWin $(VERSION))" + +release: build + @command -v $(TEA) >/dev/null 2>&1 || { echo "The Gitea tea CLI is required for releases" >&2; exit 1; } + @test -z "$$(git status --porcelain)" || { echo "Refusing to release from a dirty working tree" >&2; exit 1; } + @test "$$(git rev-parse HEAD)" = "$$(git rev-parse '@{upstream}')" || { echo "Refusing to release: HEAD has not been pushed to its upstream branch" >&2; exit 1; } + @! git rev-parse --verify --quiet "refs/tags/$(TAG)" >/dev/null || { echo "Tag $(TAG) already exists locally" >&2; exit 1; } + sha256sum $(BINARY) > $(CHECKSUM) + $(TEA) releases create \ + --remote $(REMOTE) \ + --tag $(TAG) \ + --target "$$(git rev-parse HEAD)" \ + --title "FancyWin $(VERSION)" \ + --note "Portable FancyWin $(VERSION) build for Windows $(GOARCH)." \ + --asset $(BINARY) \ + --asset $(CHECKSUM) + @echo "Released FancyWin $(VERSION) as $(TAG)"