all: re-organize build/release

- extract steps into shell files
- fix GoReleaser builds (I hope)
- rely more on the toolchain for version information
- allow non-standard builds, but print a warning

Suggested-by: Marc-Antoine Ruel <maruel@gmail.com>
diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml
index 44494eb..d511538 100644
--- a/.github/workflows/release-build.yml
+++ b/.github/workflows/release-build.yml
@@ -79,7 +79,8 @@
         uses: goreleaser/goreleaser-action@v4
         with:
           version: latest
-          args: release --clean
+          # because our builds aren't hermetic, we must build serially
+          args: release --clean --parallelism 1
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
diff --git a/.goreleaser.yml b/.goreleaser.yml
index 352d675..6e8ae18 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -8,7 +8,7 @@
 
 builds:
   - id: sketch
-    # Note: this build corresponds to 'make outie'; please keep them in sync.
+    # Note: this build corresponds to build/outie.sh; please keep them in sync.
     main: ./cmd/sketch
     binary: sketch
     goos:
@@ -17,16 +17,16 @@
     goarch:
       - amd64
       - arm64
-    tags:
-      - outie
     ldflags:
-      - -X main.version={{.Version}}
-      - -X main.commit={{.Commit}}
-      - -X main.date={{.Date}}
-      - -X main.makefile=goreleaser
+      - -X main.release={{.Version}}
+      - -X main.builtBy=goreleaser
+    env:
+      - SKETCH_VERSION={{.Version}}
+      - BUILT_BY=goreleaser
     hooks:
       pre:
-        - ./.goreleaser/build-target.sh
+        - ./build/webui.sh
+        - ./build/innie.sh
 
 archives:
   - id: releaseArchive
diff --git a/.goreleaser/build-target.sh b/.goreleaser/build-target.sh
deleted file mode 100755
index 37a64dc..0000000
--- a/.goreleaser/build-target.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-set -e
-
-# This script is called by GoReleaser for each target
-# GOOS and GOARCH are set by GoReleaser
-# We prepare the embedded assets, then let GoReleaser do the final go build
-
-echo "Preparing embedded assets for GOOS=$GOOS GOARCH=$GOARCH"
-
-# Build webui assets (only once)
-if [ ! -d "embedded/webui-dist" ]; then
-    echo "Building webui assets..."
-    make webui-assets
-fi
-
-# Build innie binaries (only once)
-if [ ! -f "embedded/sketch-linux/sketch-linux-amd64" ] || [ ! -f "embedded/sketch-linux/sketch-linux-arm64" ]; then
-    echo "Building innie binaries..."
-    make innie
-fi
-
-echo "Assets prepared. GoReleaser will now build the outie binary."
diff --git a/Makefile b/Makefile
index 088cbf0..2d0e134 100644
--- a/Makefile
+++ b/Makefile
@@ -4,35 +4,30 @@
 # 1. Linux binary ("innie") - runs in container, embeds webui assets
 # 2. Native binary ("outie") - runs on user's machine, embeds innie
 
-BUILD_TIME := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
-COMMIT := $(shell git rev-parse HEAD 2>/dev/null || echo "unknown")
-VERSION := $(shell git describe --tags --dirty --always 2>/dev/null || echo "dev")
-LDFLAGS := -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.date=$(BUILD_TIME) -X main.makefile=true
-
-# Support for cross-compilation, used by GoReleaser
+# Allow overriding some env vars, used by GoReleaser
+BUILT_BY ?= make
+SKETCH_VERSION ?=
 GOOS ?= $(shell go env GOOS)
 GOARCH ?= $(shell go env GOARCH)
 
-.PHONY: all clean help
-.PHONY: outie innie
-.PHONY: webui-assets
+export BUILT_BY
+export SKETCH_VERSION
+export GOOS
+export GOARCH
+export LDFLAGS := -X main.builtBy=$(BUILT_BY) -X main.release=$(SKETCH_VERSION)
+
+.PHONY: all clean outie innie webui
 
 all: outie
 
 outie: innie
-	# Note: This incantation is duplicated in .goreleaser.yml; please keep them in sync.
-	GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags="$(LDFLAGS)" -tags=outie -o sketch ./cmd/sketch
+	./build/outie.sh
 
