Skip to content

Commit 9920012

Browse files
committed
fix: use localhost plugin for HTTP origin — fixes Spotify login (#1) and YouTube Error 153 (#2)
Added tauri-plugin-localhost to serve app assets via http://localhost:3080 instead of the tauri:// custom protocol. Third-party embeds (YouTube, Spotify) require standard HTTP origins to function. Closes #1, Closes #2
1 parent 59094c1 commit 9920012

13 files changed

Lines changed: 522 additions & 73 deletions

.env.signing.example

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# ──────────────────────────────────────────────────────────────
2+
# Knot Code — macOS Code Signing & Notarization
3+
# ──────────────────────────────────────────────────────────────
4+
# Copy to .env.signing and fill in your values.
5+
# This file is loaded by scripts/sign-and-deploy.sh
6+
#
7+
# NEVER commit .env.signing — it contains secrets.
8+
# ──────────────────────────────────────────────────────────────
9+
10+
# Your code signing identity (from Keychain Access or `security find-identity -v -p codesigning`)
11+
# Format: "Developer ID Application: Your Name (TEAM_ID)"
12+
APPLE_SIGNING_IDENTITY=
13+
14+
# Apple ID email used for your Developer account
15+
APPLE_ID=
16+
17+
# Your 10-character Apple Team ID
18+
# Find at: https://developer.apple.com/account → Membership Details
19+
APPLE_TEAM_ID=
20+
21+
# App-specific password for notarization (NOT your Apple ID password)
22+
# Generate at: https://appleid.apple.com → Sign-In and Security → App-Specific Passwords
23+
APPLE_APP_SPECIFIC_PASSWORD=

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
# production
2121
/build
22+
/dist
2223

2324
# misc
2425
.DS_Store
@@ -33,6 +34,7 @@ yarn-error.log*
3334
# env files (can opt-in for committing if needed)
3435
.env*
3536
!.env.example
37+
!.env.signing.example
3638

3739
# vercel
3840
.vercel

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
"desktop:build": "CI=true tauri build",
1111
"desktop:build:debug": "tauri build --debug",
1212
"desktop:release": "pnpm desktop:check && pnpm desktop:build",
13+
"desktop:sign": "./scripts/sign-and-deploy.sh",
14+
"desktop:sign:universal": "./scripts/sign-and-deploy.sh --universal",
1315
"desktop:doctor": "pkill -f \"next dev|tauri dev\" || true; rm -f .next/lock",
1416
"bdev": "pnpm desktop:build && pnpm desktop:dev",
1517
"dev": "pnpm desktop:dev",

scripts/sign-and-deploy.sh

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
#!/usr/bin/env bash
2+
# ──────────────────────────────────────────────────────────────
3+
# Knot Code — Sign, Notarize & Deploy macOS DMG
4+
# ──────────────────────────────────────────────────────────────
5+
#
6+
# Signs the Tauri-built .app bundle with your Apple Developer
7+
# certificate, packages it into a DMG, notarizes it with Apple,
8+
# and staples the notarization ticket so end users never see
9+
# Gatekeeper warnings.
10+
#
11+
# Prerequisites:
12+
# 1. An Apple Developer account enrolled in the Developer ID program
13+
# 2. A "Developer ID Application" certificate in your Keychain
14+
# 3. An app-specific password for notarization
15+
# → https://appleid.apple.com → Sign-In and Security → App-Specific Passwords
16+
#
17+
# Usage:
18+
# ./scripts/sign-and-deploy.sh # Build, sign, notarize
19+
# ./scripts/sign-and-deploy.sh --universal # Universal binary (arm64 + x86_64)
20+
# ./scripts/sign-and-deploy.sh --skip-build # Sign an existing build
21+
# ./scripts/sign-and-deploy.sh --help # Show this help
22+
#
23+
# Environment (set in .env.signing or export before running):
24+
# APPLE_SIGNING_IDENTITY — e.g. "Developer ID Application: Your Name (TEAM_ID)"
25+
# APPLE_ID — your Apple ID email
26+
# APPLE_TEAM_ID — 10-character team ID
27+
# APPLE_APP_SPECIFIC_PASSWORD — app-specific password for notarytool
28+
#
29+
set -euo pipefail
30+
31+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
32+
cd "$ROOT"
33+
34+
# ── Colors ──────────────────────────────────────────────────────
35+
RED='\033[0;31m'
36+
GREEN='\033[0;32m'
37+
YELLOW='\033[1;33m'
38+
CYAN='\033[0;36m'
39+
BOLD='\033[1m'
40+
DIM='\033[2m'
41+
NC='\033[0m'
42+
43+
log() { echo -e "${CYAN}${NC} $*"; }
44+
ok() { echo -e "${GREEN}${NC} $*"; }
45+
warn() { echo -e "${YELLOW}${NC} $*"; }
46+
err() { echo -e "${RED}${NC} $*"; exit 1; }
47+
step() { echo -e "\n${BOLD}$1${NC}\n"; }
48+
49+
# ── Help ────────────────────────────────────────────────────────
50+
show_help() {
51+
echo -e "\n${BOLD}Knot Code — Sign, Notarize & Deploy${NC}\n"
52+
echo " Usage:"
53+
echo " ./scripts/sign-and-deploy.sh [options]"
54+
echo ""
55+
echo " Options:"
56+
echo " --universal Build universal binary (arm64 + x86_64)"
57+
echo " --skip-build Skip the Tauri build step (sign existing build)"
58+
echo " --skip-notarize Skip notarization (just sign)"
59+
echo " --help Show this help message"
60+
echo ""
61+
echo " Environment variables (or set in .env.signing):"
62+
echo " APPLE_SIGNING_IDENTITY Code signing identity"
63+
echo " APPLE_ID Apple ID email"
64+
echo " APPLE_TEAM_ID 10-character team ID"
65+
echo " APPLE_APP_SPECIFIC_PASSWORD App-specific password"
66+
echo ""
67+
exit 0
68+
}
69+
70+
# ── Parse args ──────────────────────────────────────────────────
71+
UNIVERSAL=""
72+
SKIP_BUILD=false
73+
SKIP_NOTARIZE=false
74+
75+
for arg in "$@"; do
76+
case "$arg" in
77+
--universal) UNIVERSAL="--target universal-apple-darwin" ;;
78+
--skip-build) SKIP_BUILD=true ;;
79+
--skip-notarize) SKIP_NOTARIZE=true ;;
80+
--help|-h) show_help ;;
81+
*) warn "Unknown option: $arg" ;;
82+
esac
83+
done
84+
85+
# ── Load signing env ────────────────────────────────────────────
86+
if [ -f "$ROOT/.env.signing" ]; then
87+
log "Loading signing config from .env.signing"
88+
set -a
89+
# shellcheck disable=SC1091
90+
source "$ROOT/.env.signing"
91+
set +a
92+
fi
93+
94+
# ── Validate signing prerequisites ──────────────────────────────
95+
step "1/5 Checking prerequisites"
96+
97+
if [ -z "${APPLE_SIGNING_IDENTITY:-}" ]; then
98+
echo ""
99+
echo -e " ${YELLOW}No APPLE_SIGNING_IDENTITY set.${NC}"
100+
echo ""
101+
echo " Available signing identities in your Keychain:"
102+
echo ""
103+
security find-identity -v -p codesigning | head -20
104+
echo ""
105+
echo -e " ${DIM}Copy the identity string (in quotes) and set it:${NC}"
106+
echo -e " ${CYAN}export APPLE_SIGNING_IDENTITY=\"Developer ID Application: ...\"${NC}"
107+
echo ""
108+
echo -e " ${DIM}Or create ${CYAN}.env.signing${NC}${DIM} with your credentials:${NC}"
109+
echo -e " ${CYAN}APPLE_SIGNING_IDENTITY=\"Developer ID Application: Your Name (TEAMID)\"${NC}"
110+
echo -e " ${CYAN}APPLE_ID=\"you@example.com\"${NC}"
111+
echo -e " ${CYAN}APPLE_TEAM_ID=\"ABCDE12345\"${NC}"
112+
echo -e " ${CYAN}APPLE_APP_SPECIFIC_PASSWORD=\"xxxx-xxxx-xxxx-xxxx\"${NC}"
113+
echo ""
114+
err "APPLE_SIGNING_IDENTITY is required."
115+
fi
116+
117+
ok "Signing identity: ${APPLE_SIGNING_IDENTITY}"
118+
119+
if [ "$SKIP_NOTARIZE" = false ]; then
120+
[ -z "${APPLE_ID:-}" ] && err "APPLE_ID is required for notarization. Set it or use --skip-notarize."
121+
[ -z "${APPLE_TEAM_ID:-}" ] && err "APPLE_TEAM_ID is required for notarization. Set it or use --skip-notarize."
122+
[ -z "${APPLE_APP_SPECIFIC_PASSWORD:-}" ] && err "APPLE_APP_SPECIFIC_PASSWORD is required for notarization. Set it or use --skip-notarize."
123+
ok "Notarization credentials present"
124+
fi
125+
126+
if ! command -v codesign &>/dev/null; then
127+
err "codesign not found. This script requires macOS with Xcode Command Line Tools."
128+
fi
129+
ok "codesign available"
130+
131+
if [ "$SKIP_NOTARIZE" = false ] && ! command -v xcrun &>/dev/null; then
132+
err "xcrun not found. Install Xcode Command Line Tools: xcode-select --install"
133+
fi
134+
135+
# ── Store notarization credentials in Keychain ──────────────────
136+
if [ "$SKIP_NOTARIZE" = false ]; then
137+
log "Storing notarization credentials in Keychain (notarytool)…"
138+
xcrun notarytool store-credentials "KnotCode-notarize" \
139+
--apple-id "$APPLE_ID" \
140+
--team-id "$APPLE_TEAM_ID" \
141+
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
142+
2>/dev/null || true
143+
ok "Keychain profile 'KnotCode-notarize' ready"
144+
fi
145+
146+
# ── Build ───────────────────────────────────────────────────────
147+
VERSION=$(node -e "console.log(require('./package.json').version)")
148+
149+
if [ "$SKIP_BUILD" = false ]; then
150+
step "2/5 Building KnotCode v${VERSION}"
151+
152+
if [ -n "$UNIVERSAL" ]; then
153+
log "Building universal binary (arm64 + x86_64)…"
154+
rustup target add aarch64-apple-darwin x86_64-apple-darwin 2>/dev/null || true
155+
else
156+
log "Building native binary…"
157+
fi
158+
159+
APPLE_SIGNING_IDENTITY="$APPLE_SIGNING_IDENTITY" \
160+
APPLE_ID="$APPLE_ID" \
161+
APPLE_TEAM_ID="$APPLE_TEAM_ID" \
162+
APPLE_APP_SPECIFIC_PASSWORD="$APPLE_APP_SPECIFIC_PASSWORD" \
163+
pnpm tauri build $UNIVERSAL
164+
165+
ok "Tauri build complete"
166+
else
167+
step "2/5 Skipping build (--skip-build)"
168+
fi
169+
170+
# ── Locate artefacts ────────────────────────────────────────────
171+
step "3/5 Locating build artefacts"
172+
173+
if [ -n "$UNIVERSAL" ]; then
174+
TARGET_DIR="src-tauri/target/universal-apple-darwin/release/bundle"
175+
else
176+
TARGET_DIR="src-tauri/target/release/bundle"
177+
fi
178+
179+
APP_PATH=$(find "$TARGET_DIR" -name "*.app" -type d 2>/dev/null | head -1)
180+
[ -z "$APP_PATH" ] && err "Could not find .app bundle in $TARGET_DIR"
181+
ok "App bundle: $APP_PATH"
182+
183+
# ── Deep-sign the .app ──────────────────────────────────────────
184+
step "4/5 Code signing"
185+
186+
log "Signing all nested binaries and frameworks…"
187+
188+
codesign --deep --force --verify --verbose \
189+
--sign "$APPLE_SIGNING_IDENTITY" \
190+
--options runtime \
191+
--entitlements "$ROOT/src-tauri/Entitlements.plist" \
192+
"$APP_PATH" 2>&1 | while IFS= read -r line; do
193+
echo -e " ${DIM}$line${NC}"
194+
done
195+
196+
ok "Code signing complete"
197+
198+
log "Verifying signature…"
199+
codesign --verify --deep --strict --verbose=2 "$APP_PATH" 2>&1 | while IFS= read -r line; do
200+
echo -e " ${DIM}$line${NC}"
201+
done
202+
ok "Signature verified"
203+
204+
log "Checking Gatekeeper acceptance…"
205+
if spctl --assess --type exec --verbose "$APP_PATH" 2>&1; then
206+
ok "Gatekeeper: accepted"
207+
else
208+
warn "Gatekeeper pre-check failed (may pass after notarization)"
209+
fi
210+
211+
# ── Create signed DMG ───────────────────────────────────────────
212+
APP_NAME=$(basename "$APP_PATH" .app)
213+
DMG_NAME="KnotCode_${VERSION}.dmg"
214+
DMG_PATH="$ROOT/dist/$DMG_NAME"
215+
mkdir -p "$ROOT/dist"
216+
217+
log "Creating DMG: $DMG_NAME"
218+
219+
hdiutil create -volname "$APP_NAME" \
220+
-srcfolder "$APP_PATH" \
221+
-ov -format UDZO \
222+
"$DMG_PATH"
223+
224+
log "Signing DMG…"
225+
codesign --force --sign "$APPLE_SIGNING_IDENTITY" "$DMG_PATH"
226+
ok "DMG signed: $DMG_PATH"
227+
228+
# ── Notarize ────────────────────────────────────────────────────
229+
if [ "$SKIP_NOTARIZE" = false ]; then
230+
step "5/5 Notarization"
231+
232+
log "Submitting DMG to Apple for notarization…"
233+
log "This can take 5–15 minutes. Go grab a coffee."
234+
echo ""
235+
236+
xcrun notarytool submit "$DMG_PATH" \
237+
--keychain-profile "KnotCode-notarize" \
238+
--wait 2>&1 | while IFS= read -r line; do
239+
echo -e " ${DIM}$line${NC}"
240+
done
241+
242+
ok "Notarization complete"
243+
244+
log "Stapling notarization ticket to DMG…"
245+
xcrun stapler staple "$DMG_PATH"
246+
ok "Ticket stapled"
247+
248+
log "Final Gatekeeper check…"
249+
spctl --assess --type open --context context:primary-signature --verbose "$DMG_PATH" 2>&1 || true
250+
ok "DMG is signed, notarized, and ready to distribute"
251+
else
252+
step "5/5 Skipping notarization (--skip-notarize)"
253+
fi
254+
255+
# ── Summary ─────────────────────────────────────────────────────
256+
echo ""
257+
echo -e " ${GREEN}${BOLD}✓ Done!${NC}"
258+
echo ""
259+
echo -e " ${BOLD}Artefact:${NC} $DMG_PATH"
260+
SIZE=$(du -sh "$DMG_PATH" | awk '{print $1}')
261+
echo -e " ${BOLD}Size:${NC} $SIZE"
262+
echo -e " ${BOLD}Version:${NC} v$VERSION"
263+
echo ""
264+
echo -e " ${DIM}To distribute:${NC}"
265+
echo -e " ${CYAN}1.${NC} Upload to GitHub Releases, S3, or your CDN"
266+
echo -e " ${CYAN}2.${NC} Users can install directly — no Gatekeeper warnings"
267+
echo ""

0 commit comments

Comments
 (0)