master
John Lauer v0.16.2 (insiders): nb-bridge#41: hover/move/wheel accept raw {x,y} (the stale_coordinates gate is clicks-only and name… 2ae6d18 2mo ago
#!/usr/bin/env bash
# promote.sh - move an INSIDERS build of the nb bridge to the PUBLIC tier, on a human's exact words.
#
#   tools/promote.sh <version> "<the human's exact attestation words>" --by "<name>"
#
# adom/nb-bridge#39 / ab 2.0.99 release tiers: the public manifest (native-browser-bridge-manifest.json,
# the updateManifestUrl every non-insider desktop follows) moves ONLY through this script, and this
# script REFUSES without an attestation. An AI passes the human's words through verbatim or does not
# promote, never on its own judgment. The words travel IN the public manifest
# (promotion_attestation / promoted_by / promoted_at).
#
# The bytes are ALREADY on the product page: tools/ship.sh uploaded the zip as release <ver> on
# adom/nb-bridge and archived it under dist/<ver>/ with the insiders manifest. Promote re-points the
# public manifest at that same asset (sha re-checked against the served bytes) and pushes it.
set -euo pipefail
VER="${1:?usage: tools/promote.sh <version> \"<attestation words>\" --by <name>}"
ATTEST="${2:-}"
shift 2 || true
BY="${ADOM_PROMOTED_BY:-}"
while [ $# -gt 0 ]; do case "$1" in --by) BY="$2"; shift 2;; *) shift;; esac; done
REPO="$(cd "$(dirname "$0")/.." && pwd)"; cd "$REPO"
export PATH="$HOME/.local/bin:$PATH"
PUBLIC="adom/nb-bridge"
PUBLIC_MANIFEST="native-browser-bridge-manifest.json"
ZIP="native-browser-bridge-v${VER}.zip"
ARCH="dist/${VER}"

echo "== 0. attestation gate =="
A="$(printf '%s' "$ATTEST" | tr -s '[:space:]' ' ' | sed 's/^ //;s/ $//')"
if [ "${#A}" -lt 8 ] || printf '%s' "$A" | grep -qiE '^(ok|yes|promote|ship it|go|approved|test|placeholder|x+)$'; then
  echo "REFUSED: promoting to the PUBLIC tier needs the human's exact words (>= 8 chars, not a placeholder)."
  echo "         e.g. tools/promote.sh $VER \"John: $VER is good on my laptop and the conf room box, ship it to everyone\" --by \"John Lauer\""
  exit 2
fi
[ -n "$BY" ] || { echo "REFUSED: say who promoted (--by \"<name>\" or ADOM_PROMOTED_BY)."; exit 2; }
echo "   attested by: $BY"
echo "   words: \"$A\""

echo "== 1. the archived insiders build =="
[ -f "$ARCH/$ZIP" ] || { echo "MISSING $ARCH/$ZIP: ship $VER to insiders first (tools/ship.sh $VER ...)"; exit 1; }
[ -f "$ARCH/insiders-manifest.json" ] || { echo "MISSING $ARCH/insiders-manifest.json: the insiders record for $VER"; exit 1; }
SHA_LOCAL=$(python3 -c "import hashlib,sys;print(hashlib.sha256(open(sys.argv[1],'rb').read()).hexdigest())" "$ARCH/$ZIP")
SHA_INS=$(python3 -c "import json,sys;print(json.load(open(sys.argv[1]))['sha256'])" "$ARCH/insiders-manifest.json")
[ "$SHA_LOCAL" = "$SHA_INS" ] || { echo "SHA MISMATCH: archived zip $SHA_LOCAL vs insiders manifest $SHA_INS, refusing"; exit 1; }
URL=$(python3 -c "import json,sys;print(json.load(open(sys.argv[1]))['url'])" "$ARCH/insiders-manifest.json")
REMOTE=$(curl -sL "$URL" | python3 -c "import hashlib,sys;print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest())")
[ "$REMOTE" = "$SHA_LOCAL" ] || { echo "REMOTE ASSET MISMATCH: $URL serves sha $REMOTE, archive is $SHA_LOCAL, refusing to point the public tier at it"; exit 1; }
echo "   $ZIP sha256=$SHA_LOCAL matches the insiders record AND the served release asset"

echo "== 2. public manifest =="
python3 - "$VER" "$ARCH" "$A" "$BY" "$PUBLIC_MANIFEST" <<'PY'
import json, sys, datetime, os
ver, arch, words, by, pub = sys.argv[1:6]
ins = json.load(open(os.path.join(arch, 'insiders-manifest.json')))
now = datetime.datetime.now(datetime.timezone.utc).isoformat().replace('+00:00', 'Z')
m = json.load(open(pub))
m.update({'version': ver, 'url': ins['url'], 'sha256': ins['sha256'], 'size': ins['size'], 'released_at': now,
          'insiders_released_at': ins.get('released_at'),
          'promotion_attestation': words, 'promoted_by': by, 'promoted_at': now})
m.pop('tier', None)
for k in ('rollback_attestation', 'rolled_back_by', 'rolled_back_at'): m.pop(k, None)
json.dump(m, open(pub, 'w'), indent=2); open(pub, 'a').write('\n')
json.dump(m, open(os.path.join(arch, 'public-manifest.json'), 'w'), indent=2)
print(f"   public manifest -> {ins['url']}\n   archived {arch}/public-manifest.json")
PY

echo "== 3a. mark the insiders release-log entry promoted =="
python3 - "dist/insiders-repo/nb-bridge/README.md" "$VER" "$A" "$BY" <<'PY'
import sys, datetime, re, os
path, ver, words, by = sys.argv[1:5]
if os.path.exists(path):
    s = open(path).read()
    pat = re.compile(r"(- \*\*" + re.escape(ver) + r"\*\* insiders, [0-9-]+\.)")
    m = pat.search(s)
    if m:
        ls = s.find(f"- **{ver}**"); le = s.find("\n", ls)
        line = s[ls:le]
        if 'Promoted to public' not in line:
            line = line.replace(" Not promoted.", "") + f" Promoted to public on {datetime.date.today().isoformat()} by {by}: \"{words}\"."
            s = s[:ls] + line + s[le:]; open(path, 'w').write(s); print(f"   release log: {ver} marked promoted")
        else: print(f"   release log: {ver} already marked")
    else: print(f"   release log: no insiders entry for {ver} to mark")
PY
( cd dist/insiders-repo && adom-wiki repo push adom/insiders --files nb-bridge/README.md -m "nb-bridge ${VER}: promoted to public by ${BY}" >/dev/null 2>&1 ) || echo "   (insiders release-log push failed; the public promote continues)"

echo "== 3b. stamp the release notes on the product page =="
NOTES=$(adom-wiki release view "$PUBLIC" "$VER" --json 2>/dev/null | python3 -c "import sys,json; raw=sys.stdin.read(); i=raw.find('{'); d=json.loads(raw[i:]) if i>=0 else {}; r=d.get('data') or d; r=r.get('release') or r; print((r.get('changelog') or '').strip())" 2>/dev/null || true)
case "$NOTES" in *"Promoted to public"*) echo "   release notes already stamped";; *)
  adom-wiki release edit "$PUBLIC" "$VER" --changelog "${NOTES:+$NOTES }Promoted to public on $(date -u +%Y-%m-%d)." >/dev/null 2>&1 && echo "   release notes stamped" || echo "   (release notes stamp failed; promote continues)";; esac

echo "== 4. push the public manifest =="
adom-wiki repo push "$PUBLIC" --files "$PUBLIC_MANIFEST" \
  -m "v${VER}: promoted to PUBLIC by ${BY}: ${A}" >/dev/null 2>&1 || {
    [ "${FORCE_PUSH:-0}" = "1" ] || { echo "   PUSH FAILED (set FORCE_PUSH=1 to retry)"; exit 1; }
  }

echo "== 5. DIFF-VERIFY =="
MAN=$(python3 -c "import json;print(json.load(open('$PUBLIC_MANIFEST'))['version'])")
LIVE=$(curl -s "https://wiki.adom.inc/api/pages/adom/nb-bridge/files/$PUBLIC_MANIFEST" | python3 -c "import json,sys;print(json.load(sys.stdin).get('version'))" 2>/dev/null || echo '?')
[ "$MAN" = "$VER" ] && [ "$LIVE" = "$VER" ] && echo "PROMOTED $VER to PUBLIC (attested by $BY); live public manifest reads $LIVE" || { echo "PUBLIC MANIFEST NOT AT $VER (local $MAN, live $LIVE)"; exit 1; }