BroV: single Mac target, chat brain = local claude CLI (stream-json, resume)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
maksarsanjeev
2026-10-06 19:49:32 +03:00
parent 53b4f60da1
commit 0f77ec8704
25 changed files with 471 additions and 1007 deletions
-89
View File
@@ -1,89 +0,0 @@
#!/usr/bin/env bash
# iPhone plan, step 2: builds the GitHub app (fr.louisraille.NotchBuddy) the
# way release.sh does — Release optimizations, Developer ID signature — plus
# the "Coucou Developer ID" provisioning profile, the iCloud entitlements and
# the PHONE_LINK probe. Not notarized, not published: it only proves that a
# Developer ID build can talk to iCloud. A Developer ID profile only allows the
# Production CloudKit environment: the Ping/Pong schema must be deployed to
# Production in the CloudKit Console first.
#
# ./scripts/cloud-test-build.sh
#
# The normal release (scripts/release.sh, Release configuration) is unchanged.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
BUILD_DIR="/tmp/coucou-cloud-test"
APP="$BUILD_DIR/Coucou.app"
PROFILE_NAME="Coucou Developer ID"
APP_ID="256AUJ9555.fr.louisraille.NotchBuddy"
CONTAINER="iCloud.fr.louisraille.Coucou"
die() { echo "error: $*" >&2; exit 1; }
# ── 1. Developer ID identity ──────────────────────────────────────────────────
IDENTITY=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -1 | sed 's/.*"\(Developer ID Application[^"]*\)".*/\1/' || true)
[ -n "$IDENTITY" ] || die "no 'Developer ID Application' certificate found. Install it via Xcode → Settings → Accounts."
echo "Signing with: $IDENTITY"
# ── 2. Provisioning profile ───────────────────────────────────────────────────
# Xcode looks in both folders depending on its version. Double-clicking a
# downloaded .provisionprofile on macOS doesn't reliably put it in either.
TMP_PLIST=$(mktemp)
trap 'rm -f "$TMP_PLIST"' EXIT
FOUND=""
for dir in "$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles" "$HOME/Library/MobileDevice/Provisioning Profiles"; do
[ -d "$dir" ] || continue
for f in "$dir"/*.provisionprofile; do
[ -f "$f" ] || continue
security cms -D -i "$f" > "$TMP_PLIST" 2>/dev/null || continue
NAME=$(/usr/libexec/PlistBuddy -c "Print :Name" "$TMP_PLIST" 2>/dev/null || true)
[ "$NAME" = "$PROFILE_NAME" ] || continue
FOUND="$f"
break 2
done
done
[ -n "$FOUND" ] || die "no provisioning profile named '$PROFILE_NAME' is installed.
developer.apple.com → Profiles → + → Developer ID → App ID fr.louisraille.NotchBuddy,
name it exactly '$PROFILE_NAME' and download it. Then copy it for Xcode:
mkdir -p ~/Library/Developer/Xcode/UserData/Provisioning\\ Profiles
cp ~/Downloads/*.provisionprofile ~/Library/Developer/Xcode/UserData/Provisioning\\ Profiles/"
echo "Profile: $FOUND"
PROFILE_APP_ID=$(/usr/libexec/PlistBuddy -c "Print :Entitlements:com.apple.application-identifier" "$TMP_PLIST" 2>/dev/null || true)
[ "$PROFILE_APP_ID" = "$APP_ID" ] || die "the profile is for '$PROFILE_APP_ID', not $APP_ID"
/usr/libexec/PlistBuddy -c "Print :Entitlements:com.apple.developer.icloud-container-identifiers" "$TMP_PLIST" 2>/dev/null | grep -q "$CONTAINER" \
|| die "the profile has no iCloud container $CONTAINER. Turn on iCloud (CloudKit, with that container) on the App ID fr.louisraille.NotchBuddy, then regenerate and reinstall the profile."
EXPIRY=$(/usr/libexec/PlistBuddy -c "Print :ExpirationDate" "$TMP_PLIST")
echo "Profile expires: $EXPIRY"
# ── 3. Build ──────────────────────────────────────────────────────────────────
cd "$REPO_ROOT/NotchBuddy"
xcodegen generate
rm -rf "$BUILD_DIR" && mkdir -p "$BUILD_DIR"
xcodebuild \
-project NotchBuddy.xcodeproj \
-scheme NotchBuddy \
-configuration ReleaseCloud \
build \
CODE_SIGN_IDENTITY="$IDENTITY" \
CONFIGURATION_BUILD_DIR="$BUILD_DIR" \
-quiet \
|| die "the build failed (see the errors above)"
# ── 4. Checks ─────────────────────────────────────────────────────────────────
[ -f "$APP/Contents/embedded.provisionprofile" ] || die "the profile was not embedded in the app"
codesign --verify --deep --strict "$APP" || die "the signature does not verify"
echo
echo "Entitlements of the built app:"
codesign -d --entitlements - --xml "$APP" 2>/dev/null | plutil -p - || true
echo
echo "✓ Built $APP (Developer ID, not notarized)."
echo
echo "To test:"
echo " 1. Quit any other Coucou (menu bar icon → Quit) and stop it in Xcode."
echo " 2. open $APP"
echo " 3. tail -f ~/Library/Logs/NotchBuddy/nb.log | grep PhoneLink"
echo " 4. Developer ID builds use the Production CloudKit environment: run the iPhone"
echo " app with the CoucouPhoneProduction scheme, pull to refresh, then tap Send pong."
-173
View File
@@ -1,173 +0,0 @@
#!/usr/bin/env bash
# Builds, notarizes and publishes Coucou for macOS (GitHub build).
#
# ./scripts/release.sh 0.1.2 build, sign, notarize, staple, tag, publish
# ./scripts/release.sh 0.1.2 --finish finish after an interrupted notarization wait
#
# Run it from a clean checkout of main. CFBundleShortVersionString in
# NotchBuddy/project.yml must match the version, and CHANGELOG.md needs a
# "## <version>" section: it becomes the release notes.
set -euo pipefail
VERSION="${1:?Usage: $0 <version> [--finish]}"
MODE="${2:-}"
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
BUILD_DIR="/tmp/coucou-release-$VERSION"
APP="$BUILD_DIR/Coucou.app"
ZIP="$BUILD_DIR/Coucou.zip"
COMMIT_FILE="$BUILD_DIR/commit"
TAG="v$VERSION"
die() { echo "error: $*" >&2; exit 1; }
case "$MODE" in
""|--finish) ;;
*) die "unknown option '$MODE' (the only option is --finish)" ;;
esac
cd "$REPO_ROOT"
# ── Checks shared by both modes ───────────────────────────────────────────────
command -v gh >/dev/null || die "gh is not installed (brew install gh)"
# The "## <version>" section of CHANGELOG.md, without leading or trailing blank lines.
CHANGES=$(awk -v head="## $VERSION" '
index($0, head " ") == 1 || $0 == head { found = 1; next }
found && /^## / { exit }
found {
if ($0 ~ /^[ \t\r]*$/) { if (started) blanks++; next }
while (blanks > 0) { print ""; blanks-- }
print; started = 1
}
' CHANGELOG.md)
[ -n "$CHANGES" ] || die "CHANGELOG.md has no '## $VERSION' section"
grep -q "| \[$VERSION\]" README.md || die "README.md has no row for $VERSION in the Versions table"
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
die "tag $TAG already exists here"
fi
if git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then
die "tag $TAG already exists on GitHub"
fi
if [ "$MODE" != "--finish" ]; then
# The release must match a commit: no uncommitted changes to tracked files.
if ! git diff --quiet || ! git diff --cached --quiet; then
die "commit or stash your changes first, the release has to match a commit"
fi
COMMIT=$(git rev-parse HEAD)
# ── 1. Developer ID identity ────────────────────────────────────────────────
# "|| true": with pipefail, grep finding nothing would end the script silently.
IDENTITY=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -1 | sed 's/.*"\(Developer ID Application[^"]*\)".*/\1/' || true)
[ -n "$IDENTITY" ] || die "no 'Developer ID Application' certificate found. Install it via Xcode → Settings → Accounts."
echo "Signing with: $IDENTITY"
# ── 1b. Developer ID provisioning profile (iCloud for the iPhone sync) ──────
PROFILE_NAME="Coucou Developer ID"
PROFILE_FOUND=""
TMP_PLIST=$(mktemp)
for dir in "$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles" "$HOME/Library/MobileDevice/Provisioning Profiles"; do
[ -d "$dir" ] || continue
for f in "$dir"/*.provisionprofile; do
[ -f "$f" ] || continue
security cms -D -i "$f" > "$TMP_PLIST" 2>/dev/null || continue
[ "$(/usr/libexec/PlistBuddy -c "Print :Name" "$TMP_PLIST" 2>/dev/null || true)" = "$PROFILE_NAME" ] || continue
PROFILE_FOUND="$f"
break 2
done
done
[ -n "$PROFILE_FOUND" ] || { rm -f "$TMP_PLIST"; die "no provisioning profile named '$PROFILE_NAME'. developer.apple.com → Profiles → Developer ID (Mac) for fr.louisraille.NotchBuddy, then copy it to ~/Library/Developer/Xcode/UserData/Provisioning Profiles/"; }
EXPIRY=$(/usr/libexec/PlistBuddy -c "Print :ExpirationDate" "$TMP_PLIST")
rm -f "$TMP_PLIST"
echo "Profile: $PROFILE_FOUND (expires $EXPIRY)"
# ── 2. xcodegen + Release build ─────────────────────────────────────────────
cd "$REPO_ROOT/NotchBuddy"
PLIST_VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" Resources/Info.plist 2>/dev/null || true)
[ "$PLIST_VERSION" = "$VERSION" ] || die "NotchBuddy/Resources/Info.plist is version $PLIST_VERSION, not $VERSION: run xcodegen and commit Info.plist"
xcodegen generate
PLIST_VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" Resources/Info.plist)
[ "$PLIST_VERSION" = "$VERSION" ] || die "project.yml says $PLIST_VERSION, not $VERSION: update CFBundleShortVersionString in NotchBuddy/project.yml"
rm -rf "$BUILD_DIR" && mkdir -p "$BUILD_DIR"
echo "$COMMIT" > "$COMMIT_FILE"
xcodebuild \
-project NotchBuddy.xcodeproj \
-scheme NotchBuddy \
-configuration Release \
build \
CODE_SIGN_IDENTITY="$IDENTITY" \
CODE_SIGNING_REQUIRED=YES \
CODE_SIGNING_ALLOWED=YES \
CONFIGURATION_BUILD_DIR="$BUILD_DIR"
[ -f "$APP/Contents/embedded.provisionprofile" ] || die "the provisioning profile was not embedded in the app"
codesign -d --entitlements - --xml "$APP" 2>/dev/null | grep -q "iCloud.fr.louisraille.Coucou" \
|| die "the app is not signed with the iCloud entitlements"
# ── 3. Zip + notarize ───────────────────────────────────────────────────────
ditto -c -k --keepParent "$APP" "$ZIP"
echo
echo "Sending to Apple for notarization. If you stop the wait (Ctrl+C), Apple keeps going:"
echo " check it with xcrun notarytool history --keychain-profile coucou-notary"
echo " once Accepted ./scripts/release.sh $VERSION --finish"
echo
xcrun notarytool submit "$ZIP" --keychain-profile coucou-notary --wait \
|| die "notarization failed: see xcrun notarytool log <id> --keychain-profile coucou-notary (the id is printed above)"
cd "$REPO_ROOT"
else
[ -d "$APP" ] && [ -f "$COMMIT_FILE" ] || die "nothing to finish in $BUILD_DIR, run ./scripts/release.sh $VERSION first"
COMMIT=$(cat "$COMMIT_FILE")
fi
# ── 4. Staple + verify ────────────────────────────────────────────────────────
xcrun stapler staple "$APP" \
|| die "stapling failed: Apple has not accepted the build yet. Check xcrun notarytool history --keychain-profile coucou-notary, then run ./scripts/release.sh $VERSION --finish"
spctl -a -vv "$APP"
# ── 5. Re-zip (with the stapled app) ──────────────────────────────────────────
rm -f "$ZIP"
ditto -c -k --keepParent "$APP" "$ZIP"
echo "Release zip ready: $ZIP"
# ── 6. Tag the built commit + GitHub release ──────────────────────────────────
NOTES="Coucou $VERSION for macOS 15 or later (Apple silicon and Intel).
Signed with a Developer ID and notarized by Apple.
## What's new
$CHANGES
## Install
1. Download Coucou.zip below and unzip it.
2. Move Coucou.app to your Applications folder, replacing the old one if you have it.
3. Launch it, and click Open when macOS asks you to confirm.
Linux and Windows: see the [README](https://github.com/Louis-CFM/coucou#readme)."
echo
echo "──────── Release notes ────────"
echo "$NOTES"
echo "───────────────────────────────"
echo
read -r -p "Tag $(git rev-parse --short "$COMMIT") as $TAG and publish this release? [y/N] " ANSWER
case "$ANSWER" in
y|Y|yes|oui|o|O) ;;
*) die "stopped before tagging. Run ./scripts/release.sh $VERSION --finish to publish later." ;;
esac
git tag "$TAG" "$COMMIT"
git push origin "$TAG"
gh release create "$TAG" "$ZIP" \
--repo Louis-CFM/coucou \
--title "Coucou $VERSION" \
--latest \
--notes "$NOTES"
echo "✓ $TAG released: https://github.com/Louis-CFM/coucou/releases/tag/$TAG"