diff --git a/.gitignore b/.gitignore index 17ce9b2..7a56e96 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ dist-ssr # Rust / Tauri src-tauri/target/ +# Apple signing credentials (never commit) +scripts/apple-signing.env + # Editor directories and files .vscode/* !.vscode/extensions.json @@ -25,3 +28,4 @@ src-tauri/target/ *.njsproj *.sln *.sw? +secrets/ diff --git a/scripts/apple-signing.env.example b/scripts/apple-signing.env.example new file mode 100644 index 0000000..77b9377 --- /dev/null +++ b/scripts/apple-signing.env.example @@ -0,0 +1,15 @@ +# Copy to scripts/apple-signing.env and fill in. This file is gitignored. +# Required for a signed + notarized DMG that Gatekeeper accepts on download. +# +# 1. APPLE_SIGNING_IDENTITY: the exact string from `security find-identity -v -p codesigning` +# that starts with "Developer ID Application: ...". You must create that certificate +# first at https://developer.apple.com/account/resources/certificates/list +# (Cert type = "Developer ID Application", NOT "Apple Development"). +# 2. APPLE_ID: your Apple ID email. +# 3. APPLE_PASSWORD: an app-specific password from https://account.apple.com +# (Sign-In and Security -> App-Specific Passwords). NOT your normal password. +# 4. APPLE_TEAM_ID: 10-char Team ID from https://developer.apple.com/account#MembershipDetailsCard +export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name (4759TW4SDC)" +export APPLE_ID="james.twose2711@gmail.com" +export APPLE_PASSWORD="xxxx-xxxx-xxxx-xxxx" +export APPLE_TEAM_ID="4759TW4SDC" \ No newline at end of file diff --git a/scripts/gitea-release.sh b/scripts/gitea-release.sh index cc0f9c6..a16fbc0 100755 --- a/scripts/gitea-release.sh +++ b/scripts/gitea-release.sh @@ -1,8 +1,10 @@ #!/usr/bin/env bash # Upload the built macOS artifacts to a Gitea release. No runners, no CI. # Auth comes from the macOS keychain (same creds `git` uses to push). -# Usage: ./scripts/gitea-release.sh [v0.1.0] -# Requires: jq, curl, and a prior `npm run tauri build`. +# Usage: ./scripts/gitea-release.sh [v0.2.0] +# No arg = auto patch-bump (0.1.0 -> 0.1.1). Explicit arg syncs version files. +# Requires: jq, curl, npm run tauri build deps. If scripts/apple-signing.env is +# present, builds a signed + notarized DMG automatically; else ad-hoc (unsigned). set -euo pipefail GITEA_URL="https://gitea.jms.rocks" @@ -23,11 +25,49 @@ host=$HOST" 2>/dev/null || true) printf 'Authorization: Basic %s' "$(printf '%s:%s' "$user" "$pass" | base64)" } -TAG="${1:-v$(jq -r .version package.json)}" +# Resolve the release tag. No arg => auto-bump patch from the current version. +# An explicit arg (v0.2.0) wins and also syncs both version files to it, so the +# built DMG's embedded version always matches the release tag. +# ponytail: patch-only auto-bump; pass v0.2.0 / v1.0.0 for minor/major. +if [ -n "${1:-}" ]; then + VER="${1#v}" +else + VER=$(node -e 'const v=require("./package.json").version;const [a,b,c]=v.split(".").map(Number);process.stdout.write(`${a}.${b}.${c+1}`)') +fi +TAG="v$VER" +node -e ' + const fs=require("fs"),ver=process.argv[1]; + for(const f of ["package.json","src-tauri/tauri.conf.json"]){ + const t=fs.readFileSync(f,"utf8"); + const out=t.replace(/("version"\s*:\s*")\d+\.\d+\.\d+(")/,`$1${ver}$2`); + if(out===t)throw new Error("version not replaced in "+f); + fs.writeFileSync(f,out); + } +' "$VER" +echo "release $TAG (version files synced)" +git add package.json src-tauri/tauri.conf.json +if ! git diff --cached --quiet; then + git commit -m "chore: release $TAG" -q + echo "committed version bump" +fi + +# If Apple signing creds are present, do a signed + notarized build now so the +# uploaded DMG passes Gatekeeper on a browser download. Without these the DMG +# is only ad-hoc signed and macOS shows it as "damaged" when downloaded. +ENV_FILE="$(dirname "$0")/apple-signing.env" +if [ -f "$ENV_FILE" ]; then + set -a; . "$ENV_FILE"; set +a + echo "Building signed + notarized DMG ..." + npm run tauri build -- --bundles app,dmg +else + echo "warn: scripts/apple-signing.env missing — building unsigned (ad-hoc) DMG." + echo " Browser downloads will be blocked by Gatekeeper. See apple-signing.env.example." + npm run tauri build -- --bundles app,dmg +fi DMG=$(ls -t src-tauri/target/release/bundle/dmg/*.dmg | head -1) APP_BUNDLE=$(ls -d src-tauri/target/release/bundle/macos/*.app | head -1) -[ -f "$DMG" ] || { echo "no dmg found — run 'npm run tauri build' first"; exit 1; } +[ -f "$DMG" ] || { echo "no dmg found — build failed"; exit 1; } [ -d "$APP_BUNDLE" ] || { echo "no .app found"; exit 1; } # ponytail: ship the dmg as the installer; also zip the .app for unmanaged @@ -52,4 +92,12 @@ for f in "$DMG" "$ZIP"; do -H "$AUTH" -F "attachment=@$f" -o /dev/null done +# ponytail: guard against shipping an unsigned bundle by mistake — if creds +# were configured, confirm the app is actually Developer-ID signed. +if [ -f "$ENV_FILE" ]; then + codesign -dv --verbose=2 "$APP_BUNDLE" 2>&1 | grep -q "Authority=Developer ID" \ + || { echo "error: APPLE creds set but app is not Developer-ID signed — notarization failed or skipped"; exit 1; } + echo "verified: Developer ID signature present" +fi + echo "done -> $GITEA_URL/$OWNER_REPO/releases/tag/$TAG" \ No newline at end of file