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:
f-trycua
2026-02-11 23:49:11 -08:00
parent 026306719b
commit db2789b723
8 changed files with 334 additions and 129 deletions

View File

@@ -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:

View 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>

View File

@@ -4,5 +4,7 @@
<dict>
<key>com.apple.security.virtualization</key>
<true/>
<key>com.apple.vm.networking</key>
<true/>
</dict>
</plist>

View File

@@ -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."

View File

@@ -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

View File

@@ -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

View File

@@ -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">

View File

@@ -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}"