Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
contents: write
id-token: write
attestations: write
uses: simtabi/.github/.github/workflows/go-release.yml@941f7b7ecce5dc8af6daedfc7496dd4367c70877 # simtabi/.github#28
uses: simtabi/.github/.github/workflows/go-release.yml@d648d8998f95a351001979f9c4527badca588bf1 # simtabi/.github#30
with:
tag: ${{ inputs.tag || github.ref_name }}
runs-on: macos-latest
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
release download fails.

### Changed
- `release.yml` calls simtabi/.github's shared `go-release.yml` at `d648d89` (simtabi/.github#30), its latest commit, which adds an optional `node-version` input and changes nothing a caller passes.
- `make check-build` asserts how many files it inspected in `build/`, not only that nothing is out of place: `make build` passes a floor of 2 (`targets.txt` and the binary) and `make cross` and `make dist` one per target plus `targets.txt` and `checksums.txt`, through `BUILD_LAYOUT_MIN`, so a check that reads an empty or wrong directory fails instead of passing.
- **Binaries ship on the GitHub release only, built on a version tag or on
request.** A pushed `vX.Y.Z` tag (or `vX.Y.Z-rc.N` pre-release) builds and
publishes the release, and
Expand Down
14 changes: 10 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ BIN := build/$(HOST_OS)/$(BINARY)$(EXE)
# `make install` copies the host build here (may need sudo).
BINDIR ?= /usr/local/bin

# The fewest entries `make check-build` must inspect in build/: the files at its
# root and in each OS folder. Each build passes what it just wrote (the host
# build: targets.txt and the binary; cross: targets.txt, checksums.txt and one
# binary per target), so a check reading an empty or wrong directory fails.
BUILD_LAYOUT_MIN ?= 1

.PHONY: help build build-local install test vet fmt lint cross cross-check dist check-build icons clean

help: ## Show this help
Expand All @@ -43,7 +49,7 @@ build: build-local ## Build $(BIN) for the current OS (the same as build-local)

build-local: ## Build $(BIN) with the host's Go toolchain
go -C src build -trimpath -ldflags "$(LDFLAGS)" -o ../$(BIN) $(MAIN)
@$(MAKE) --no-print-directory check-build
@$(MAKE) --no-print-directory check-build BUILD_LAYOUT_MIN=2

install: build-local ## Build, then copy $(BIN) to $(BINDIR) (default /usr/local/bin; may need sudo)
install -d "$(BINDIR)"
Expand All @@ -63,7 +69,7 @@ lint: ## Run golangci-lint (config in src/.golangci.yml), if installed

cross: ## Cross-compile every target in build/targets.txt into build/<os>/
VERSION=$(VERSION) COMMIT=$(COMMIT) DATE=$(DATE) ./src/scripts/build-all.sh
@$(MAKE) --no-print-directory check-build
@$(MAKE) --no-print-directory check-build BUILD_LAYOUT_MIN=$$(( $$(grep -cvE '^[[:space:]]*(#|$$)' build/targets.txt) + 2 ))

cross-check: ## Compile every package for the cgo-free targets, writing nothing (the former `make cross`)
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go -C src build ./...
Expand All @@ -72,10 +78,10 @@ cross-check: ## Compile every package for the cgo-free targets, writing nothing

dist: ## cross, plus .tar.gz/.zip archives in build/archives/ (publishes nothing)
ARCHIVES=1 VERSION=$(VERSION) COMMIT=$(COMMIT) DATE=$(DATE) ./src/scripts/build-all.sh
@$(MAKE) --no-print-directory check-build
@$(MAKE) --no-print-directory check-build BUILD_LAYOUT_MIN=$$(( $$(grep -cvE '^[[:space:]]*(#|$$)' build/targets.txt) + 2 ))

check-build: ## Fail when build/ holds anything but the OS folders, inputs, checksums.txt and archives/
go -C src test ./internal/release -run 'TestBuildLayout$$' -count=1
BUILD_LAYOUT_MIN=$(BUILD_LAYOUT_MIN) go -C src test ./internal/release -run 'TestBuildLayout$$' -count=1

icons: ## Regenerate brand PNGs + Windows resource (.syso) from assets/vigil.svg
cd src/tools/iconz && go run .
Expand Down
70 changes: 70 additions & 0 deletions src/internal/release/workflow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
)
Expand Down Expand Up @@ -65,6 +66,75 @@ func TestBuildLayout(t *testing.T) {
if problems := buildLayoutProblems(t, dir); len(problems) > 0 {
t.Errorf("build/ breaks the layout (one folder per OS, see the Makefile):\n %s", strings.Join(problems, "\n "))
}
n, floor := buildEntries(t, dir), buildFloor(t)
if n < floor {
t.Errorf("inspected %d entries in build/, expected at least %d (BUILD_LAYOUT_MIN): the check read less than the build wrote", n, floor)
}
t.Logf("inspected %d entries in build/ (floor %d)", n, floor)
}

// buildFloor is the fewest entries build/ must hold: BUILD_LAYOUT_MIN, which the
// Makefile sets to what the build that just ran wrote, else 1 (targets.txt).
func buildFloor(t *testing.T) int {
t.Helper()
raw := os.Getenv("BUILD_LAYOUT_MIN")
if raw == "" {
return 1
}
n, err := strconv.Atoi(raw)
if err != nil || n < 1 {
t.Fatalf("BUILD_LAYOUT_MIN=%q is not a positive number", raw)
}
return n
}

// buildEntries counts what the layout check inspected: the files at the root of
// build/ and the files in each OS folder. Folders themselves do not count.
func buildEntries(t *testing.T, dir string) int {
t.Helper()
entries, err := os.ReadDir(dir)
if err != nil {
t.Fatalf("reading %s: %v", dir, err)
}
n := 0
for _, e := range entries {
switch {
case e.Name() == ".DS_Store":
case !e.IsDir():
n++
case osFolders[e.Name()]:
inner, err := os.ReadDir(filepath.Join(dir, e.Name()))
if err != nil {
t.Fatal(err)
}
for _, f := range inner {
if !f.IsDir() && f.Name() != ".DS_Store" {
n++
}
}
}
}
return n
}

// The count is what the floor stands on, so it has to count what is there.
func TestBuildEntriesCountsWhatItInspected(t *testing.T) {
dir := t.TempDir()
for _, f := range []string{
"targets.txt", "checksums.txt", ".DS_Store",
"macos/app_macos_arm64", "linux/app_linux_amd64", "archives/app_linux_amd64.tar.gz",
} {
p := filepath.Join(dir, filepath.FromSlash(f))
if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(p, nil, 0o644); err != nil {
t.Fatal(err)
}
}
if got := buildEntries(t, dir); got != 5 {
t.Errorf("buildEntries counted %d, want 5", got)
}
}

// The layout check has to be able to fail: each of these trees breaks it.
Expand Down
Loading