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 @@ -37,6 +37,6 @@ jobs:
contents: write
id-token: write
attestations: write
uses: simtabi/.github/.github/workflows/go-release.yml@745880bdb9b35a21eb16824cfc995f16e1e82fd5 # simtabi/.github#27
uses: simtabi/.github/.github/workflows/go-release.yml@d648d8998f95a351001979f9c4527badca588bf1 # simtabi/.github#30
with:
tag: ${{ inputs.tag || github.ref_name }}
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### 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.
- The Go module moved into `src/` and is now `github.com/simtabi/osaat/src`. `go.mod`, `cmd/`,
`internal/`, `.goreleaser.yaml` and the build and release scripts live under `src/`;
`scripts/bash-fallback.sh` stays at the root so its published URL keeps working. A release now
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 @@ HOST_OS := $(if $(filter darwin,$(HOST_GOOS)),macos,$(HOST_GOOS))
EXE := $(if $(filter windows,$(HOST_GOOS)),.exe,)
BIN := build/$(HOST_OS)/$(BINARY)$(EXE)

# 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 test-race lint vet fmt tidy cross dist check-build snapshot run clean

help: ## Show this help
Expand All @@ -40,7 +46,7 @@ build: build-local ## Build build/<host-os>/osaat (the same as build-local)

build-local: ## Build build/<host-os>/osaat with the host's Go toolchain
CGO_ENABLED=0 go -C src build -trimpath -ldflags '$(LDFLAGS)' -o ../$(BIN) $(CMD)
@$(MAKE) --no-print-directory check-build
@$(MAKE) --no-print-directory check-build BUILD_LAYOUT_MIN=2

BINDIR ?= $(HOME)/.local/bin

Expand Down Expand Up @@ -70,14 +76,14 @@ lint: ## Run golangci-lint (must be installed)

cross: ## Cross-compile every target in build/targets.txt into build/<os>/
ARCHIVES=0 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 ))

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

snapshot: ## Build the release artifacts into .cache/goreleaser/ without publishing (requires goreleaser)
@command -v goreleaser >/dev/null 2>&1 || { echo "goreleaser not found — install from https://goreleaser.com"; exit 1; }
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 @@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
)
Expand Down Expand Up @@ -115,6 +116,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