Something went wrong. Try again.
experiments in a post-browser web
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146#!/bin/bash# Push local `main` to tangled. The ONLY sanctioned path to publishing# work — replaces the convention of `git push tangled main` run ad hoc.## Usage:# scripts/push-main.sh # preview what would be pushed; refuse to push# scripts/push-main.sh --yes # push for real (still runs all checks)# scripts/push-main.sh --yes --no-disruptive # skip the disruptive suite## ## What it enforces## 1. Local `main` is a strict descendant of `tangled/main`.# (Prevents accidental rewinds; refuses if local main is behind or sideways.)# 2. Optional fetch-first: `--fetch` runs `git fetch tangled` to refresh the# local view of `tangled/main` before checks.# 3. The `desktop-disruptive` Playwright suite, when the push touches# apps/desktop/. See "The disruptive suite" below.## ## The disruptive suite## Some specs drive REAL native macOS fullscreen. Native fullscreen allocates its# own Space and animates the display into it, and the app's headless mode works# by `setOpacity(0)` — which hides painting but cannot suppress a# window-manager-level transition. So those specs take over the display of# whoever is at the machine, headless or not, and are excluded from the normal# agent-facing runs (`yarn test:desktop:electron`, `yarn test:grep`) by living in# their own Playwright project. This script is where they run instead: a push is# deliberate, human-initiated, and rare.## It runs only with --yes (the default preview stays instant) and only when the# push range touches apps/desktop/. `--no-disruptive` skips it — for pushing from# a machine someone is actively using, or when the display is mid-presentation.## It lives HERE rather than in `.githooks/pre-push` deliberately: a push through# this script is human-initiated and rare, so gating it at the one chokepoint# that issues the push is simpler than wiring a hook that every push path would# need to trigger correctly.
set -euo pipefail
YES=0FETCH=0DISRUPTIVE=1for arg in "$@"; do case "$arg" in --yes) YES=1 ;; --fetch) FETCH=1 ;; --no-disruptive) DISRUPTIVE=0 ;; *) echo "unknown arg: $arg" >&2; exit 2 ;; esacdone
MAIN_DIR="$(cd "$(dirname "$0")/.." && pwd)"cd "$MAIN_DIR"
if [ "$FETCH" = 1 ]; then echo "Fetching from tangled..." >&2 git fetch tangled >&2fi
# Resolve commit IDs.MAIN_REV=$(git rev-parse main 2>/dev/null || true)TANGLED_REV=$(git rev-parse tangled/main 2>/dev/null || true)
if [ -z "$MAIN_REV" ]; then echo "error: local 'main' branch not found." >&2 exit 1fiif [ -z "$TANGLED_REV" ]; then echo "error: 'tangled/main' not found — has tangled been fetched? Try --fetch." >&2 exit 1fi
# Same-commit: nothing to push.if [ "$MAIN_REV" = "$TANGLED_REV" ]; then echo "main and tangled/main are at the same commit (${MAIN_REV:0:12}). Nothing to push." >&2 exit 0fi
# Check: is TANGLED_REV an ancestor of MAIN_REV? (i.e. main is a strict# descendant of tangled/main — safe fast-forward push)if ! git merge-base --is-ancestor "$TANGLED_REV" "$MAIN_REV"; then echo "" >&2 echo "ERROR: local 'main' is NOT a strict descendant of tangled/main." >&2 echo "" >&2 echo " local main: ${MAIN_REV:0:12}" >&2 echo " tangled/main: ${TANGLED_REV:0:12}" >&2 echo "" >&2 echo "This means tangled has commits the local main doesn't include. Either:" >&2 echo " (a) someone (or a cron) pushed to tangled main since the last local fetch." >&2 echo " Run with --fetch to refresh, then re-run." >&2 echo " (b) local main was reset to an old position. Move it forward to include" >&2 echo " tangled's tip before re-running." >&2 echo "" >&2 echo "Push REFUSED to avoid losing work on tangled." >&2 exit 5fi
# List the commits we're about to push.PUSH_RANGE="${TANGLED_REV}..${MAIN_REV}"echo "" >&2echo "Commits to push (${TANGLED_REV:0:12} → ${MAIN_REV:0:12}):" >&2echo "" >&2git log --oneline "$PUSH_RANGE" >&2
# Dry-run / preview mode.if [ "$YES" != 1 ]; then echo "" >&2 echo "(preview only — re-run with --yes to actually push)" >&2 exit 0fi
# The disruptive suite (see header). Gated on the push actually touching the# desktop app: these specs only cover apps/desktop, and a docs- or server-only# push has no reason to hijack the display for a couple of minutes.if [ "$DISRUPTIVE" = 1 ]; then TOUCHES_DESKTOP=$(git diff --name-only "$TANGLED_REV" "$MAIN_REV" 2>/dev/null | grep -c '^apps/desktop/' || true) if [ "$TOUCHES_DESKTOP" -gt 0 ]; then echo "" >&2 echo "Running the desktop-disruptive suite (${TOUCHES_DESKTOP} apps/desktop/ file(s) in this push)." >&2 echo "HEADS UP: this drives REAL native macOS fullscreen — the display WILL flip" >&2 echo "in and out of a fullscreen Space. Headless cannot hide that; that is exactly" >&2 echo "why these specs are excluded from every other run. Re-run with" >&2 echo "--no-disruptive to skip." >&2 echo "" >&2 if ! yarn test:desktop:disruptive >&2; then echo "" >&2 echo "ERROR: the desktop-disruptive suite FAILED. Push REFUSED." >&2 exit 6 fi echo "" >&2 echo "desktop-disruptive suite passed." >&2 else echo "" >&2 echo "Skipping the desktop-disruptive suite (this push touches no apps/desktop/ files)." >&2 fifi
# Push for real.echo "" >&2echo "Pushing main to tangled..." >&2git push tangled main >&2
echo "" >&2echo "Push complete. tangled/main is now at ${MAIN_REV:0:12}." >&2