Files
blocker/scripts/size-check.sh
SellPL 004479a032 ci: add ci.sh, size gate, and shared script helpers
`scripts/ci.sh` is the source of truth for what must pass before a PR is
mergeable. CLAUDE.md section 10.4 requires the Gitea workflow to run the same
checks; the workflow calls this script rather than reimplementing the steps, so
the two cannot drift apart.

Runs: cargo fmt --check, clippy -D warnings, cargo test, the section 5 size
gate, Android lint/assembleRelease, and a gitleaks scan.

Verified behaviour, rather than assumed:

- **Failures really fail.** Injected a syntax error into `core/src/lib.rs` and
  confirmed `ci.sh --keep-going` exits 1 and names all four dependent checks;
  then restored the file and confirmed a clean exit 0. `--keep-going` captures
  failures per step in a subshell instead of letting `set -e` abort the run, so
  the summary records what already passed.
- **Skips are never passes.** Android and gitleaks both skip on this machine
  (no SDK, no gitleaks), and each SKIP names what was skipped and why, with the
  summary listing them separately from the passes. A skip that reads as a green
  tick is how CI quietly stops testing anything.
- **Runs from any CWD.** `cargo` resolves a workspace from the CWD, not from
  the script's location, so `ci.sh` cds to the repo root. Verified by running it
  from `/tmp`.
- **`cargo` is on PATH.** rustup adds `~/.cargo/bin` via `~/.bashrc`, which a
  non-interactive shell does not read — so a script invoked from CI or by
  absolute path failed with "command not found" until `lib.sh` prepends it.
- **Size gate resolves absolute paths.** `CARGO_TARGET_DIR` is absolute by
  default, so joining it onto the repo root produced a doubled path; the default
  is now only joined when relative.

Build artifacts go to `$HOME/.blocker-target`, outside the repo, so `target/`
never appears in `git status` and can be deleted in one command.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-01 15:16:58 +03:00

44 lines
1.8 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Size gate: fail if the stripped release daemon exceeds the section 5 budget.
#
# Usage: size-check.sh [binary-path] [max-bytes]
#
# Kept separate from ci.sh so the Gitea workflow can run it as its own step
# against an artifact it has already built, rather than rebuilding.
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/lib.sh"
# Default binary path. CARGO_TARGET_DIR may be absolute (the usual case — it
# points outside the repo) or relative, so it must not be blindly joined onto
# REPO_ROOT: doing that turns "/home/sp/.blocker-target/..." into
# "/home/sp/blocker//home/sp/.blocker-target/...".
DEFAULT_TARGET="${CARGO_TARGET_DIR:-${HOME}/.blocker-target}"
if [[ "${DEFAULT_TARGET}" = /* ]]; then
DEFAULT_BINARY="${DEFAULT_TARGET}/release/blocker-daemon"
else
DEFAULT_BINARY="${REPO_ROOT}/${DEFAULT_TARGET}/release/blocker-daemon"
fi
BINARY="${1:-${DEFAULT_BINARY}}"
MAX_BYTES="${2:-${MAX_DAEMON_BYTES}}"
# Normalise so the message shows the real path rather than a doubled prefix.
BINARY="$(cd -- "$(dirname -- "${BINARY}")" 2>/dev/null && pwd)/$(basename -- "${BINARY}")" \
|| die "binary not found: ${BINARY}"
SIZE="$(stat -c %s "${BINARY}" 2>/dev/null || stat -f %z "${BINARY}")"
# Human-readable, using 1024-based units to match how the budget is written.
printf 'binary : %s\n' "${BINARY}"
printf 'size : %s bytes (%d.%03d MB)\n' \
"${SIZE}" "$((SIZE / 1048576))" "$(((SIZE % 1048576) * 1000 / 1048576))"
printf 'budget : %s bytes (%d MB)\n' "${MAX_BYTES}" "$((MAX_BYTES / 1048576))"
if (( SIZE > MAX_BYTES )); then
printf '%sOVER BUDGET by %s bytes%s\n' "${C_RED}" "$((SIZE - MAX_BYTES))" "${C_RESET}"
exit 1
fi
printf '%swithin budget, %s bytes headroom%s\n' \
"${C_GREEN}" "$((MAX_BYTES - SIZE))" "${C_RESET}"