-innie: webui-assets
-	CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -tags=innie -o embedded/sketch-linux/sketch-linux-amd64 ./cmd/sketch
-	CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="$(LDFLAGS)" -tags=innie -o embedded/sketch-linux/sketch-linux-arm64 ./cmd/sketch
+innie: webui
+	./build/innie.sh
 
-webui-assets:
-	rm -rf embedded/webui-dist
-	unset GOOS GOARCH && go run ./cmd/genwebui -- embedded/webui-dist
+webui:
+	./build/webui.sh
 
 clean:
-	@echo "Cleaning build artifacts..."
-	rm -f sketch
-	rm -rf embedded/sketch-linux embedded/webui-dist
-	cd webui && rm -rf node_modules dist
+	./build/clean.sh
diff --git a/build/clean.sh b/build/clean.sh
new file mode 100755
index 0000000..d6117da
--- /dev/null
+++ b/build/clean.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+
+rm -f sketch
+rm -rf embedded/sketch-linux embedded/webui-dist
+rm -rf webui/node_modules webui/dist
\ No newline at end of file
diff --git a/build/innie.sh b/build/innie.sh
new file mode 100755
index 0000000..b353f42
--- /dev/null
+++ b/build/innie.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -e
+
+CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="${LDFLAGS:-}" -tags=innie -o embedded/sketch-linux/sketch-linux-amd64 ./cmd/sketch
+CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="${LDFLAGS:-}" -tags=innie -o embedded/sketch-linux/sketch-linux-arm64 ./cmd/sketch
diff --git a/build/outie.sh b/build/outie.sh
new file mode 100755
index 0000000..f267167
--- /dev/null
+++ b/build/outie.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -e
+
+# Note: This incantation is duplicated in .goreleaser.yml; please keep them in sync.
+go build -ldflags="${LDFLAGS:-}" -tags=outie -o sketch ./cmd/sketch
diff --git a/build/webui.sh b/build/webui.sh
new file mode 100755
index 0000000..0dfc2ed
--- /dev/null
+++ b/build/webui.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -e
+
+rm -rf embedded/webui-dist
+unset GOOS GOARCH && go run ./cmd/genwebui -- embedded/webui-dist
diff --git a/cmd/sketch/main.go b/cmd/sketch/main.go
index 21e569b..9d1d571 100644
--- a/cmd/sketch/main.go
+++ b/cmd/sketch/main.go
@@ -40,11 +40,8 @@
 
 // Version information set by ldflags at build time
 var (
-	version  = "dev"     // version string
-	commit   = "none"    // git commit hash
-	date     = "unknown" // build timestamp
-	builtBy  = "unknown" // who built this binary
-	makefile = ""        // marker indicating a makefile build
+	release = "dev" // release version
+	builtBy = ""    // how this binary got built (makefile, goreleaser)
 )
 
 func main() {
@@ -61,8 +58,11 @@
 	flagArgs := parseCLIFlags()
 
 	// If not built with make, embedded assets will be missing.
-	if makefile == "" {
-		return fmt.Errorf("please use `make` to build sketch")
+	if builtBy == "" {
+		// If your sketch binary isn't working and you are seeing this warning,
+		// it's probably because your build method has stale or missing embedded assets.
+		// See the makefile/GoReleaser configs for how to build.
+		fmt.Fprintln(os.Stderr, "⚠️  not using a recommended build method")
 	}
 
 	// Set up signal handling if -ignoresig flag is set
@@ -71,21 +71,15 @@
 	}
 
 	if flagArgs.version {
-		fmt.Printf("sketch %s\n", version)
-
-		if commit != "none" {
-			fmt.Printf("  commit: %s\n", commit)
-		}
-		if date != "unknown" {
-			fmt.Printf("  built: %s\n", date)
-		}
-		if builtBy != "unknown" {
-			fmt.Printf("  by: %s\n", builtBy)
-		}
-
+		fmt.Printf("sketch %s\n", release)
+		fmt.Printf("\tbuild.system: %s\n", builtBy)
 		bi, ok := debug.ReadBuildInfo()
 		if ok {
-			fmt.Printf("  buildinfo: %s@%v\n", bi.Path, bi.Main.Version)
+			for _, s := range bi.Settings {
+				if strings.HasPrefix(s.Key, "vcs.") {
+					fmt.Printf("\t%s: %v\n", s.Key, s.Value)
+				}
+			}
 		}
 		return nil
 	}