chore: import upstream snapshot with attribution
This commit is contained in:
Executable
+150
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================================
|
||||
# build-local.sh, Build CodeBurnMenubar.app on a macOS 14 (Sonoma) machine.
|
||||
# ============================================================================
|
||||
# Why this exists
|
||||
# ---------------
|
||||
# Package.swift's `.macOS(.v14)` deployment target already fixes the -10825
|
||||
# launch failure for every build, including the CI-distributed release: ld64
|
||||
# drops the macOS-15-only libswift_errno.dylib dependency based on the
|
||||
# deployment target, not the SDK used to build. This script is not about that.
|
||||
#
|
||||
# It exists for the narrower case of building on a Sonoma machine that only
|
||||
# has the Command Line Tools (macOS 14 SDK). That SDK's SwiftUI does NOT carry
|
||||
# the @MainActor annotations the macOS 15 SDK added to the `View` protocol, so
|
||||
# a plain `swift build` there fails with ~80 `main actor-isolated ... from a
|
||||
# nonisolated context` errors. This script copies the sources to a scratch
|
||||
# dir, gives every `View`/`App` conformance an explicit `@MainActor` there
|
||||
# (repo sources stay untouched), and builds a universal bundle with a
|
||||
# swift.org Swift 6.2 toolchain against the local macOS 14 SDK.
|
||||
#
|
||||
# Prerequisites
|
||||
# - Command Line Tools (provides the macOS 14 SDK + sips/iconutil/codesign)
|
||||
# - A swift.org Swift 6.x toolchain in ~/Library/Developer/Toolchains/
|
||||
# download: https://www.swift.org/install/macos/ (Swift 6.2 recommended)
|
||||
#
|
||||
# Usage: mac/Scripts/build-local.sh [<version>] (defaults to "dev")
|
||||
# ----------------------------------------------------------------------------
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="${1:-dev}"
|
||||
BUNDLE_ID="org.agentseal.codeburn-menubar"
|
||||
EXE="CodeBurnMenubar"
|
||||
MIN_MACOS="14.0"
|
||||
|
||||
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
|
||||
MAC_DIR="${ROOT}/mac"
|
||||
ICON_SOURCE="${ROOT}/assets/menubar-logo.png"
|
||||
SCRATCH="$(mktemp -d /tmp/codeburn-local-build.XXXXXX)"
|
||||
APPS="${HOME}/Applications"
|
||||
BUNDLE="${APPS}/${EXE}.app"
|
||||
|
||||
trap 'rm -rf "${SCRATCH}"' EXIT
|
||||
|
||||
# --- locate a Swift 6.x toolchain -------------------------------------------
|
||||
TC=""
|
||||
for cand in "${HOME}/Library/Developer/Toolchains/swift-6.2-RELEASE.xctoolchain" \
|
||||
"${HOME}/Library/Developer/Toolchains/swift-latest.xctoolchain" \
|
||||
/Library/Developer/Toolchains/swift-latest.xctoolchain; do
|
||||
[[ -x "${cand}/usr/bin/swift" ]] && { TC="${cand}"; break; }
|
||||
done
|
||||
if [[ -z "${TC}" ]]; then
|
||||
echo "✗ No swift.org Swift 6.x toolchain found in ~/Library/Developer/Toolchains/." >&2
|
||||
echo " Install one from https://www.swift.org/install/macos/ (Swift 6.2)." >&2
|
||||
exit 1
|
||||
fi
|
||||
SWIFT="${TC}/usr/bin/swift"
|
||||
export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"
|
||||
SDK_VERSION="$(xcrun --sdk macosx --show-sdk-version)"
|
||||
case "${SDK_VERSION}" in
|
||||
14.*) ;;
|
||||
*)
|
||||
echo "✗ Active SDK is macOS ${SDK_VERSION}, not 14.x, xcode-select is likely" >&2
|
||||
echo " pointed at a newer Xcode instead of the Command Line Tools. Run:" >&2
|
||||
echo " sudo xcode-select -s /Library/Developer/CommandLineTools" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
echo "▸ Toolchain : $("${SWIFT}" --version | head -1)"
|
||||
echo "▸ SDK : ${SDKROOT} (${SDK_VERSION})"
|
||||
|
||||
# --- copy sources and add explicit @MainActor to SwiftUI views --------------
|
||||
echo "▸ Staging sources in ${SCRATCH}..."
|
||||
# Tests/ is copied only so the manifest's testTarget path resolves; `swift build`
|
||||
# (product only) never compiles it, so it needs no @MainActor patching.
|
||||
cp -R "${MAC_DIR}/Sources" "${MAC_DIR}/Tests" "${MAC_DIR}/Package.swift" "${SCRATCH}/"
|
||||
find "${SCRATCH}/Sources" -name "*.swift" -print0 | while IFS= read -r -d '' f; do
|
||||
# Slurp mode ([^{]* spans newlines) so this also catches multi-line generic
|
||||
# struct headers and `extension X: View` conformances, not just the
|
||||
# single-line `struct X: View {` shape the current sources happen to use.
|
||||
perl -0777 -i -pe '
|
||||
s/^((?:private |public |fileprivate |internal )?(?:struct|extension)\s+\w+(?:<[^{]*?>)?\s*:[^{]*\bView\b[^{]*\{)/\@MainActor\n$1/gm;
|
||||
s/^(struct\s+\w+\s*:[^{]*\bApp\b[^{]*\{)/\@MainActor\n$1/gm;
|
||||
s/\@MainActor\n\@MainActor\n/\@MainActor\n/g;
|
||||
' "$f"
|
||||
done
|
||||
|
||||
# --- build each arch separately, then lipo into one universal binary --------
|
||||
# `swift build --arch arm64 --arch x86_64` together shells out to xcbuild,
|
||||
# which the Command Line Tools doesn't ship, each arch alone stays on the
|
||||
# plain SwiftPM build path, so build twice and merge with lipo instead.
|
||||
BINS=()
|
||||
for arch in arm64 x86_64; do
|
||||
echo "▸ Building ${arch} release..."
|
||||
( cd "${SCRATCH}" && "${SWIFT}" build -c release --arch "${arch}" )
|
||||
bin="$(cd "${SCRATCH}" && "${SWIFT}" build -c release --arch "${arch}" --show-bin-path)/${EXE}"
|
||||
[[ -x "${bin}" ]] || { echo "✗ ${arch} build produced no binary" >&2; exit 1; }
|
||||
BINS+=("${bin}")
|
||||
done
|
||||
BIN="${SCRATCH}/${EXE}-universal"
|
||||
lipo -create -output "${BIN}" "${BINS[@]}"
|
||||
|
||||
# --- assemble the .app bundle ------------------------------------------------
|
||||
echo "▸ Assembling ${BUNDLE}..."
|
||||
pkill -x "${EXE}" 2>/dev/null || true; sleep 1
|
||||
rm -rf "${BUNDLE}"
|
||||
mkdir -p "${BUNDLE}/Contents/MacOS" "${BUNDLE}/Contents/Resources"
|
||||
cp "${BIN}" "${BUNDLE}/Contents/MacOS/${EXE}"
|
||||
cp "${ICON_SOURCE}" "${BUNDLE}/Contents/Resources/menubar-logo.png"
|
||||
|
||||
ICONSET="${SCRATCH}/AppIcon.iconset"; mkdir -p "${ICONSET}"
|
||||
for spec in "16:16x16" "32:16x16@2x" "32:32x32" "64:32x32@2x" "128:128x128" \
|
||||
"256:128x128@2x" "256:256x256" "512:256x256@2x" "512:512x512"; do
|
||||
sips -z "${spec%%:*}" "${spec%%:*}" "${ICON_SOURCE}" --out "${ICONSET}/icon_${spec##*:}.png" >/dev/null
|
||||
done
|
||||
cp "${ICON_SOURCE}" "${ICONSET}/icon_512x512@2x.png"
|
||||
iconutil -c icns "${ICONSET}" -o "${BUNDLE}/Contents/Resources/AppIcon.icns"
|
||||
|
||||
cat > "${BUNDLE}/Contents/Info.plist" <<PLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key><string>en</string>
|
||||
<key>CFBundleDisplayName</key><string>CodeBurn Menubar</string>
|
||||
<key>CFBundleExecutable</key><string>${EXE}</string>
|
||||
<key>CFBundleIconFile</key><string>AppIcon</string>
|
||||
<key>CFBundleIdentifier</key><string>${BUNDLE_ID}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
|
||||
<key>CFBundleName</key><string>${EXE}</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key><string>${VERSION}</string>
|
||||
<key>CFBundleVersion</key><string>${VERSION}</string>
|
||||
<key>LSMinimumSystemVersion</key><string>${MIN_MACOS}</string>
|
||||
<key>LSUIElement</key><true/>
|
||||
<key>NSHighResolutionCapable</key><true/>
|
||||
<key>NSHumanReadableCopyright</key><string>© AgentSeal</string>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
printf 'APPL????' > "${BUNDLE}/Contents/PkgInfo"
|
||||
|
||||
echo "▸ Ad-hoc signing..."
|
||||
codesign --force --sign - --timestamp=none --deep "${BUNDLE}"
|
||||
codesign --verify --deep --strict "${BUNDLE}"
|
||||
|
||||
echo ""
|
||||
echo "✓ Installed ${BUNDLE}"
|
||||
lipo -info "${BUNDLE}/Contents/MacOS/${EXE}" | sed 's/^/ /'
|
||||
vtool -show-build "${BUNDLE}/Contents/MacOS/${EXE}" 2>/dev/null | grep -iE "minos|sdk" | sed 's/^/ /'
|
||||
echo " Launch with: codeburn menubar (or: open '${BUNDLE}')"
|
||||
Executable
+156
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds a universal CodeBurnMenubar.app bundle from the SwiftPM target and drops a
|
||||
# distributable zip alongside. Used by the GitHub release workflow; also runnable locally.
|
||||
#
|
||||
# Usage:
|
||||
# mac/Scripts/package-app.sh [<version>]
|
||||
# Defaults to `dev` if no version is given.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="${1:-dev}"
|
||||
ASSET_VERSION="${VERSION#mac-}"
|
||||
BUNDLE_VERSION="${ASSET_VERSION#v}"
|
||||
BUNDLE_NAME="CodeBurnMenubar.app"
|
||||
BUNDLE_ID="org.agentseal.codeburn-menubar"
|
||||
EXECUTABLE_NAME="CodeBurnMenubar"
|
||||
MIN_MACOS="14.0"
|
||||
|
||||
repo_root() {
|
||||
git rev-parse --show-toplevel 2>/dev/null || (cd "$(dirname "$0")/../.." && pwd)
|
||||
}
|
||||
|
||||
ROOT=$(repo_root)
|
||||
MAC_DIR="${ROOT}/mac"
|
||||
DIST_DIR="${MAC_DIR}/.build/dist"
|
||||
ICON_SOURCE="${ROOT}/assets/menubar-logo.png"
|
||||
|
||||
cd "${MAC_DIR}"
|
||||
|
||||
echo "▸ Cleaning previous dist..."
|
||||
rm -rf "${DIST_DIR}"
|
||||
mkdir -p "${DIST_DIR}"
|
||||
|
||||
echo "▸ Building universal binary (arm64 + x86_64)..."
|
||||
swift build -c release --arch arm64 --arch x86_64
|
||||
|
||||
BIN_PATH=$(swift build -c release --arch arm64 --arch x86_64 --show-bin-path)
|
||||
BUILT_BINARY="${BIN_PATH}/${EXECUTABLE_NAME}"
|
||||
if [[ ! -x "${BUILT_BINARY}" ]]; then
|
||||
echo "Binary not found at ${BUILT_BINARY}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "▸ Assembling ${BUNDLE_NAME}..."
|
||||
BUNDLE="${DIST_DIR}/${BUNDLE_NAME}"
|
||||
mkdir -p "${BUNDLE}/Contents/MacOS"
|
||||
mkdir -p "${BUNDLE}/Contents/Resources"
|
||||
cp "${BUILT_BINARY}" "${BUNDLE}/Contents/MacOS/${EXECUTABLE_NAME}"
|
||||
cp "${ICON_SOURCE}" "${BUNDLE}/Contents/Resources/menubar-logo.png"
|
||||
|
||||
ICONSET="${DIST_DIR}/AppIcon.iconset"
|
||||
rm -rf "${ICONSET}"
|
||||
mkdir -p "${ICONSET}"
|
||||
sips -z 16 16 "${ICON_SOURCE}" --out "${ICONSET}/icon_16x16.png" >/dev/null
|
||||
sips -z 32 32 "${ICON_SOURCE}" --out "${ICONSET}/icon_16x16@2x.png" >/dev/null
|
||||
sips -z 32 32 "${ICON_SOURCE}" --out "${ICONSET}/icon_32x32.png" >/dev/null
|
||||
sips -z 64 64 "${ICON_SOURCE}" --out "${ICONSET}/icon_32x32@2x.png" >/dev/null
|
||||
sips -z 128 128 "${ICON_SOURCE}" --out "${ICONSET}/icon_128x128.png" >/dev/null
|
||||
sips -z 256 256 "${ICON_SOURCE}" --out "${ICONSET}/icon_128x128@2x.png" >/dev/null
|
||||
sips -z 256 256 "${ICON_SOURCE}" --out "${ICONSET}/icon_256x256.png" >/dev/null
|
||||
sips -z 512 512 "${ICON_SOURCE}" --out "${ICONSET}/icon_256x256@2x.png" >/dev/null
|
||||
sips -z 512 512 "${ICON_SOURCE}" --out "${ICONSET}/icon_512x512.png" >/dev/null
|
||||
cp "${ICON_SOURCE}" "${ICONSET}/icon_512x512@2x.png"
|
||||
iconutil -c icns "${ICONSET}" -o "${BUNDLE}/Contents/Resources/AppIcon.icns"
|
||||
rm -rf "${ICONSET}"
|
||||
|
||||
cat > "${BUNDLE}/Contents/Info.plist" <<PLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>CodeBurn Menubar</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${BUNDLE_ID}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${BUNDLE_VERSION}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${BUNDLE_VERSION}</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MIN_MACOS}</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>© AgentSeal</string>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
|
||||
cat > "${BUNDLE}/Contents/PkgInfo" <<'PKG'
|
||||
APPL????
|
||||
PKG
|
||||
|
||||
# Sign so macOS treats the bundle as internally consistent. Set CODESIGN_IDENTITY
|
||||
# to a stable identity (Developer ID Application for distribution, or an Apple
|
||||
# Development cert for local testing) so the TCC "access data from other apps"
|
||||
# grant persists across rebuilds. Falls back to ad-hoc when unset (e.g. CI), which
|
||||
# re-prompts on every build because each ad-hoc build has a fresh code identity.
|
||||
CODESIGN_IDENTITY="${CODESIGN_IDENTITY:-}"
|
||||
if [[ -n "${CODESIGN_IDENTITY}" ]]; then
|
||||
echo "▸ Signing with identity: ${CODESIGN_IDENTITY}"
|
||||
codesign --force --sign "${CODESIGN_IDENTITY}" --options runtime --timestamp=none --deep "${BUNDLE}"
|
||||
else
|
||||
echo "▸ Ad-hoc signing (set CODESIGN_IDENTITY for a persistent TCC grant)..."
|
||||
codesign --force --sign - --timestamp=none --deep "${BUNDLE}"
|
||||
fi
|
||||
codesign --verify --deep --strict "${BUNDLE}"
|
||||
|
||||
echo "▸ Verifying deployment target and libswift_errno absence..."
|
||||
BUILT_EXE="${BUNDLE}/Contents/MacOS/${EXECUTABLE_NAME}"
|
||||
MINOS_LINES=$(vtool -show-build "${BUILT_EXE}" 2>/dev/null | awk '/minos/{print $2}')
|
||||
if [[ -z "${MINOS_LINES}" ]]; then
|
||||
echo "Could not read a minos deployment target from ${BUILT_EXE} (vtool returned nothing)." >&2
|
||||
exit 1
|
||||
fi
|
||||
BAD_MINOS=$(printf '%s\n' "${MINOS_LINES}" | grep -v '^14\.0$' || true)
|
||||
if [[ -n "${BAD_MINOS}" ]]; then
|
||||
echo "✗ Expected minos 14.0 for every arch slice, found: ${BAD_MINOS}" >&2
|
||||
echo " Did Package.swift's platforms: [.macOS(...)] regress past .v14?" >&2
|
||||
exit 1
|
||||
fi
|
||||
if otool -L "${BUILT_EXE}" | grep libswift_errno | grep -qv 'weak'; then
|
||||
echo "✗ ${BUILT_EXE} links libswift_errno.dylib (macOS 15+ only), would fail on Sonoma with -10825." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo " minos 14.0 confirmed, no libswift_errno dependency."
|
||||
|
||||
ZIP_NAME="CodeBurnMenubar-${ASSET_VERSION}.zip"
|
||||
ZIP_PATH="${DIST_DIR}/${ZIP_NAME}"
|
||||
echo "▸ Packaging ${ZIP_NAME}..."
|
||||
(cd "${DIST_DIR}" && COPYFILE_DISABLE=1 /usr/bin/ditto -c -k --norsrc --keepParent "${BUNDLE_NAME}" "${ZIP_NAME}")
|
||||
|
||||
CHECKSUM_NAME="${ZIP_NAME}.sha256"
|
||||
CHECKSUM_PATH="${DIST_DIR}/${CHECKSUM_NAME}"
|
||||
echo "▸ Computing SHA-256 checksum..."
|
||||
(cd "${DIST_DIR}" && shasum -a 256 "${ZIP_NAME}" > "${CHECKSUM_NAME}")
|
||||
|
||||
echo ""
|
||||
echo "✓ Built ${ZIP_PATH}"
|
||||
echo "✓ Checksum ${CHECKSUM_PATH}"
|
||||
cat "${CHECKSUM_PATH}"
|
||||
ls -la "${DIST_DIR}"
|
||||
Reference in New Issue
Block a user