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 @@ -39,7 +39,7 @@ 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 }}
go-version-file: .go-version
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### 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.
- Building probaci needs Go 1.26, and CI runs on it. Go 1.25 has ten standard-library
vulnerabilities fixed only in 1.26, which the web UI's `net/http` server reaches; `golang.org/x/net`
moves to v0.60.0 for the same reason, and gosec to v2.30.0.
Expand Down
14 changes: 10 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,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 run test cover vet fmt lint tidy cross dist check-build \
sync-docs release-check bundle snapshot clean

Expand All @@ -38,7 +44,7 @@ build: build-local ## Build $(BIN) (the same as build-local)

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

install: ## Install probaci into GOBIN from this checkout (go -C src install)
go -C src install -ldflags "$(LDFLAGS)" ./cmd/$(BINARY)
Expand Down Expand Up @@ -67,14 +73,14 @@ tidy: ## go mod tidy

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

# The module in src/ cannot embed files above it, so `probaci docs` reads a copy
# of README.md and docs/ in src/embedded/. TestEmbeddedDocsMatchTheRepository
Expand Down
70 changes: 70 additions & 0 deletions src/internal/release/layout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package release_test
import (
"os"
"path/filepath"
"strconv"
"strings"
"testing"
)
Expand Down Expand Up @@ -60,6 +61,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