Make the release process script-driven and documented
The release flow, install policy, and on-device rules previously lived in session context. Now: - scripts/release.sh: the executable release — gate 1 static checks, gate 2 full go test (incl. TestNoFramesWhileIdle), gate 3 frame-regression profile on the emulator, build (SKIP_CHECK=1 avoids double static checks), then install to EVERY connected device (emulators and phones; the wireless-debugging serial changes per reconnect, the loop sidesteps it). Any gate failure aborts before install; dirty tree is warned, not fatal. - doc/release.md: the process, the install-to-all-devices default, the rule that a phone is INSTALL-ONLY during a release (on-device profiling/testing is diagnostic and needs explicit approval per run), failure-handling table, versioning gap (still gogio default 1.0.0.1), and emulator requirements. - doc/README.md: release.md added to the doc table. - architecture.md §11: pointer to the release flow. Validated end-to-end: release.sh ran all three gates (PASS), built the APK, and installed it on both the phone and the emulator in one command.
This commit is contained in:
parent
8bdf916d8a
commit
f31f849665
|
|
@ -1,6 +1,6 @@
|
|||
# Pad documentation
|
||||
|
||||
Three documents, kept at the level of *what, why, and invariants* — not
|
||||
Four documents, kept at the level of *what, why, and invariants* — not
|
||||
line-by-line code — so they stay true as the implementation evolves.
|
||||
|
||||
| Doc | What it is |
|
||||
|
|
@ -8,6 +8,7 @@ line-by-line code — so they stay true as the implementation evolves.
|
|||
| [`spec.md`](./spec.md) | What the app **actually does**, measured performance, the code layout, and an explicit list of deferred features. |
|
||||
| [`architecture.md`](./architecture.md) | How it works: single-owner concurrency model, channel topology, Frame handoff contract, ownership rules, editor/browser/render internals. |
|
||||
| [`development_plan.md`](./development_plan.md) | The active plan: completed phases, remaining work, and the on-device observation loop. |
|
||||
| [`release.md`](./release.md) | The release process (`scripts/release.sh`): the gates, the install-to-all-devices policy, and the rule that on-device profiling/testing is diagnostic and needs explicit approval. |
|
||||
|
||||
## Package inventory
|
||||
|
||||
|
|
|
|||
|
|
@ -586,6 +586,10 @@ cannot measure this app because it renders into a `SurfaceView`).
|
|||
app and seeds/removes a file in its storage, and the developer may be using
|
||||
the phone while a release runs. It exists to investigate a suspected
|
||||
frame/battery problem, not to certify a release.
|
||||
|
||||
The full release flow (gates → build → install to all connected devices,
|
||||
and the policy that a phone is install-only during a release) is
|
||||
`doc/release.md` + `scripts/release.sh`.
|
||||
- The profiler is owned by the goroutine that creates it and is single-goroutine
|
||||
(no locks). It does **not** `Sync()` the CSV per flush (only per row batch) to
|
||||
avoid periodic fsync hitches in the logic path.
|
||||
|
|
|
|||
74
doc/release.md
Normal file
74
doc/release.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Release process
|
||||
|
||||
A release is **`./scripts/release.sh`** — that one command is the
|
||||
executable form of this document. If this document and the script ever
|
||||
disagree, fix both in the same change.
|
||||
|
||||
## What a release does
|
||||
|
||||
1. **Gate 1 — static checks**: `scripts/check.sh` (go vet + staticcheck).
|
||||
2. **Gate 2 — test suite**: `go test -count=1 ./...` — includes
|
||||
`TestNoFramesWhileIdle` (internal/test/e2e), the headless
|
||||
frame-regression guard.
|
||||
3. **Gate 3 — frame-regression profile, EMULATOR only**:
|
||||
`scripts/profile_emulator.sh` (auto-selects an emulator; it never
|
||||
auto-selects a phone). See architecture.md §11 for what it measures and
|
||||
why the budgets are shaped the way they are.
|
||||
4. **Build**: `scripts/build_phone.sh --no-install` (arm64+arm APK →
|
||||
`cmd/pad/pad-phone.apk`; static checks are skipped here, already gate 1).
|
||||
5. **Install to every connected device** — emulators **and phones**.
|
||||
|
||||
Any gate failure aborts before the install. The working tree may be dirty;
|
||||
the script warns, but the gates then certify the *current* (possibly
|
||||
uncommitted) state — commit the release work before shipping.
|
||||
|
||||
## Install policy
|
||||
|
||||
- Pushing a release build to the developer's phone is the **default and
|
||||
expected** behavior — `release.sh` installs to all `adb devices` entries,
|
||||
which is what makes the phone just work without knowing its serial
|
||||
(the wireless-debugging `IP:port` changes on every reconnect; the
|
||||
install loop sidesteps that entirely).
|
||||
- On a phone that has never had the app, grant **"All files access"**
|
||||
(MANAGE_EXTERNAL_STORAGE) in system settings after the first install.
|
||||
|
||||
## On-device profiling/testing: NOT part of a release
|
||||
|
||||
The release touches a phone **only to install the APK**. It never
|
||||
profiles, tests, force-stops, or otherwise drives a phone:
|
||||
|
||||
- On-device runs (e.g. `scripts/profile_emulator.sh -s <phone serial>`)
|
||||
are **diagnostics**: they force-stop the app, seed and remove a file in
|
||||
its storage, and read its profiler output. The developer may be using the
|
||||
phone while a release runs.
|
||||
- Therefore an on-device run requires **explicit approval in the moment**
|
||||
(ask first, name the serial), and its results are informational, never a
|
||||
gate. The frame-regression *gate* is tier 2 (headless) + tier 3
|
||||
(emulator) only.
|
||||
|
||||
## Failure handling
|
||||
|
||||
| Gate | Fails on | First things to check |
|
||||
|---|---|---|
|
||||
| 1 static | vet/staticcheck findings | The findings; don't waive without a reason in the commit message |
|
||||
| 2 tests | any test | `go test -count=1 -run <Name> ./...` to isolate; e2e tests are deterministic — a flake is a bug in the test |
|
||||
| 3 profile | a phase over its frame budget | The printed phases + `PERF`/`PERF-PRESENT` logcat lines (a spinner is usually obvious); confirm the emulator wasn't under host load (rerun once before investigating) |
|
||||
| build | gogio/apktool/sign | Toolchain notes in `scripts/build_phone.sh` header |
|
||||
|
||||
If the profile gate is in question, a diagnostic run **on the phone**
|
||||
(requires approval) is the escalation path — not a replacement for the gate.
|
||||
|
||||
## Versioning (known gap)
|
||||
|
||||
The APK is currently built with the gogio-default version
|
||||
(`1.0.0.1`); releases do **not** bump a version yet. When release
|
||||
distribution starts mattering, add a version step here (and to
|
||||
`release.sh`) — until then, "which release" is identified by the git
|
||||
commit the APK was built from.
|
||||
|
||||
## Emulator requirements
|
||||
|
||||
Gate 3 needs a running emulator (`adb devices` shows `emulator-*`).
|
||||
`scripts/emu.sh` manages the AVD (see its header); the AVD must have had
|
||||
"All files access" granted once (it has). No phone needs to be connected
|
||||
for a release to succeed — the install step simply reports no devices.
|
||||
|
|
@ -31,8 +31,14 @@ command -v apksigner >/dev/null 2>&1 || die "apksigner not found (need $ANDROID_
|
|||
command -v adb >/dev/null 2>&1 || die "adb not found (need $ANDROID_HOME/platform-tools)"
|
||||
[ -f "$HOME/.android/debug.keystore" ] || die "debug keystore missing: $HOME/.android/debug.keystore"
|
||||
|
||||
echo "=== static checks (go vet + staticcheck) ==="
|
||||
"$REPO/scripts/check.sh"
|
||||
# SKIP_CHECK=1 skips the static checks (scripts/release.sh already runs them
|
||||
# as gate 1); a standalone build always runs them.
|
||||
if [ "${SKIP_CHECK:-0}" = "1" ]; then
|
||||
echo "=== static checks (skipped: SKIP_CHECK=1) ==="
|
||||
else
|
||||
echo "=== static checks (go vet + staticcheck) ==="
|
||||
"$REPO/scripts/check.sh"
|
||||
fi
|
||||
|
||||
APKTOOL="$ANDROID_HOME/tools/apktool.jar"
|
||||
if [ ! -f "$APKTOOL" ]; then
|
||||
|
|
|
|||
62
scripts/release.sh
Executable file
62
scripts/release.sh
Executable file
|
|
@ -0,0 +1,62 @@
|
|||
#!/usr/bin/env bash
|
||||
# Pad release: run the release gates, build the phone APK, and install it to
|
||||
# every connected device.
|
||||
#
|
||||
# The full process, policies, and failure handling are documented in
|
||||
# doc/release.md — this script is the executable form of that document.
|
||||
#
|
||||
# Gates (ALL must pass; any failure aborts before the install):
|
||||
# 1. static checks: go vet + staticcheck (scripts/check.sh)
|
||||
# 2. go test -count=1 ./... (includes TestNoFramesWhileIdle)
|
||||
# 3. frame-regression profile on the EMULATOR (scripts/profile_emulator.sh)
|
||||
#
|
||||
# Install policy (doc/release.md):
|
||||
# - The built APK is installed to EVERY connected device — emulators and
|
||||
# phones. Pushing a release build to the developer's phone is the
|
||||
# DEFAULT, expected behavior.
|
||||
# - This script installs ONLY. It never profiles or tests a phone.
|
||||
# On-device profiling/testing (e.g. scripts/profile_emulator.sh -s
|
||||
# <phone serial>) is diagnostic and requires explicit developer approval
|
||||
# per run.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/release.sh # gates + build + install to all devices
|
||||
# ./scripts/release.sh --no-install
|
||||
set -euo pipefail
|
||||
|
||||
REPO=$(cd "$(dirname "$0")/.." && pwd)
|
||||
INSTALL=1
|
||||
[ "${1:-}" = "--no-install" ] && INSTALL=0
|
||||
|
||||
if [ -n "$(git -C "$REPO" status --porcelain 2>/dev/null)" ]; then
|
||||
echo "NOTE: working tree is dirty — the gates run on the current (possibly"
|
||||
echo " uncommitted) state. Commit the release work before shipping."
|
||||
fi
|
||||
|
||||
echo "=== gate 1/3: static checks ==="
|
||||
"$REPO/scripts/check.sh"
|
||||
|
||||
echo "=== gate 2/3: go test (includes TestNoFramesWhileIdle) ==="
|
||||
(cd "$REPO" && go test -count=1 ./...)
|
||||
|
||||
echo "=== gate 3/3: frame-regression profile (emulator only) ==="
|
||||
"$REPO/scripts/profile_emulator.sh"
|
||||
|
||||
echo "=== build phone APK ==="
|
||||
SKIP_CHECK=1 "$REPO/scripts/build_phone.sh" --no-install
|
||||
|
||||
if [ "$INSTALL" = "1" ]; then
|
||||
echo "=== install to all connected devices ==="
|
||||
serials=$(adb devices | awk 'NR>1 && $2=="device" {print $1}')
|
||||
if [ -z "$serials" ]; then
|
||||
echo "no connected devices — APK built at cmd/pad/pad-phone.apk;"
|
||||
echo "install later with: adb -s <serial> install -r cmd/pad/pad-phone.apk"
|
||||
else
|
||||
for s in $serials; do
|
||||
echo "-- installing on $s"
|
||||
adb -s "$s" install -r "$REPO/cmd/pad/pad-phone.apk"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "=== RELEASE COMPLETE ==="
|
||||
Loading…
Reference in New Issue
Block a user