feat(app): implement basic function
General - 配置界面支持配置对象存储桶 - 快捷键进入截图 - 一键上传截图 & 复制截图链接 MacOS - 状态栏指示器和应用图标
This commit is contained in:
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# create-dev-cert.sh — Create a stable, self-signed code-signing certificate.
|
||||
#
|
||||
# Why this exists:
|
||||
# macOS's privacy database (TCC) tracks app identity primarily by the
|
||||
# code-signing cdhash (for ad-hoc) or the certificate (for properly signed
|
||||
# apps). Wails default ad-hoc signing produces a new cdhash on every build,
|
||||
# so TCC keeps re-prompting for Screen Recording / Accessibility permission.
|
||||
#
|
||||
# By creating ONE self-signed certificate locally and reusing it for every
|
||||
# build, the signing identity never changes, so TCC remembers the grant.
|
||||
#
|
||||
# Idempotent: if the cert already exists with the configured CN, this script
|
||||
# is a no-op and exits 0.
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
CERT_CN="${CERT_CN:-SnapGo Dev Cert}"
|
||||
KEYCHAIN="${KEYCHAIN:-login.keychain-db}"
|
||||
|
||||
# 1. Skip ONLY if a usable code-signing identity exists. We deliberately do
|
||||
# not skip on the mere presence of a certificate object, because a partial
|
||||
# import (e.g. wrong PKCS#12 password) leaves the cert behind without a
|
||||
# private key, which makes codesign fail later in confusing ways.
|
||||
if security find-identity -v -p codesigning 2>/dev/null | grep -q "\"${CERT_CN}\""; then
|
||||
echo "[create-dev-cert.sh] usable identity '${CERT_CN}' already present — nothing to do."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 1b. Clean any stale certificate-only entry from a previous failed import,
|
||||
# so the new import does not collide on duplicate CN.
|
||||
if security find-certificate -c "${CERT_CN}" "${KEYCHAIN}" >/dev/null 2>&1; then
|
||||
echo "[create-dev-cert.sh] removing stale (key-less) cert entry from previous run..."
|
||||
# Loop because there may be multiple stale copies.
|
||||
while security find-certificate -c "${CERT_CN}" "${KEYCHAIN}" >/dev/null 2>&1; do
|
||||
security delete-certificate -c "${CERT_CN}" "${KEYCHAIN}" >/dev/null 2>&1 || break
|
||||
done
|
||||
fi
|
||||
|
||||
# 2. Generate a self-signed code-signing cert via openssl + import via security.
|
||||
WORK="$(mktemp -d)"
|
||||
trap 'rm -rf "${WORK}"' EXIT
|
||||
|
||||
KEY="${WORK}/dev.key"
|
||||
CSR="${WORK}/dev.csr"
|
||||
CRT="${WORK}/dev.crt"
|
||||
P12="${WORK}/dev.p12"
|
||||
EXTFILE="${WORK}/ext.cnf"
|
||||
|
||||
# X509 v3 extension file enabling "Code Signing" extended key usage (EKU).
|
||||
cat > "${EXTFILE}" <<'EOF'
|
||||
[v3_ext]
|
||||
keyUsage = critical,digitalSignature
|
||||
extendedKeyUsage = critical,codeSigning
|
||||
basicConstraints = CA:false
|
||||
EOF
|
||||
|
||||
echo "[create-dev-cert.sh] generating private key..."
|
||||
openssl genrsa -out "${KEY}" 2048 >/dev/null 2>&1
|
||||
|
||||
echo "[create-dev-cert.sh] generating CSR..."
|
||||
openssl req -new -key "${KEY}" -out "${CSR}" -subj "/CN=${CERT_CN}/O=SnapGo Dev/C=US" >/dev/null 2>&1
|
||||
|
||||
echo "[create-dev-cert.sh] self-signing certificate (10 years)..."
|
||||
openssl x509 -req -in "${CSR}" -signkey "${KEY}" -out "${CRT}" \
|
||||
-days 3650 -extfile "${EXTFILE}" -extensions v3_ext >/dev/null 2>&1
|
||||
|
||||
echo "[create-dev-cert.sh] bundling into PKCS#12..."
|
||||
# `-legacy` is REQUIRED on OpenSSL 3+: macOS's `security` tool can only
|
||||
# verify the legacy PKCS#12 MAC (SHA-1 / 3DES). Without this flag the import
|
||||
# fails with "MAC verification failed during PKCS12 import".
|
||||
# Use a non-empty passphrase to avoid edge cases where empty pass triggers
|
||||
# different MAC behaviour on some openssl builds.
|
||||
P12_PASS="snapgo"
|
||||
openssl pkcs12 -export -legacy \
|
||||
-inkey "${KEY}" -in "${CRT}" -out "${P12}" \
|
||||
-name "${CERT_CN}" -passout "pass:${P12_PASS}" >/dev/null 2>&1
|
||||
|
||||
echo "[create-dev-cert.sh] importing into ${KEYCHAIN} (you may be prompted for your login password)..."
|
||||
security import "${P12}" -k "${KEYCHAIN}" -P "${P12_PASS}" -T /usr/bin/codesign
|
||||
|
||||
# 3. Mark the cert as trusted for code signing. This requires admin.
|
||||
# We use `security add-trusted-cert` with policy `codeSign`.
|
||||
echo "[create-dev-cert.sh] adding trust setting (admin password may be required)..."
|
||||
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain \
|
||||
-p codeSign "${CRT}" || {
|
||||
echo "[create-dev-cert.sh] WARN: trust setting could not be added. The cert"
|
||||
echo " is still importable for codesign use, but TCC may still re-prompt"
|
||||
echo " unless the cert is trusted. Re-run as admin to fix."
|
||||
}
|
||||
|
||||
echo "[create-dev-cert.sh] done. Verify with:"
|
||||
echo " security find-identity -v -p codesigning"
|
||||
echo "You should see: '${CERT_CN}'"
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# dev-build.sh — Local dev rebuild + stable re-sign + open.
|
||||
#
|
||||
# Use this instead of plain `wails build` while iterating, so that every
|
||||
# rebuild uses the same self-signed dev certificate (created by
|
||||
# create-dev-cert.sh). This keeps macOS TCC from re-prompting for Screen
|
||||
# Recording / Accessibility permissions after every code change.
|
||||
#
|
||||
# Workflow:
|
||||
# ./scripts/create-dev-cert.sh # one time
|
||||
# ./scripts/dev-build.sh # every rebuild
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
cd "${ROOT_DIR}"
|
||||
|
||||
CERT_CN="${CERT_CN:-SnapGo Dev Cert}"
|
||||
|
||||
# 1. Make sure wails is on PATH (works whether or not the user added it).
|
||||
export PATH="$(go env GOBIN):${PATH}"
|
||||
|
||||
# 2. Make sure the dev cert exists. If not, abort with a clear pointer.
|
||||
if ! security find-identity -v -p codesigning | grep -q "${CERT_CN}"; then
|
||||
echo "[dev-build.sh] ERROR: code-signing identity '${CERT_CN}' not found." >&2
|
||||
echo " Run ./scripts/create-dev-cert.sh first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 3. Stop any running instance so the rebuild is not blocked by file locks
|
||||
# and so the next launch uses the fresh binary.
|
||||
pkill -f SnapGo 2>/dev/null || true
|
||||
sleep 0.4
|
||||
|
||||
# 4. Build via Wails. We deliberately keep the default ad-hoc-style packaging
|
||||
# and re-sign right after, since `wails build` does not expose a
|
||||
# --identity flag in v2.
|
||||
ARCH="${ARCH:-arm64}"
|
||||
echo "[dev-build.sh] building darwin/${ARCH}..."
|
||||
wails build -platform "darwin/${ARCH}" -clean
|
||||
|
||||
# 5. Re-sign with the stable dev cert. We invoke sign.sh through env var
|
||||
# DEVELOPER_ID_APPLICATION which it accepts as the identity.
|
||||
APP_PATH="${ROOT_DIR}/build/bin/SnapGo.app"
|
||||
echo "[dev-build.sh] re-signing ${APP_PATH} with '${CERT_CN}'..."
|
||||
DEVELOPER_ID_APPLICATION="${CERT_CN}" SIGN_MODE=developer-id \
|
||||
"${SCRIPT_DIR}/sign.sh" >/dev/null
|
||||
|
||||
# 6. Sync to /Applications and launch from there.
|
||||
# Why /Applications: macOS TCC tracks an app by its code-signing identity
|
||||
# AND its on-disk path. Running from a stable absolute path makes the
|
||||
# privacy grants stick. We deliberately do NOT launch from build/bin to
|
||||
# avoid creating a second TCC entry every time the build dir moves.
|
||||
INSTALL_PATH="/Applications/SnapGo.app"
|
||||
echo "[dev-build.sh] syncing to ${INSTALL_PATH}..."
|
||||
rm -rf "${INSTALL_PATH}"
|
||||
cp -R "${APP_PATH}" "${INSTALL_PATH}"
|
||||
|
||||
echo "[dev-build.sh] opening app..."
|
||||
open "${INSTALL_PATH}"
|
||||
|
||||
echo "[dev-build.sh] done. If TCC still re-prompts, run:"
|
||||
echo " tccutil reset ScreenCapture io.snapgo.app"
|
||||
echo " tccutil reset Accessibility io.snapgo.app"
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# make-dmg.sh — Build a distributable .dmg for SnapGo.app
|
||||
#
|
||||
# Design rationale:
|
||||
# - Prefer `create-dmg` (https://github.com/create-dmg/create-dmg) when available
|
||||
# because it produces a polished installer with /Applications drag target,
|
||||
# custom background and proper icon layout.
|
||||
# - Fall back to a plain `hdiutil create` UDZO image so the script always works
|
||||
# on a fresh machine even without homebrew. The fallback DMG still shows the
|
||||
# app + a symlink to /Applications so users can drag-install.
|
||||
# - Output is always written to build/bin/SnapGo-<version>-<arch>.dmg so it
|
||||
# does not collide with the .app bundle.
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
APP_PATH="${APP_PATH:-${ROOT_DIR}/build/bin/SnapGo.app}"
|
||||
OUT_DIR="${OUT_DIR:-${ROOT_DIR}/build/bin}"
|
||||
|
||||
if [[ ! -d "${APP_PATH}" ]]; then
|
||||
echo "[make-dmg.sh] ERROR: ${APP_PATH} not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# -------- Version / arch derivation --------
|
||||
VERSION="${VERSION:-$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' \
|
||||
"${APP_PATH}/Contents/Info.plist" 2>/dev/null || echo "0.1.0")}"
|
||||
ARCH="${ARCH:-$(uname -m)}" # arm64 or x86_64
|
||||
DMG_NAME="SnapGo-${VERSION}-${ARCH}.dmg"
|
||||
DMG_PATH="${OUT_DIR}/${DMG_NAME}"
|
||||
VOLNAME="SnapGo ${VERSION}"
|
||||
|
||||
mkdir -p "${OUT_DIR}"
|
||||
rm -f "${DMG_PATH}"
|
||||
|
||||
# -------- Path 1: create-dmg (pretty) --------
|
||||
if command -v create-dmg >/dev/null 2>&1; then
|
||||
echo "[make-dmg.sh] using create-dmg"
|
||||
# NOTE: create-dmg refuses to run when the output exists; we already removed it.
|
||||
create-dmg \
|
||||
--volname "${VOLNAME}" \
|
||||
--window-pos 200 120 \
|
||||
--window-size 600 360 \
|
||||
--icon-size 100 \
|
||||
--icon "SnapGo.app" 160 180 \
|
||||
--hide-extension "SnapGo.app" \
|
||||
--app-drop-link 440 180 \
|
||||
--no-internet-enable \
|
||||
"${DMG_PATH}" \
|
||||
"${APP_PATH}"
|
||||
else
|
||||
# -------- Path 2: hdiutil fallback (always works) --------
|
||||
echo "[make-dmg.sh] create-dmg not found, using hdiutil fallback"
|
||||
STAGING="$(mktemp -d)"
|
||||
trap 'rm -rf "${STAGING}"' EXIT
|
||||
|
||||
# Copy the .app and add an /Applications symlink so users can drag-install.
|
||||
cp -R "${APP_PATH}" "${STAGING}/"
|
||||
ln -s /Applications "${STAGING}/Applications"
|
||||
|
||||
hdiutil create \
|
||||
-volname "${VOLNAME}" \
|
||||
-srcfolder "${STAGING}" \
|
||||
-ov -format UDZO \
|
||||
"${DMG_PATH}"
|
||||
fi
|
||||
|
||||
# -------- Sign the DMG itself if a Developer ID is available --------
|
||||
# Notarization requires the DMG be signed too.
|
||||
if [[ -n "${DEVELOPER_ID_APPLICATION:-}" ]]; then
|
||||
echo "[make-dmg.sh] signing DMG with: ${DEVELOPER_ID_APPLICATION}"
|
||||
codesign --force --sign "${DEVELOPER_ID_APPLICATION}" --timestamp "${DMG_PATH}"
|
||||
elif security find-identity -v -p codesigning 2>/dev/null \
|
||||
| grep -q "Developer ID Application"; then
|
||||
IDENT=$(security find-identity -v -p codesigning \
|
||||
| awk -F'"' '/Developer ID Application/ {print $2; exit}')
|
||||
echo "[make-dmg.sh] signing DMG with auto-detected identity: ${IDENT}"
|
||||
codesign --force --sign "${IDENT}" --timestamp "${DMG_PATH}"
|
||||
else
|
||||
echo "[make-dmg.sh] no Developer ID identity, DMG left unsigned (ad-hoc build)."
|
||||
fi
|
||||
|
||||
echo "[make-dmg.sh] OK -> ${DMG_PATH}"
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# notarize.sh — Submit the signed DMG to Apple Notary Service and staple the
|
||||
# returned ticket so users get a green Gatekeeper check offline.
|
||||
#
|
||||
# Design rationale:
|
||||
# - We use `xcrun notarytool` (Xcode 13+). The legacy `altool` is deprecated.
|
||||
# - Two credential modes are supported, in order of preference:
|
||||
# 1. KEYCHAIN_PROFILE — recommended, created once via:
|
||||
# xcrun notarytool store-credentials "snapgo-notary" \
|
||||
# --apple-id "<APPLE_ID>" \
|
||||
# --team-id "<TEAMID>" \
|
||||
# --password "<APP_SPECIFIC_PASSWORD>"
|
||||
# 2. APPLE_ID + APPLE_TEAM_ID + APPLE_APP_SPECIFIC_PASSWORD env vars.
|
||||
# - After successful notarization we run `xcrun stapler staple` so the app/dmg
|
||||
# is verifiable without an internet connection on the user's Mac.
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
DMG_PATH="${DMG_PATH:-}"
|
||||
if [[ -z "${DMG_PATH}" ]]; then
|
||||
# Auto-detect the most recent DMG produced by make-dmg.sh
|
||||
DMG_PATH="$(ls -1t "${ROOT_DIR}/build/bin/"SnapGo-*.dmg 2>/dev/null | head -1 || true)"
|
||||
fi
|
||||
if [[ -z "${DMG_PATH}" || ! -f "${DMG_PATH}" ]]; then
|
||||
echo "[notarize.sh] ERROR: DMG not found. Set DMG_PATH or run make-dmg.sh first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# -------- Build the credential argv array --------
|
||||
# Using array form keeps spaces in passwords / paths safe.
|
||||
NOTARY_ARGS=()
|
||||
if [[ -n "${KEYCHAIN_PROFILE:-}" ]]; then
|
||||
NOTARY_ARGS+=(--keychain-profile "${KEYCHAIN_PROFILE}")
|
||||
elif [[ -n "${APPLE_ID:-}" && -n "${APPLE_TEAM_ID:-}" && -n "${APPLE_APP_SPECIFIC_PASSWORD:-}" ]]; then
|
||||
NOTARY_ARGS+=(--apple-id "${APPLE_ID}"
|
||||
--team-id "${APPLE_TEAM_ID}"
|
||||
--password "${APPLE_APP_SPECIFIC_PASSWORD}")
|
||||
else
|
||||
cat >&2 <<EOF
|
||||
[notarize.sh] ERROR: notary credentials not configured.
|
||||
|
||||
Option A (recommended) — store once in keychain, then export KEYCHAIN_PROFILE:
|
||||
xcrun notarytool store-credentials "snapgo-notary" \\
|
||||
--apple-id "you@example.com" \\
|
||||
--team-id "ABCDE12345" \\
|
||||
--password "<app-specific-password>"
|
||||
export KEYCHAIN_PROFILE=snapgo-notary
|
||||
|
||||
Option B — export env vars directly:
|
||||
export APPLE_ID="you@example.com"
|
||||
export APPLE_TEAM_ID="ABCDE12345"
|
||||
export APPLE_APP_SPECIFIC_PASSWORD="abcd-efgh-ijkl-mnop"
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[notarize.sh] submitting: ${DMG_PATH}"
|
||||
xcrun notarytool submit "${DMG_PATH}" "${NOTARY_ARGS[@]}" --wait
|
||||
|
||||
echo "[notarize.sh] stapling ticket..."
|
||||
xcrun stapler staple "${DMG_PATH}"
|
||||
xcrun stapler validate "${DMG_PATH}"
|
||||
|
||||
# Also staple the .app inside, if we still have it on disk — this ensures the
|
||||
# offline Gatekeeper check works even if the user copies the app out of the DMG.
|
||||
APP_PATH="${ROOT_DIR}/build/bin/SnapGo.app"
|
||||
if [[ -d "${APP_PATH}" ]]; then
|
||||
echo "[notarize.sh] stapling app bundle..."
|
||||
xcrun stapler staple "${APP_PATH}" || true
|
||||
fi
|
||||
|
||||
echo "[notarize.sh] OK -> ${DMG_PATH}"
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# release.sh — One-shot build → sign → DMG → notarize → staple pipeline.
|
||||
#
|
||||
# Design rationale:
|
||||
# - Each stage is delegated to a dedicated single-purpose script. This keeps the
|
||||
# pipeline composable: a developer can re-run any individual stage without
|
||||
# redoing the whole release.
|
||||
# - Notarization is OPT-IN. Setting NOTARIZE=1 (or providing notary credentials)
|
||||
# triggers it; otherwise we stop after producing a signed DMG, which is enough
|
||||
# for internal distribution and CI smoke tests.
|
||||
# - ARCH defaults to the host arch so a developer on Apple Silicon gets an
|
||||
# arm64 build. Set ARCH=universal to ship a fat binary.
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
ARCH="${ARCH:-$(uname -m)}"
|
||||
case "${ARCH}" in
|
||||
arm64) WAILS_PLATFORM="darwin/arm64" ;;
|
||||
x86_64) WAILS_PLATFORM="darwin/amd64" ;;
|
||||
universal) WAILS_PLATFORM="darwin/universal" ;;
|
||||
*) echo "[release.sh] ERROR: unsupported ARCH=${ARCH}" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
echo "================================================================"
|
||||
echo " SnapGo release pipeline"
|
||||
echo " arch : ${ARCH} (${WAILS_PLATFORM})"
|
||||
echo " sign : ${SIGN_MODE:-auto}"
|
||||
echo " notarize : ${NOTARIZE:-0}"
|
||||
echo "================================================================"
|
||||
|
||||
# ---- 1. Build ----
|
||||
echo ""
|
||||
echo "[release.sh] (1/4) wails build"
|
||||
( cd "${ROOT_DIR}" && wails build -platform "${WAILS_PLATFORM}" -clean )
|
||||
|
||||
# ---- 2. Sign .app ----
|
||||
echo ""
|
||||
echo "[release.sh] (2/4) sign app"
|
||||
"${SCRIPT_DIR}/sign.sh"
|
||||
|
||||
# ---- 3. DMG ----
|
||||
echo ""
|
||||
echo "[release.sh] (3/4) make DMG"
|
||||
ARCH="${ARCH}" "${SCRIPT_DIR}/make-dmg.sh"
|
||||
|
||||
# ---- 4. Notarize (optional) ----
|
||||
echo ""
|
||||
if [[ "${NOTARIZE:-0}" == "1" || -n "${KEYCHAIN_PROFILE:-}" \
|
||||
|| ( -n "${APPLE_ID:-}" && -n "${APPLE_TEAM_ID:-}" \
|
||||
&& -n "${APPLE_APP_SPECIFIC_PASSWORD:-}" ) ]]; then
|
||||
echo "[release.sh] (4/4) notarize"
|
||||
"${SCRIPT_DIR}/notarize.sh"
|
||||
else
|
||||
echo "[release.sh] (4/4) notarize SKIPPED (set NOTARIZE=1 with credentials to enable)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "[release.sh] DONE."
|
||||
ls -lh "${ROOT_DIR}/build/bin/"SnapGo-*.dmg 2>/dev/null || true
|
||||
Executable
+134
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# sign.sh — Sign SnapGo.app with Hardened Runtime.
|
||||
#
|
||||
# Design rationale:
|
||||
# - Two modes are supported, controlled purely by env vars (no CLI args), so
|
||||
# the script stays idempotent and easy to wire into CI:
|
||||
# 1. "developer-id" : real Developer ID Application signing for distribution
|
||||
# (requires DEVELOPER_ID_APPLICATION env or default
|
||||
# lookup via `security find-identity`).
|
||||
# 2. "ad-hoc" : codesign with `-` identity. The bundle works on the
|
||||
# building machine after `xattr -dr com.apple.quarantine`,
|
||||
# but cannot be notarized.
|
||||
# - We always sign with --options=runtime so the bundle is notarization-ready.
|
||||
# - We sign nested binaries first, then the .app bundle (Apple's required order).
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
# -------- Paths --------
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
APP_PATH="${APP_PATH:-${ROOT_DIR}/build/bin/SnapGo.app}"
|
||||
ENTITLEMENTS="${ENTITLEMENTS:-${ROOT_DIR}/build/darwin/entitlements.plist}"
|
||||
|
||||
# -------- Mode resolution --------
|
||||
# SIGN_MODE=developer-id | ad-hoc (default: ad-hoc when no identity is found)
|
||||
SIGN_MODE="${SIGN_MODE:-auto}"
|
||||
SIGN_IDENTITY=""
|
||||
|
||||
resolve_identity() {
|
||||
if [[ -n "${DEVELOPER_ID_APPLICATION:-}" ]]; then
|
||||
SIGN_IDENTITY="${DEVELOPER_ID_APPLICATION}"
|
||||
SIGN_MODE="developer-id"
|
||||
return
|
||||
fi
|
||||
# Try to auto-discover the first Developer ID Application identity.
|
||||
local found
|
||||
found=$(security find-identity -v -p codesigning 2>/dev/null \
|
||||
| awk -F'"' '/Developer ID Application/ {print $2; exit}')
|
||||
if [[ -n "${found}" ]]; then
|
||||
SIGN_IDENTITY="${found}"
|
||||
SIGN_MODE="developer-id"
|
||||
else
|
||||
SIGN_IDENTITY="-"
|
||||
SIGN_MODE="ad-hoc"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "${SIGN_MODE}" == "auto" ]]; then
|
||||
resolve_identity
|
||||
elif [[ "${SIGN_MODE}" == "developer-id" ]]; then
|
||||
if [[ -z "${DEVELOPER_ID_APPLICATION:-}" ]]; then
|
||||
resolve_identity
|
||||
if [[ "${SIGN_MODE}" != "developer-id" ]]; then
|
||||
echo "[sign.sh] ERROR: SIGN_MODE=developer-id but no Developer ID Application identity found." >&2
|
||||
echo " Set DEVELOPER_ID_APPLICATION='Developer ID Application: NAME (TEAMID)'" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
SIGN_IDENTITY="${DEVELOPER_ID_APPLICATION}"
|
||||
fi
|
||||
elif [[ "${SIGN_MODE}" == "ad-hoc" ]]; then
|
||||
SIGN_IDENTITY="-"
|
||||
else
|
||||
echo "[sign.sh] ERROR: invalid SIGN_MODE='${SIGN_MODE}'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# -------- Sanity check --------
|
||||
if [[ ! -d "${APP_PATH}" ]]; then
|
||||
echo "[sign.sh] ERROR: app bundle not found at ${APP_PATH}" >&2
|
||||
echo " Run 'wails build -platform darwin/arm64 -clean' first." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "${ENTITLEMENTS}" ]]; then
|
||||
echo "[sign.sh] ERROR: entitlements not found at ${ENTITLEMENTS}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[sign.sh] mode = ${SIGN_MODE}"
|
||||
echo "[sign.sh] identity = ${SIGN_IDENTITY}"
|
||||
echo "[sign.sh] app = ${APP_PATH}"
|
||||
echo "[sign.sh] ents = ${ENTITLEMENTS}"
|
||||
|
||||
# -------- Decide whether to request RFC3161 timestamp --------
|
||||
# Apple's timestamp server only accepts signatures from Developer ID-issued
|
||||
# certificates. For ad-hoc or local self-signed certs the timestamp call
|
||||
# either fails or attaches noise that makes re-signing nondeterministic
|
||||
# (which in turn breaks TCC's "is this the same app?" comparison).
|
||||
TIMESTAMP_FLAG="--timestamp=none"
|
||||
if [[ "${SIGN_MODE}" == "developer-id" && "${SIGN_IDENTITY}" == "Developer ID Application:"* ]]; then
|
||||
TIMESTAMP_FLAG="--timestamp"
|
||||
fi
|
||||
echo "[sign.sh] timestamp = ${TIMESTAMP_FLAG}"
|
||||
|
||||
# -------- Strip stale signatures so re-signing is deterministic --------
|
||||
codesign --remove-signature "${APP_PATH}" 2>/dev/null || true
|
||||
|
||||
# -------- Sign nested executables first (Apple required ordering) --------
|
||||
# Find any embedded Mach-O binaries / frameworks / dylibs.
|
||||
NESTED_PATHS=()
|
||||
while IFS= read -r path; do
|
||||
NESTED_PATHS+=("$path")
|
||||
done < <(find "${APP_PATH}/Contents" \
|
||||
\( -name "*.dylib" -o -name "*.framework" -o -path "*/Frameworks/*" \) \
|
||||
-not -path "*/_CodeSignature/*" 2>/dev/null || true)
|
||||
|
||||
for nested in "${NESTED_PATHS[@]:-}"; do
|
||||
[[ -z "${nested}" ]] && continue
|
||||
echo "[sign.sh] signing nested: ${nested}"
|
||||
codesign --force ${TIMESTAMP_FLAG} --options=runtime \
|
||||
--sign "${SIGN_IDENTITY}" \
|
||||
"${nested}"
|
||||
done
|
||||
|
||||
# -------- Sign the main app bundle --------
|
||||
echo "[sign.sh] signing app bundle..."
|
||||
codesign --force ${TIMESTAMP_FLAG} --options=runtime \
|
||||
--entitlements "${ENTITLEMENTS}" \
|
||||
--sign "${SIGN_IDENTITY}" \
|
||||
"${APP_PATH}"
|
||||
|
||||
# -------- Verify --------
|
||||
echo "[sign.sh] verifying signature..."
|
||||
codesign --verify --deep --strict --verbose=2 "${APP_PATH}"
|
||||
|
||||
if [[ "${SIGN_MODE}" == "developer-id" ]]; then
|
||||
# Gatekeeper assessment will pass only after notarization + stapling,
|
||||
# but we can pre-flight the spctl evaluation now.
|
||||
echo "[sign.sh] spctl assessment (will likely say 'rejected' until notarized)..."
|
||||
spctl --assess --type execute --verbose=4 "${APP_PATH}" || true
|
||||
fi
|
||||
|
||||
echo "[sign.sh] OK."
|
||||
Reference in New Issue
Block a user