mirror of
https://github.com/trycua/lume.git
synced 2026-02-15 02:39:32 -06:00
feat(lume): restructure release binary as .app bundle for bridged networking
Restructure the lume release artifact from a standalone CLI binary into a macOS .app bundle so that a provisioning profile can be loaded by the OS, enabling the com.apple.vm.networking restricted entitlement for bridged networking support in release builds. Closes #1076
This commit is contained in:
11
.github/workflows/cd-swift-lume.yml
vendored
11
.github/workflows/cd-swift-lume.yml
vendored
@@ -31,6 +31,8 @@ on:
|
||||
required: true
|
||||
DEVELOPER_NAME:
|
||||
required: true
|
||||
PROVISIONING_PROFILE_BASE64:
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -43,6 +45,7 @@ env:
|
||||
TEAM_ID: ${{ secrets.TEAM_ID }}
|
||||
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
|
||||
DEVELOPER_NAME: ${{ secrets.DEVELOPER_NAME }}
|
||||
PROVISIONING_PROFILE_BASE64: ${{ secrets.PROVISIONING_PROFILE_BASE64 }}
|
||||
|
||||
jobs:
|
||||
notarize:
|
||||
@@ -137,6 +140,14 @@ jobs:
|
||||
# Clean up certificate files
|
||||
rm application.p12 installer.p12
|
||||
|
||||
- name: Install Provisioning Profile
|
||||
env:
|
||||
PROVISIONING_PROFILE_BASE64: ${{ secrets.PROVISIONING_PROFILE_BASE64 }}
|
||||
run: |
|
||||
echo "Installing provisioning profile..."
|
||||
echo "$PROVISIONING_PROFILE_BASE64" | base64 --decode > libs/lume/resources/embedded.provisionprofile
|
||||
echo "Provisioning profile installed successfully"
|
||||
|
||||
- name: Build and Notarize
|
||||
id: build_notarize
|
||||
env:
|
||||
|
||||
25
libs/lume/resources/Info.plist
Normal file
25
libs/lume/resources/Info.plist
Normal file
@@ -0,0 +1,25 @@
|
||||
<?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>CFBundleIdentifier</key>
|
||||
<string>com.trycua.lume</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>lume</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Lume</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>__VERSION__</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>__VERSION__</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>14.0</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -4,5 +4,7 @@
|
||||
<dict>
|
||||
<key>com.apple.security.virtualization</key>
|
||||
<true/>
|
||||
<key>com.apple.vm.networking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -64,23 +64,61 @@ log "normal" "Ensuring .release directory exists and is accessible"
|
||||
log "essential" "Building release version..."
|
||||
swift build -c release --product lume > /dev/null
|
||||
|
||||
# Sign the binary with hardened runtime entitlements
|
||||
log "essential" "Signing binary with entitlements..."
|
||||
# --- Assemble .app bundle ---
|
||||
log "essential" "Assembling .app bundle..."
|
||||
|
||||
APP_BUNDLE=".release/lume.app"
|
||||
rm -rf "$APP_BUNDLE"
|
||||
mkdir -p "$APP_BUNDLE/Contents/MacOS"
|
||||
|
||||
# Copy the binary into the bundle
|
||||
cp -f .build/release/lume "$APP_BUNDLE/Contents/MacOS/lume"
|
||||
|
||||
# Copy resource bundle (contains unattended presets) alongside the executable
|
||||
# so SPM's Bundle.module resolves correctly
|
||||
BUILD_BUNDLE=".build/release/lume_lume.bundle"
|
||||
if [ -d "$BUILD_BUNDLE" ]; then
|
||||
cp -rf "$BUILD_BUNDLE" "$APP_BUNDLE/Contents/MacOS/"
|
||||
fi
|
||||
|
||||
# Stamp and copy Info.plist
|
||||
sed "s/__VERSION__/$VERSION/g" "./resources/Info.plist" > "$APP_BUNDLE/Contents/Info.plist"
|
||||
|
||||
# Embed the provisioning profile
|
||||
PROVISION_PROFILE="./resources/embedded.provisionprofile"
|
||||
if [ -f "$PROVISION_PROFILE" ]; then
|
||||
cp "$PROVISION_PROFILE" "$APP_BUNDLE/Contents/embedded.provisionprofile"
|
||||
else
|
||||
log "error" "Error: embedded.provisionprofile not found at $PROVISION_PROFILE"
|
||||
log "error" "The provisioning profile is required for the com.apple.vm.networking entitlement."
|
||||
log "error" "Obtain one from the Apple Developer portal tied to bundle ID com.trycua.lume."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Sign the .app bundle ---
|
||||
log "essential" "Signing .app bundle..."
|
||||
|
||||
# Sign the binary inside the bundle first (with entitlements)
|
||||
codesign --force --options runtime \
|
||||
--entitlement ./resources/lume.entitlements \
|
||||
--sign "$CERT_APPLICATION_NAME" \
|
||||
.build/release/lume 2> /dev/null
|
||||
"$APP_BUNDLE/Contents/MacOS/lume" 2> /dev/null
|
||||
|
||||
# Create a temporary directory for packaging
|
||||
TEMP_ROOT=$(mktemp -d)
|
||||
mkdir -p "$TEMP_ROOT/usr/local/bin"
|
||||
cp -f .build/release/lume "$TEMP_ROOT/usr/local/bin/"
|
||||
# Sign the outer bundle
|
||||
codesign --force --options runtime \
|
||||
--sign "$CERT_APPLICATION_NAME" \
|
||||
"$APP_BUNDLE" 2> /dev/null
|
||||
|
||||
# Build the installer package
|
||||
# --- Package as .pkg installer ---
|
||||
log "essential" "Building installer package..."
|
||||
|
||||
TEMP_ROOT=$(mktemp -d)
|
||||
mkdir -p "$TEMP_ROOT/usr/local/share/lume"
|
||||
cp -R "$APP_BUNDLE" "$TEMP_ROOT/usr/local/share/lume/"
|
||||
|
||||
if ! pkgbuild --root "$TEMP_ROOT" \
|
||||
--identifier "com.trycua.lume" \
|
||||
--version "1.0" \
|
||||
--version "$VERSION" \
|
||||
--install-location "/" \
|
||||
--sign "$CERT_INSTALLER_NAME" \
|
||||
./.release/lume.pkg; then
|
||||
@@ -96,7 +134,7 @@ fi
|
||||
|
||||
log "essential" "Package created successfully"
|
||||
|
||||
# Submit for notarization using stored credentials
|
||||
# --- Notarize ---
|
||||
log "essential" "Submitting for notarization..."
|
||||
if [ "$LOG_LEVEL" = "minimal" ] || [ "$LOG_LEVEL" = "none" ]; then
|
||||
# Minimal output - capture ID but hide details
|
||||
@@ -127,82 +165,45 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
# Staple the notarization ticket
|
||||
log "essential" "Stapling notarization ticket..."
|
||||
# Staple the notarization ticket to the .pkg
|
||||
log "essential" "Stapling notarization ticket to .pkg..."
|
||||
if ! xcrun stapler staple ./.release/lume.pkg > /dev/null 2>&1; then
|
||||
log "error" "Failed to staple notarization ticket"
|
||||
log "error" "Failed to staple notarization ticket to .pkg"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create temporary directory for package extraction
|
||||
EXTRACT_ROOT=$(mktemp -d)
|
||||
PKG_PATH="$(pwd)/.release/lume.pkg"
|
||||
|
||||
# Extract the pkg using xar
|
||||
cd "$EXTRACT_ROOT"
|
||||
xar -xf "$PKG_PATH" > /dev/null 2>&1
|
||||
|
||||
# Verify Payload exists before proceeding
|
||||
if [ ! -f "Payload" ]; then
|
||||
log "error" "Error: Payload file not found after xar extraction"
|
||||
exit 1
|
||||
# Staple the notarization ticket to the .app bundle
|
||||
log "essential" "Stapling notarization ticket to .app bundle..."
|
||||
if ! xcrun stapler staple "$APP_BUNDLE" > /dev/null 2>&1; then
|
||||
log "normal" "Note: Could not staple .app bundle directly (this is expected when notarizing via .pkg)"
|
||||
fi
|
||||
|
||||
# Create a directory for the extracted contents
|
||||
mkdir -p extracted
|
||||
cd extracted
|
||||
|
||||
# Extract the Payload
|
||||
cat ../Payload | gunzip -dc | cpio -i > /dev/null 2>&1
|
||||
|
||||
# Verify the binary exists
|
||||
if [ ! -f "usr/local/bin/lume" ]; then
|
||||
log "error" "Error: lume binary not found in expected location"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the release directory absolute path
|
||||
RELEASE_DIR="$(realpath "$(dirname "$PKG_PATH")")"
|
||||
log "normal" "Using release directory: $RELEASE_DIR"
|
||||
|
||||
# Copy extracted lume to the release directory
|
||||
cp -f usr/local/bin/lume "$RELEASE_DIR/lume"
|
||||
|
||||
# Copy the resource bundle (contains unattended presets) from the build directory
|
||||
BUILD_BUNDLE="$LUME_DIR/.build/release/lume_lume.bundle"
|
||||
if [ -d "$BUILD_BUNDLE" ]; then
|
||||
cp -rf "$BUILD_BUNDLE" "$RELEASE_DIR/"
|
||||
fi
|
||||
|
||||
# Install to user-local bin directory (standard location)
|
||||
USER_BIN="$HOME/.local/bin"
|
||||
mkdir -p "$USER_BIN"
|
||||
cp -f "$RELEASE_DIR/lume" "$USER_BIN/lume"
|
||||
|
||||
# Advise user to add to PATH if not present
|
||||
if ! echo "$PATH" | grep -q "$USER_BIN"; then
|
||||
log "normal" "[lume build] Note: $USER_BIN is not in your PATH. Add 'export PATH=\"$USER_BIN:\$PATH\"' to your shell profile."
|
||||
fi
|
||||
# --- Create release archives ---
|
||||
|
||||
# Get architecture and create OS identifier
|
||||
ARCH=$(uname -m)
|
||||
OS_IDENTIFIER="darwin-${ARCH}"
|
||||
RELEASE_DIR="$(cd .release && pwd)"
|
||||
|
||||
# Create versioned archives of the package with OS identifier in the name
|
||||
log "essential" "Creating archives in $RELEASE_DIR..."
|
||||
cd "$RELEASE_DIR"
|
||||
|
||||
# Clean up any existing artifacts first to avoid conflicts
|
||||
rm -f lume-*.tar.gz lume-*.pkg.tar.gz
|
||||
|
||||
# Create a backward-compatible wrapper script at the tarball root
|
||||
cat > lume <<'WRAPPER_EOF'
|
||||
#!/bin/sh
|
||||
exec "$(dirname "$0")/lume.app/Contents/MacOS/lume" "$@"
|
||||
WRAPPER_EOF
|
||||
chmod +x lume
|
||||
|
||||
# Create version-specific archives
|
||||
log "essential" "Creating version-specific archives (${VERSION})..."
|
||||
# Package the binary and resource bundle
|
||||
if [ -d "lume_lume.bundle" ]; then
|
||||
tar -czf "lume-${VERSION}-${OS_IDENTIFIER}.tar.gz" lume lume_lume.bundle > /dev/null 2>&1
|
||||
else
|
||||
tar -czf "lume-${VERSION}-${OS_IDENTIFIER}.tar.gz" lume > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
# Package the .app bundle and wrapper script
|
||||
tar -czf "lume-${VERSION}-${OS_IDENTIFIER}.tar.gz" lume lume.app > /dev/null 2>&1
|
||||
|
||||
# Package the installer
|
||||
tar -czf "lume-${VERSION}-${OS_IDENTIFIER}.pkg.tar.gz" lume.pkg > /dev/null 2>&1
|
||||
|
||||
@@ -220,6 +221,5 @@ chmod 644 "$RELEASE_DIR"/*.tar.gz "$RELEASE_DIR"/*.pkg.tar.gz "$RELEASE_DIR"/che
|
||||
|
||||
# Clean up
|
||||
rm -rf "$TEMP_ROOT"
|
||||
rm -rf "$EXTRACT_ROOT"
|
||||
|
||||
log "essential" "Build and packaging completed successfully."
|
||||
|
||||
@@ -9,25 +9,56 @@ LUME_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
cd "$LUME_DIR"
|
||||
|
||||
swift build -c release --product lume
|
||||
codesign --force --entitlement ./resources/lume.entitlements --sign - .build/release/lume
|
||||
|
||||
mkdir -p ./.release
|
||||
cp -f .build/release/lume ./.release/lume
|
||||
# Assemble .app bundle
|
||||
APP_BUNDLE=".release/lume.app"
|
||||
mkdir -p "$APP_BUNDLE/Contents/MacOS"
|
||||
|
||||
# Copy the resource bundle (contains unattended presets)
|
||||
cp -f .build/release/lume "$APP_BUNDLE/Contents/MacOS/lume"
|
||||
|
||||
# Copy resource bundle alongside the executable for Bundle.module resolution
|
||||
if [ -d ".build/release/lume_lume.bundle" ]; then
|
||||
cp -rf .build/release/lume_lume.bundle ./.release/
|
||||
cp -rf .build/release/lume_lume.bundle "$APP_BUNDLE/Contents/MacOS/"
|
||||
fi
|
||||
|
||||
# Stamp Info.plist with version from VERSION file
|
||||
VERSION=$(cat VERSION 2>/dev/null || echo "0.0.0")
|
||||
sed "s/__VERSION__/$VERSION/g" "./resources/Info.plist" > "$APP_BUNDLE/Contents/Info.plist"
|
||||
|
||||
# Embed provisioning profile if available
|
||||
if [ -f "./resources/embedded.provisionprofile" ]; then
|
||||
cp "./resources/embedded.provisionprofile" "$APP_BUNDLE/Contents/embedded.provisionprofile"
|
||||
fi
|
||||
|
||||
# Ad-hoc sign the bundle
|
||||
codesign --force --entitlement ./resources/lume.entitlements --sign - "$APP_BUNDLE/Contents/MacOS/lume"
|
||||
codesign --force --sign - "$APP_BUNDLE"
|
||||
|
||||
# Create wrapper script
|
||||
mkdir -p .release
|
||||
cat > .release/lume <<'WRAPPER_EOF'
|
||||
#!/bin/sh
|
||||
exec "$(dirname "$0")/lume.app/Contents/MacOS/lume" "$@"
|
||||
WRAPPER_EOF
|
||||
chmod +x .release/lume
|
||||
|
||||
# Install to user-local bin directory (standard location)
|
||||
USER_BIN="$HOME/.local/bin"
|
||||
mkdir -p "$USER_BIN"
|
||||
cp -f ./.release/lume "$USER_BIN/lume"
|
||||
APP_INSTALL_DIR="$HOME/.local/share/lume"
|
||||
|
||||
# Install the resource bundle alongside the binary
|
||||
if [ -d "./.release/lume_lume.bundle" ]; then
|
||||
cp -rf ./.release/lume_lume.bundle "$USER_BIN/"
|
||||
fi
|
||||
mkdir -p "$USER_BIN"
|
||||
mkdir -p "$APP_INSTALL_DIR"
|
||||
|
||||
# Install .app bundle
|
||||
rm -rf "$APP_INSTALL_DIR/lume.app"
|
||||
cp -R ".release/lume.app" "$APP_INSTALL_DIR/"
|
||||
|
||||
# Create wrapper script in bin directory
|
||||
cat > "$USER_BIN/lume" <<WRAPPER_EOF
|
||||
#!/bin/sh
|
||||
exec "$APP_INSTALL_DIR/lume.app/Contents/MacOS/lume" "\$@"
|
||||
WRAPPER_EOF
|
||||
chmod +x "$USER_BIN/lume"
|
||||
|
||||
# Advise user to add to PATH if not present
|
||||
if ! echo "$PATH" | grep -q "$USER_BIN"; then
|
||||
|
||||
@@ -28,6 +28,9 @@ fi
|
||||
DEFAULT_INSTALL_DIR="$HOME/.local/bin"
|
||||
INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}"
|
||||
|
||||
# Default .app bundle installation directory
|
||||
APP_INSTALL_DIR="$HOME/.local/share/lume"
|
||||
|
||||
# Build configuration (debug or release)
|
||||
BUILD_CONFIG="debug"
|
||||
|
||||
@@ -42,6 +45,9 @@ INSTALL_BACKGROUND_SERVICE=true
|
||||
# Default port for lume serve (default: 7777)
|
||||
LUME_PORT=7777
|
||||
|
||||
# Track whether we're using the .app bundle format
|
||||
USE_APP_BUNDLE=false
|
||||
|
||||
# Parse command line arguments
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
@@ -126,17 +132,56 @@ build_lume() {
|
||||
ENTITLEMENTS_FILE="$LUME_DIR/resources/lume.entitlements"
|
||||
fi
|
||||
|
||||
# Codesign the binary
|
||||
echo "Codesigning binary..."
|
||||
codesign --force --entitlements "$ENTITLEMENTS_FILE" --sign - "$BUILD_PATH/lume"
|
||||
if [ "$USE_BRIDGED_ENTITLEMENT" = true ]; then
|
||||
# Assemble .app bundle for provisioning profile support
|
||||
echo "Assembling .app bundle for bridged networking..."
|
||||
|
||||
# Verify the signed binary can launch.
|
||||
if ! "$BUILD_PATH/lume" --version >/dev/null 2>&1; then
|
||||
if [ "$USE_BRIDGED_ENTITLEMENT" = true ]; then
|
||||
echo "${YELLOW}Warning: binary did not launch with bridged entitlement; falling back to local-safe entitlements.${NORMAL}"
|
||||
APP_BUNDLE="$BUILD_PATH/lume.app"
|
||||
rm -rf "$APP_BUNDLE"
|
||||
mkdir -p "$APP_BUNDLE/Contents/MacOS"
|
||||
|
||||
cp -f "$BUILD_PATH/lume" "$APP_BUNDLE/Contents/MacOS/lume"
|
||||
|
||||
# Copy resource bundle alongside the executable
|
||||
if [ -d "$BUILD_PATH/lume_lume.bundle" ]; then
|
||||
cp -rf "$BUILD_PATH/lume_lume.bundle" "$APP_BUNDLE/Contents/MacOS/"
|
||||
fi
|
||||
|
||||
# Stamp Info.plist with version
|
||||
CURRENT_VERSION=$("$BUILD_PATH/lume" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "0.0.0")
|
||||
sed "s/__VERSION__/$CURRENT_VERSION/g" "$LUME_DIR/resources/Info.plist" > "$APP_BUNDLE/Contents/Info.plist"
|
||||
|
||||
# Embed provisioning profile if available
|
||||
if [ -f "$LUME_DIR/resources/embedded.provisionprofile" ]; then
|
||||
cp "$LUME_DIR/resources/embedded.provisionprofile" "$APP_BUNDLE/Contents/embedded.provisionprofile"
|
||||
else
|
||||
echo "${YELLOW}Warning: No provisioning profile found at $LUME_DIR/resources/embedded.provisionprofile${NORMAL}"
|
||||
echo "${YELLOW}Bridged networking requires a provisioning profile from Apple Developer portal.${NORMAL}"
|
||||
fi
|
||||
|
||||
# Sign the bundle
|
||||
codesign --force --entitlements "$ENTITLEMENTS_FILE" --sign - "$APP_BUNDLE/Contents/MacOS/lume"
|
||||
codesign --force --sign - "$APP_BUNDLE"
|
||||
|
||||
# Verify the signed binary can launch from the bundle
|
||||
if "$APP_BUNDLE/Contents/MacOS/lume" --version >/dev/null 2>&1; then
|
||||
USE_APP_BUNDLE=true
|
||||
else
|
||||
echo "${YELLOW}Warning: binary did not launch from .app bundle with bridged entitlement; falling back to standalone binary.${NORMAL}"
|
||||
ENTITLEMENTS_FILE="$LUME_DIR/resources/lume.local.entitlements"
|
||||
codesign --force --entitlements "$ENTITLEMENTS_FILE" --sign - "$BUILD_PATH/lume"
|
||||
USE_APP_BUNDLE=false
|
||||
fi
|
||||
else
|
||||
# Standard standalone binary (no .app bundle needed)
|
||||
codesign --force --entitlements "$ENTITLEMENTS_FILE" --sign - "$BUILD_PATH/lume"
|
||||
|
||||
# Verify the signed binary can launch
|
||||
if ! "$BUILD_PATH/lume" --version >/dev/null 2>&1; then
|
||||
echo "${YELLOW}Warning: binary did not launch; this may indicate a signing issue.${NORMAL}"
|
||||
fi
|
||||
|
||||
USE_APP_BUNDLE=false
|
||||
fi
|
||||
|
||||
echo "${GREEN}Build complete!${NORMAL}"
|
||||
@@ -149,19 +194,43 @@ install_binary() {
|
||||
# Create install directory if it doesn't exist
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
|
||||
# Copy the binary
|
||||
cp -f "$BUILD_PATH/lume" "$INSTALL_DIR/lume"
|
||||
chmod +x "$INSTALL_DIR/lume"
|
||||
if [ "$USE_APP_BUNDLE" = true ]; then
|
||||
# Install as .app bundle with wrapper script
|
||||
mkdir -p "$APP_INSTALL_DIR"
|
||||
rm -rf "$APP_INSTALL_DIR/lume.app"
|
||||
cp -R "$BUILD_PATH/lume.app" "$APP_INSTALL_DIR/"
|
||||
|
||||
# Copy the resource bundle if it exists (contains unattended presets)
|
||||
if [ -d "$BUILD_PATH/lume_lume.bundle" ]; then
|
||||
# Remove old standalone binary if it's a Mach-O file (migration)
|
||||
if [ -f "$INSTALL_DIR/lume" ] && file "$INSTALL_DIR/lume" | grep -q "Mach-O"; then
|
||||
rm -f "$INSTALL_DIR/lume"
|
||||
fi
|
||||
rm -rf "$INSTALL_DIR/lume_lume.bundle"
|
||||
cp -rf "$BUILD_PATH/lume_lume.bundle" "$INSTALL_DIR/"
|
||||
echo "Resource bundle installed to ${BOLD}$INSTALL_DIR/lume_lume.bundle${NORMAL}"
|
||||
fi
|
||||
|
||||
echo "${GREEN}Installation complete!${NORMAL}"
|
||||
echo "Lume has been installed to ${BOLD}$INSTALL_DIR/lume${NORMAL}"
|
||||
# Create wrapper script
|
||||
cat > "$INSTALL_DIR/lume" <<WRAPPER_EOF
|
||||
#!/bin/sh
|
||||
exec "$APP_INSTALL_DIR/lume.app/Contents/MacOS/lume" "\$@"
|
||||
WRAPPER_EOF
|
||||
chmod +x "$INSTALL_DIR/lume"
|
||||
|
||||
echo "${GREEN}Installation complete!${NORMAL}"
|
||||
echo "Lume installed to ${BOLD}$APP_INSTALL_DIR/lume.app${NORMAL}"
|
||||
echo "CLI available at ${BOLD}$INSTALL_DIR/lume${NORMAL}"
|
||||
else
|
||||
# Install as standalone binary
|
||||
cp -f "$BUILD_PATH/lume" "$INSTALL_DIR/lume"
|
||||
chmod +x "$INSTALL_DIR/lume"
|
||||
|
||||
# Copy the resource bundle if it exists (contains unattended presets)
|
||||
if [ -d "$BUILD_PATH/lume_lume.bundle" ]; then
|
||||
rm -rf "$INSTALL_DIR/lume_lume.bundle"
|
||||
cp -rf "$BUILD_PATH/lume_lume.bundle" "$INSTALL_DIR/"
|
||||
echo "Resource bundle installed to ${BOLD}$INSTALL_DIR/lume_lume.bundle${NORMAL}"
|
||||
fi
|
||||
|
||||
echo "${GREEN}Installation complete!${NORMAL}"
|
||||
echo "Lume has been installed to ${BOLD}$INSTALL_DIR/lume${NORMAL}"
|
||||
fi
|
||||
|
||||
# Check if the installation directory is in PATH
|
||||
if [ -n "${PATH##*$INSTALL_DIR*}" ]; then
|
||||
|
||||
@@ -26,6 +26,9 @@ fi
|
||||
DEFAULT_INSTALL_DIR="$HOME/.local/bin"
|
||||
INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}"
|
||||
|
||||
# Default .app bundle installation directory
|
||||
APP_INSTALL_DIR="$HOME/.local/share/lume"
|
||||
|
||||
# GitHub info
|
||||
GITHUB_REPO="trycua/cua"
|
||||
LATEST_RELEASE_URL="https://api.github.com/repos/$GITHUB_REPO/releases/latest"
|
||||
@@ -105,7 +108,7 @@ echo "This script will install Lume to your system."
|
||||
check_permissions() {
|
||||
# System directories that typically require root privileges
|
||||
SYSTEM_DIRS=("/usr/local/bin" "/usr/bin" "/bin" "/opt")
|
||||
|
||||
|
||||
NEEDS_ROOT=false
|
||||
for DIR in "${SYSTEM_DIRS[@]}"; do
|
||||
if [[ "$INSTALL_DIR" == "$DIR"* ]] && [ ! -w "$INSTALL_DIR" ]; then
|
||||
@@ -113,7 +116,7 @@ check_permissions() {
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
if [ "$NEEDS_ROOT" = true ]; then
|
||||
echo "${YELLOW}Warning: Installing to $INSTALL_DIR may require root privileges.${NORMAL}"
|
||||
echo "Consider these alternatives:"
|
||||
@@ -121,7 +124,7 @@ check_permissions() {
|
||||
echo " • Create the directory with correct permissions first:"
|
||||
echo " sudo mkdir -p $INSTALL_DIR && sudo chown $(whoami) $INSTALL_DIR"
|
||||
echo ""
|
||||
|
||||
|
||||
# Check if we already have write permission (might have been set up previously)
|
||||
if [ ! -w "$INSTALL_DIR" ] && [ ! -w "$(dirname "$INSTALL_DIR")" ]; then
|
||||
echo "${RED}Error: You don't have write permission to $INSTALL_DIR${NORMAL}"
|
||||
@@ -135,17 +138,17 @@ check_permissions() {
|
||||
detect_platform() {
|
||||
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
ARCH=$(uname -m)
|
||||
|
||||
|
||||
if [ "$OS" != "darwin" ]; then
|
||||
echo "${RED}Error: Currently only macOS is supported.${NORMAL}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if [ "$ARCH" != "arm64" ]; then
|
||||
echo "${RED}Error: Lume only supports macOS on Apple Silicon (ARM64).${NORMAL}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
PLATFORM="darwin-arm64"
|
||||
echo "Detected platform: ${BOLD}$PLATFORM${NORMAL}"
|
||||
}
|
||||
@@ -254,28 +257,57 @@ download_release() {
|
||||
install_binary() {
|
||||
echo "Extracting archive..."
|
||||
tar -xzf "$TEMP_DIR/lume.tar.gz" -C "$TEMP_DIR"
|
||||
|
||||
echo "Installing to $INSTALL_DIR..."
|
||||
|
||||
# Create install directory if it doesn't exist
|
||||
|
||||
# Create directories
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
|
||||
# Move the binary to the installation directory
|
||||
mv "$TEMP_DIR/lume" "$INSTALL_DIR/"
|
||||
mkdir -p "$APP_INSTALL_DIR"
|
||||
|
||||
# Make the binary executable
|
||||
chmod +x "$INSTALL_DIR/lume"
|
||||
if [ -d "$TEMP_DIR/lume.app" ]; then
|
||||
# --- New .app bundle format ---
|
||||
echo "Installing lume.app bundle..."
|
||||
|
||||
# Move the resource bundle if it exists (contains unattended presets)
|
||||
if [ -d "$TEMP_DIR/lume_lume.bundle" ]; then
|
||||
# Remove old standalone binary and resource bundle if present (migration)
|
||||
if [ -f "$INSTALL_DIR/lume" ] && [ ! -L "$INSTALL_DIR/lume" ]; then
|
||||
# It's a regular file (old standalone binary), not a symlink or script
|
||||
if file "$INSTALL_DIR/lume" | grep -q "Mach-O"; then
|
||||
echo "Migrating from standalone binary to .app bundle..."
|
||||
rm -f "$INSTALL_DIR/lume"
|
||||
fi
|
||||
fi
|
||||
rm -rf "$INSTALL_DIR/lume_lume.bundle"
|
||||
mv "$TEMP_DIR/lume_lume.bundle" "$INSTALL_DIR/"
|
||||
echo "Resource bundle installed to ${BOLD}$INSTALL_DIR/lume_lume.bundle${NORMAL}"
|
||||
|
||||
# Install the .app bundle
|
||||
rm -rf "$APP_INSTALL_DIR/lume.app"
|
||||
mv "$TEMP_DIR/lume.app" "$APP_INSTALL_DIR/"
|
||||
|
||||
# Create wrapper script so lume is callable from the command line
|
||||
cat > "$INSTALL_DIR/lume" <<WRAPPER_EOF
|
||||
#!/bin/sh
|
||||
exec "$APP_INSTALL_DIR/lume.app/Contents/MacOS/lume" "\$@"
|
||||
WRAPPER_EOF
|
||||
chmod +x "$INSTALL_DIR/lume"
|
||||
|
||||
echo "${GREEN}Installation complete!${NORMAL}"
|
||||
echo "Lume installed to ${BOLD}$APP_INSTALL_DIR/lume.app${NORMAL}"
|
||||
echo "CLI available at ${BOLD}$INSTALL_DIR/lume${NORMAL}"
|
||||
else
|
||||
# --- Legacy standalone binary format ---
|
||||
echo "Installing lume binary..."
|
||||
|
||||
mv "$TEMP_DIR/lume" "$INSTALL_DIR/"
|
||||
chmod +x "$INSTALL_DIR/lume"
|
||||
|
||||
# Move the resource bundle if it exists (contains unattended presets)
|
||||
if [ -d "$TEMP_DIR/lume_lume.bundle" ]; then
|
||||
rm -rf "$INSTALL_DIR/lume_lume.bundle"
|
||||
mv "$TEMP_DIR/lume_lume.bundle" "$INSTALL_DIR/"
|
||||
echo "Resource bundle installed to ${BOLD}$INSTALL_DIR/lume_lume.bundle${NORMAL}"
|
||||
fi
|
||||
|
||||
echo "${GREEN}Installation complete!${NORMAL}"
|
||||
echo "Lume has been installed to ${BOLD}$INSTALL_DIR/lume${NORMAL}"
|
||||
fi
|
||||
|
||||
echo "${GREEN}Installation complete!${NORMAL}"
|
||||
echo "Lume has been installed to ${BOLD}$INSTALL_DIR/lume${NORMAL}"
|
||||
|
||||
# Check if the installation directory is in PATH
|
||||
if [ -n "${PATH##*$INSTALL_DIR*}" ]; then
|
||||
SHELL_NAME=$(basename "$SHELL")
|
||||
@@ -339,6 +371,7 @@ log "Starting Lume update check..."
|
||||
# Find lume binary location
|
||||
LUME_BIN=$(command -v lume 2>/dev/null || echo "$HOME/.local/bin/lume")
|
||||
INSTALL_DIR=$(dirname "$LUME_BIN")
|
||||
APP_INSTALL_DIR="$HOME/.local/share/lume"
|
||||
|
||||
if [ ! -x "$LUME_BIN" ]; then
|
||||
log "ERROR: lume binary not found at $LUME_BIN"
|
||||
@@ -405,22 +438,41 @@ apply_update() {
|
||||
# Stop the daemon before updating
|
||||
launchctl unload "$HOME/Library/LaunchAgents/com.trycua.lume_daemon.plist" 2>/dev/null || true
|
||||
|
||||
# Install new binary
|
||||
mv "$TEMP_DIR/lume" "$INSTALL_DIR/"
|
||||
chmod +x "$INSTALL_DIR/lume"
|
||||
if [ -d "$TEMP_DIR/lume.app" ]; then
|
||||
# New .app bundle format
|
||||
mkdir -p "$APP_INSTALL_DIR"
|
||||
rm -rf "$APP_INSTALL_DIR/lume.app"
|
||||
mv "$TEMP_DIR/lume.app" "$APP_INSTALL_DIR/"
|
||||
|
||||
# Install resource bundle if it exists (contains unattended presets)
|
||||
if [ -d "$TEMP_DIR/lume_lume.bundle" ]; then
|
||||
# Ensure wrapper script exists
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
cat > "$INSTALL_DIR/lume" <<INNER_WRAPPER_EOF
|
||||
#!/bin/sh
|
||||
exec "$APP_INSTALL_DIR/lume.app/Contents/MacOS/lume" "\$@"
|
||||
INNER_WRAPPER_EOF
|
||||
chmod +x "$INSTALL_DIR/lume"
|
||||
|
||||
# Clean up old standalone files
|
||||
rm -rf "$INSTALL_DIR/lume_lume.bundle"
|
||||
mv "$TEMP_DIR/lume_lume.bundle" "$INSTALL_DIR/"
|
||||
log "Resource bundle installed"
|
||||
|
||||
log "Successfully updated lume to version $LATEST_VERSION (.app bundle)"
|
||||
else
|
||||
# Legacy standalone binary format
|
||||
mv "$TEMP_DIR/lume" "$INSTALL_DIR/"
|
||||
chmod +x "$INSTALL_DIR/lume"
|
||||
|
||||
if [ -d "$TEMP_DIR/lume_lume.bundle" ]; then
|
||||
rm -rf "$INSTALL_DIR/lume_lume.bundle"
|
||||
mv "$TEMP_DIR/lume_lume.bundle" "$INSTALL_DIR/"
|
||||
log "Resource bundle installed"
|
||||
fi
|
||||
|
||||
log "Successfully updated lume to version $LATEST_VERSION"
|
||||
fi
|
||||
|
||||
# Restart the daemon
|
||||
launchctl load "$HOME/Library/LaunchAgents/com.trycua.lume_daemon.plist" 2>/dev/null || true
|
||||
|
||||
log "Successfully updated lume to version $LATEST_VERSION"
|
||||
|
||||
# Show macOS notification
|
||||
osascript -e "display notification \"Updated to version $LATEST_VERSION\" with title \"Lume Updated\"" 2>/dev/null || true
|
||||
else
|
||||
@@ -532,8 +584,8 @@ main() {
|
||||
rm -f "$WRAPPER_SCRIPT"
|
||||
fi
|
||||
|
||||
# Create the plist file - runs signed lume binary directly (no wrapper)
|
||||
# This ensures proper code signing identity shows in Login Items
|
||||
# Create the plist file - runs lume via the wrapper script
|
||||
# The wrapper delegates to lume.app/Contents/MacOS/lume
|
||||
cat <<EOF > "$PLIST_PATH"
|
||||
<?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">
|
||||
|
||||
@@ -142,10 +142,25 @@ if [ -n "$LUME_BIN" ] && [ -f "$LUME_BIN" ]; then
|
||||
rm -f "$INSTALL_DIR/lume-daemon"
|
||||
echo " ${GREEN}Removed $INSTALL_DIR/lume-daemon${NORMAL}"
|
||||
fi
|
||||
|
||||
# Remove legacy resource bundle if exists
|
||||
if [ -d "$INSTALL_DIR/lume_lume.bundle" ]; then
|
||||
rm -rf "$INSTALL_DIR/lume_lume.bundle"
|
||||
echo " ${GREEN}Removed $INSTALL_DIR/lume_lume.bundle${NORMAL}"
|
||||
fi
|
||||
else
|
||||
echo " ${YELLOW}Lume binary not found (skipped)${NORMAL}"
|
||||
fi
|
||||
|
||||
# Remove .app bundle if installed (new format)
|
||||
APP_INSTALL_DIR="$HOME/.local/share/lume"
|
||||
if [ -d "$APP_INSTALL_DIR/lume.app" ]; then
|
||||
rm -rf "$APP_INSTALL_DIR/lume.app"
|
||||
echo " ${GREEN}Removed $APP_INSTALL_DIR/lume.app${NORMAL}"
|
||||
# Remove the share directory if empty
|
||||
rmdir "$APP_INSTALL_DIR" 2>/dev/null && echo " ${GREEN}Removed $APP_INSTALL_DIR${NORMAL}" || true
|
||||
fi
|
||||
|
||||
# Remove log files
|
||||
echo ""
|
||||
echo "${BOLD}Removing log files...${NORMAL}"
|
||||
|
||||
Reference in New Issue
Block a user