mirror of
https://github.com/gastownhall/beads.git
synced 2026-09-14 20:17:24 +08:00
935afe2466
* fix(examples): tidy both example modules and build them in CI
Both Go modules under examples/ fail a plain `go build` on current main:
go: updates to go.mod needed; to update it:
go mod tidy
They are separate modules that reach the parent through
`replace github.com/steveyegge/beads => ../..`, so their go.mod and go.sum
record the parent's entire dependency graph. Every root dependency change
therefore invalidates them — and nothing in CI ever compiled them, so the drift
accumulated silently. examples/ is the first code a new user copies, which makes
this a bad first five minutes rather than a cosmetic wart.
Two parts:
1. `go mod tidy` in examples/bd-example-extension-go and examples/library-usage.
The extension example carries most of the churn (~1.6k lines of go.sum),
which is inherent to recording the parent graph through the replace directive
and is why an earlier fix (#4942, the Go 1.26.5 bump) deliberately left it
out. Both modules now build clean with the project's canonical
`-tags gms_pure_go`.
2. scripts/build-examples.sh plus a `build-examples` job in the PR workflow, so
the same drift cannot recur unnoticed. The script discovers example modules
from `git ls-files 'examples/*/go.mod'` (no hardcoded list), sources
.buildflags for the canonical CGO/tag settings, builds into a scratch
directory so it leaves no untracked binaries, reports every failing module
rather than stopping at the first, and prints the exact `go mod tidy`
command to run.
The job is deliberately NOT added to ci-gate's required list. Because of the
replace directive it would fail on any root go.mod change not mirrored into the
examples, so making it blocking imposes a "tidy the examples too" step on every
dependency bump. That is a maintainer call about contributor friction; the job
is visible on the checks list either way, and pr.yml records how to promote it.
Verified locally: both modules build with -tags gms_pure_go; the script exits 1
and names the module when an example go.mod is reverted to its pre-tidy state;
scripts/check-build-tags.sh stays clean (the script sources .buildflags).
Agent-Signature: claude-opus-5-high on behalf of matt wilkie
* fix(examples): address dual-vendor review — vet not build, advisory job, portability
Reviewed by a claude reviewer and scripts/codex-agent reviewer (gpt-5.6-sol).
Four fixes, two of them correctness.
1. `go build -o <dir>/ ./...` was a FALSE GREEN: with -o naming a directory, Go
compiles only the MAIN packages and silently skips every library package.
Reproduced in an isolated module with a good main package plus a library
package containing a type error — `go build -o dir/ ./...` exits 0 while
`go build ./...` and `go vet ./...` both exit 1. The mirror-image bug: a
library-only example module fails `go build` with "no main packages to
build", a false red. Switched to `go vet ./...`, which type-checks every
package INCLUDING test files (examples/library-usage/main_test.go exercises
a lot of live API, and `go mod tidy` counts its imports, so a build that
never compiles it left part of the recorded graph unverified), writes no
artifacts, and still fails on the stale-go.mod condition this exists for.
That also deletes the mktemp/trap/scratch-dir machinery entirely.
2. "Not in ci-gate" is NOT "non-blocking", which the previous comment claimed.
Verified in source: pr-preflight.sh gates on every FAILURE in the raw
statusCheckRollup and calls block(), and pr-babysit requires all rollup
entries SUCCESS/NEUTRAL/SKIPPED before merging. Both ignore ci-gate
membership. So the previous state was the one posture that stalls the merge
patrol repo-wide after any un-mirrored dependency bump while advertising
itself as optional. The job is now continue-on-error: true — genuinely
advisory — and the comment records how to promote it to a real gate. Whether
it SHOULD be a gate remains the maintainers' call.
3. Portability and robustness in the script, all reproduced by the reviewers:
- `mapfile` does not exist in bash 3.2 (stock macOS) and `xargs -r` is
GNU-only, so the advertised local check could not run on macOS. Replaced
with a NUL-delimited read loop, which also fixes module paths containing
whitespace (`git ls-files | xargs -n1 dirname` turned "examples/has
space/go.mod" into two bogus entries).
- `source ./.buildflags` was unguarded under `set -uo pipefail`; a failure
continued with GOFLAGS unset and would type-check the ICU path while
check-build-tags.sh still passed, since that only greps for the literal
string. Now a hard exit.
- Finding zero modules exited 0. A job that checks nothing must not report
success; it is now an error.
Verified: both modules vet clean; reverting an example go.mod to its pre-tidy
state still exits 1 with the exact `go mod tidy` command; a broken library
package now fails where it previously passed; check-build-tags.sh clean
(97 files); shellcheck clean.
Agent-Signature: claude-opus-5-high on behalf of matt wilkie
* fix(examples): drop GNU-only sort -z from the module discovery pipeline
BSD sort has no -z, so on stock macOS — the exact platform the Bash-3.2
compatibility block targets — the sort stage emptied the pipeline and the
script exited claiming 'found no example modules'. git ls-files output is
already sorted, so the stage bought nothing. Found by cross-vendor review
(codex gpt-5.6-sol) of this branch.
Agent-Signature: claude-fable-5-high on behalf of maphew
* fix(ci,examples): disable setup-go cache in build-examples; re-tidy examples after merge
TestGoCacheOwnershipTopology requires every setup-go step in pr.yml to set
cache: false (caching is owned by explicit restore/save steps); the new
build-examples job predates that policy landing on main. Also re-run
go mod tidy in both example modules so their recorded dependency graphs
match the merged main — exactly the drift this PR's CI job exists to catch.
Agent-Signature: claude-fable-5-high on behalf of maphew
* ci(examples): bound build-examples with timeout-minutes: 10
continue-on-error keeps a red result advisory, but a hung job at the
6-hour default timeout holds the check pending, stalling merge-lane
consumers the advisory posture was meant to protect. Matches
pr-preflight-platforms' bound.
Agent-Signature: claude-fable-5-high on behalf of maphew
* ci(examples): make the advisory posture real - step-level continue-on-error
Job-level continue-on-error still reports the check run as FAILURE in the
PR rollup (only the workflow-run conclusion flips), and pr-preflight /
pr-babysit gate on per-check-run conclusions - so the job as written was
de facto blocking while advertising itself advisory. Follow the cygwin-leg
precedent: continue-on-error on the build step, outcome-guarded ::warning
annotation on failure. Also restore the repo-wide Go module cache (the
examples' graph is the parent's via the replace directive) so the job
stops cold-downloading the full dependency graph on every PR.
Agent-Signature: claude-fable-5-high on behalf of maphew
* test: register build-examples' module-cache restore in the cache topology registry
TestGoCacheOwnershipTopology keeps an explicit inventory of every cache
step per job; the advisory-lane fix added a restore step to build-examples
without registering it, so the whole scripts package went red on this
head. Register the job (inventory, managed map, ordering, setup-go id)
and drop the step's unused 'restore-cache' id — the registry compares
ids, and nothing references it.
Agent-Signature: claude-fable-5-high on behalf of maphew
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019CwiLhLbAGZdJtKYT76CPp
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
96 lines
4.0 KiB
Bash
Executable File
96 lines
4.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# build-examples.sh — type-check every Go module under examples/.
|
|
#
|
|
# The example modules are separate Go modules that reach the parent through a
|
|
# `replace github.com/steveyegge/beads => ../..` directive, so their go.mod and
|
|
# go.sum record the parent's full dependency graph. Nothing in CI compiled them,
|
|
# which meant they went stale silently: on 2026-08-01 both
|
|
# examples/bd-example-extension-go and examples/library-usage failed a plain
|
|
# `go build` on main with "updates to go.mod needed; to update it: go mod tidy".
|
|
# Examples are the first code a new user copies, so a broken one is a bad first
|
|
# five minutes.
|
|
#
|
|
# Usage: scripts/build-examples.sh
|
|
#
|
|
# Exits non-zero listing every module that failed, so one broken example does
|
|
# not hide another.
|
|
#
|
|
# Why `go vet` and not `go build`: with `-o <dir>/`, `go build ./...` compiles
|
|
# only the MAIN packages and silently skips every library package — a module
|
|
# with a broken internal package would pass. Without `-o` it litters the tree
|
|
# with binaries. `go vet ./...` type-checks every package INCLUDING test files
|
|
# (examples/library-usage/main_test.go exercises a lot of live API, and
|
|
# `go mod tidy` counts its imports, so a build that never compiles it leaves
|
|
# part of the recorded dependency graph unverified), writes no artifacts, and
|
|
# still fails on the stale-go.mod condition this script exists for. It also
|
|
# avoids `go build`'s "no main packages to build" false failure on a
|
|
# library-only example module.
|
|
|
|
set -uo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" || exit 1
|
|
cd "$REPO_ROOT" || exit 1
|
|
|
|
# Canonical build settings (CGO_ENABLED, -tags=gms_pure_go via GOFLAGS). The
|
|
# examples link the same go-mysql-server that pulls in ICU under cgo, so they
|
|
# need the project tag exactly as the main build does. Guarded: with no errexit,
|
|
# a failed source would otherwise continue with GOFLAGS unset and silently
|
|
# type-check the ICU path instead, while check-build-tags.sh still passes (it
|
|
# only greps for the literal string `.buildflags`).
|
|
# shellcheck source=/dev/null
|
|
source ./.buildflags || { echo "build-examples: cannot source .buildflags" >&2; exit 1; }
|
|
|
|
# Bash 3.2 compatible (stock macOS ships 3.2, which has no `mapfile`), and no
|
|
# GNU-only `xargs -r` or `sort -z` (BSD sort has no -z; a sort stage would have
|
|
# emptied the pipeline on macOS — and `git ls-files` output is already sorted).
|
|
# NUL-delimited so a path containing whitespace cannot be split —
|
|
# `git ls-files | xargs -n1 dirname` turned "examples/has space/go.mod" into
|
|
# two bogus entries.
|
|
modules=()
|
|
while IFS= read -r -d '' f; do
|
|
modules+=("$(dirname "$f")")
|
|
done < <(git ls-files -z 'examples/*/go.mod')
|
|
|
|
if [[ "${#modules[@]}" -eq 0 ]]; then
|
|
# Not a pass. Either examples/ was restructured, or this is running outside
|
|
# a git checkout (a release tarball), and a job that compiles nothing must
|
|
# not report success.
|
|
echo "build-examples: found no example modules (git ls-files 'examples/*/go.mod' was empty)" >&2
|
|
echo "build-examples: refusing to report success having checked nothing" >&2
|
|
exit 1
|
|
fi
|
|
|
|
failed=()
|
|
for mod in "${modules[@]}"; do
|
|
echo "==> checking $mod"
|
|
if ( cd "$mod" && go vet ./... ); then
|
|
echo " ok"
|
|
else
|
|
echo " FAILED" >&2
|
|
failed+=("$mod")
|
|
fi
|
|
done
|
|
|
|
if [[ "${#failed[@]}" -eq 0 ]]; then
|
|
echo "build-examples: ${#modules[@]} example module(s) type-check cleanly"
|
|
exit 0
|
|
fi
|
|
|
|
{
|
|
echo
|
|
echo "build-examples: ${#failed[@]} of ${#modules[@]} example module(s) failed:"
|
|
printf ' %s\n' "${failed[@]}"
|
|
echo
|
|
echo "If the error is \"updates to go.mod needed\", the example's recorded"
|
|
echo "dependency graph has drifted from the parent module — usually because a"
|
|
echo "change to the root go.mod was not mirrored into the examples. Fix with:"
|
|
echo
|
|
for mod in "${failed[@]}"; do
|
|
echo " (cd $mod && go mod tidy)"
|
|
done
|
|
echo
|
|
echo "then commit the resulting go.mod/go.sum changes."
|
|
} >&2
|
|
exit 1
|