Files
b54e665295 MUL-7280: ci(backend): cache race builds per commit and shard pkg/agent onto its own runner (#8302)
* MUL-7280: ci(backend): cache race builds per commit and shard pkg/agent onto its own runner

backend-tests never warmed its Go build cache: setup-go shares one go.sum-keyed entry across every Linux job, go-vulnerability-scan saved a 95 MB one first, and the test job race-compiled the whole module from scratch on every run. Own the caches explicitly (modules by go.sum, race build objects by commit SHA with prefix fallback, published from main only), compile build and vet in race mode so the objects are shared, and pin -count=1 so a rolling cache cannot replay results against a changed schema.

pkg/agent ran as a 139s serial tail at -parallel 2; it is timer-bound and reads no database, so it moves to its own runner without service containers. test-go.sh gains --only regular|agent for that split; the default still runs both halves.

Co-authored-by: multica-agent <[email protected]>

* MUL-7280: ci(backend): make backend-tests the sole Go cache writer and key build objects by image

backend-agent-tests restored the module cache through the combined actions/cache action with the same go.sum key as backend-tests. Being faster, it saved first with only pkg/agent's slice of the module graph, and backend-tests then failed to reserve the immutable key: the same first-writer-wins problem this change set out to fix. The agent job now restores both caches read-only, leaving backend-tests as the only writer.

The build-cache key also gains ImageOS. Cached cgo and race objects are linked against the runner image's glibc, so an entry must not cross an Ubuntu release when ubuntu-latest rolls forward; setup-go keys on ImageOS for the same reason (actions/setup-go#368). ImageOS is a runner environment variable rather than a context value, so each job resolves the key prefix once in a shell step and the restore and save steps reuse it.

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: J <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-09-11 15:57:26 +08:00

103 lines
2.7 KiB
Bash

#!/usr/bin/env bash
set -eu
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
TEST_DIR=$(mktemp -d "${TMPDIR:-/tmp}/multica-test-go.XXXXXX")
BIN_DIR="$TEST_DIR/bin"
CALLS_FILE="$TEST_DIR/go-calls.log"
OUTPUT_FILE="$TEST_DIR/output.log"
cleanup() {
rm -rf "$TEST_DIR"
}
trap cleanup EXIT
mkdir -p "$BIN_DIR"
export MULTICA_TEST_GO_CALLS="$CALLS_FILE"
: >"$CALLS_FILE"
cat >"$BIN_DIR/go" <<'FAKE'
#!/usr/bin/env bash
set -eu
case "${1:-}" in
list)
if [ "$#" -ne 2 ] || [ "$2" != "./..." ]; then
echo "unexpected go list arguments: $*" >&2
exit 2
fi
printf '%s\n' \
github.com/multica-ai/multica/server \
github.com/multica-ai/multica/server/internal/daemon \
github.com/multica-ai/multica/server/pkg/agent \
github.com/multica-ai/multica/server/pkg/agent/internal/testutil
;;
test)
printf '%s\n' "$*" >>"$MULTICA_TEST_GO_CALLS"
;;
*)
echo "unexpected go command: $*" >&2
exit 2
;;
esac
FAKE
chmod 755 "$BIN_DIR/go"
regular_call='test -race github.com/multica-ai/multica/server github.com/multica-ai/multica/server/internal/daemon'
agent_call='test -race -p 2 -parallel 2 ./pkg/agent/...'
# $1: case label; $2: expected go calls, one per line. Clears the log after.
expect_calls() {
actual_calls=$(cat "$CALLS_FILE")
if [ "$actual_calls" != "$2" ]; then
echo "$1: unexpected go test calls:" >&2
printf '%s\n' "$actual_calls" >&2
exit 1
fi
: >"$CALLS_FILE"
}
# $1: case label; the rest are arguments for test-go.sh. Asserts the usage
# exit status, the usage line, and that go was never invoked.
expect_usage_failure() {
label=$1
shift
set +e
PATH="$BIN_DIR:$PATH" bash "$SCRIPT_DIR/test-go.sh" "$@" >"$OUTPUT_FILE" 2>&1
status=$?
set -e
if [ "$status" -ne 2 ]; then
echo "$label returned status $status, want 2" >&2
cat "$OUTPUT_FILE" >&2
exit 1
fi
if [ -s "$CALLS_FILE" ]; then
echo "$label invoked go:" >&2
cat "$CALLS_FILE" >&2
exit 1
fi
if ! grep -q '^usage: .*test-go.sh \[--race\] \[--only regular|agent\]$' "$OUTPUT_FILE"; then
echo "$label did not print usage" >&2
cat "$OUTPUT_FILE" >&2
exit 1
fi
}
PATH="$BIN_DIR:$PATH" bash "$SCRIPT_DIR/test-go.sh" --race
expect_calls "--race" "$regular_call
$agent_call"
PATH="$BIN_DIR:$PATH" bash "$SCRIPT_DIR/test-go.sh" --race --only regular
expect_calls "--only regular" "$regular_call"
# Option order must not matter: CI spells it one way, humans another.
PATH="$BIN_DIR:$PATH" bash "$SCRIPT_DIR/test-go.sh" --only agent --race
expect_calls "--only agent" "$agent_call"
expect_usage_failure "unknown option" --unknown
expect_usage_failure "unknown --only scope" --only everything
expect_usage_failure "missing --only scope" --only
echo "test-go.test.sh: PASS"