#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" APP_NAME="Firelink" CONFIGURATION="${CONFIGURATION:-release}" DEFAULT_MARKETING_VERSION="$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || true)" DEFAULT_BUILD_NUMBER="$(git rev-list --count HEAD 2>/dev/null || true)" MARKETING_VERSION="${MARKETING_VERSION:-${DEFAULT_MARKETING_VERSION:-0.1.0}}" BUILD_NUMBER="${BUILD_NUMBER:-${DEFAULT_BUILD_NUMBER:-1}}" SIGNING_IDENTITY="${CODE_SIGN_IDENTITY:-${SIGNING_IDENTITY:-}}" APP_DIR="$ROOT_DIR/build/$APP_NAME.app" CONTENTS_DIR="$APP_DIR/Contents" MACOS_DIR="$CONTENTS_DIR/MacOS" RESOURCES_DIR="$CONTENTS_DIR/Resources" ICON_NAME="AppIcon" cd "$ROOT_DIR" is_valid_mach_o() { local path="$1" if ! file "$path" | grep -q 'Mach-O'; then return 1 fi lipo -archs "$path" >/dev/null 2>&1 } ensure_ytdlp() { local ytdlp_path="$ROOT_DIR/Sources/Firelink/yt-dlp" if [[ -x "$ytdlp_path" ]] && is_valid_mach_o "$ytdlp_path"; then return fi if ! command -v curl >/dev/null; then echo "WARNING: yt-dlp is missing, and curl is not available to refresh it." >&2 return fi echo "Refreshing bundled yt-dlp runtime..." curl -fsSL https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos -o "$ytdlp_path" chmod +x "$ytdlp_path" # Remove legacy _internal directory if it exists rm -rf "$ROOT_DIR/Sources/Firelink/_internal" } ensure_ytdlp swift build -c "$CONFIGURATION" rm -rf "$APP_DIR" mkdir -p "$MACOS_DIR" "$RESOURCES_DIR" cp ".build/$CONFIGURATION/$APP_NAME" "$MACOS_DIR/$APP_NAME" cp "$ROOT_DIR/Resources/$ICON_NAME.icns" "$RESOURCES_DIR/$ICON_NAME.icns" cp "$ROOT_DIR/Sources/Firelink/Assets.xcassets/MenuBarIcon.imageset/MenuBarIconTemplate.png" "$RESOURCES_DIR/MenuBarIconTemplate.png" cp "$ROOT_DIR/Resources/GitHubTemplate.png" "$RESOURCES_DIR/GitHubTemplate.png" for media_engine in yt-dlp ffmpeg; do media_engine_path="$ROOT_DIR/Sources/Firelink/$media_engine" if [[ -x "$media_engine_path" ]]; then cp "$media_engine_path" "$RESOURCES_DIR/$media_engine" chmod +x "$RESOURCES_DIR/$media_engine" else echo "WARNING: $media_engine not found or not executable at $media_engine_path" fi done if [[ -d "$ROOT_DIR/Sources/Firelink/_internal" ]]; then cp -R "$ROOT_DIR/Sources/Firelink/_internal" "$RESOURCES_DIR/_internal" fi echo "Packaging Firefox extension..." mkdir -p "$RESOURCES_DIR/FirefoxExtension" cp "$ROOT_DIR/Extensions/Firefox/background.js" "$RESOURCES_DIR/FirefoxExtension/background.js" cp "$ROOT_DIR/Extensions/Firefox/content.js" "$RESOURCES_DIR/FirefoxExtension/content.js" cp "$ROOT_DIR/Extensions/Firefox/manifest.json" "$RESOURCES_DIR/FirefoxExtension/manifest.json" cp -R "$ROOT_DIR/Extensions/Firefox/icons" "$RESOURCES_DIR/FirefoxExtension/icons" cp -R "$ROOT_DIR/Extensions/Firefox/popup" "$RESOURCES_DIR/FirefoxExtension/popup" ARIA2C_PATH=$(which aria2c || true) if [[ -n "$ARIA2C_PATH" && -x "$ARIA2C_PATH" ]]; then echo "Bundling aria2c from $ARIA2C_PATH..." cp "$ARIA2C_PATH" "$RESOURCES_DIR/aria2c" if ! command -v dylibbundler &> /dev/null; then echo "Installing dylibbundler..." brew install dylibbundler fi FRAMEWORKS_DIR="$CONTENTS_DIR/Frameworks" mkdir -p "$FRAMEWORKS_DIR" dylibbundler -od -b -x "$RESOURCES_DIR/aria2c" -d "$FRAMEWORKS_DIR" -p "@executable_path/../Frameworks/" else echo "WARNING: aria2c not found! It will not be bundled. Please install it first." fi FRAMEWORKS_DIR="$CONTENTS_DIR/Frameworks" mkdir -p "$FRAMEWORKS_DIR" install_name_tool -add_rpath "@executable_path/../Frameworks" "$MACOS_DIR/$APP_NAME" || true cat > "$CONTENTS_DIR/Info.plist" < CFBundleDevelopmentRegion en CFBundleExecutable $APP_NAME CFBundleIdentifier local.firelink.swiftui CFBundleInfoDictionaryVersion 6.0 CFBundleName $APP_NAME CFBundleIconFile $ICON_NAME CFBundlePackageType APPL CFBundleShortVersionString $MARKETING_VERSION CFBundleVersion $BUILD_NUMBER LSMinimumSystemVersion 14.0 LSApplicationCategoryType public.app-category.utilities NSHighResolutionCapable NSAppleEventsUsageDescription Firelink needs permission to control Finder so it can sleep, restart, or shut down your Mac after scheduled downloads finish. CFBundleURLTypes CFBundleURLName local.firelink.swiftui CFBundleURLSchemes firelink PLIST if command -v codesign &> /dev/null; then if [[ -n "$SIGNING_IDENTITY" ]]; then CODESIGN_ARGS=(--force --timestamp --options runtime --sign "$SIGNING_IDENTITY") echo "Signing app bundle with identity: $SIGNING_IDENTITY" else CODESIGN_ARGS=(--force --sign -) echo "Ad-hoc signing app bundle for local use." fi sign_path() { local path="$1" codesign "${CODESIGN_ARGS[@]}" "$path" } sign_mach_o_file() { local path="$1" if file "$path" | grep -q 'Mach-O'; then if ! sign_path "$path"; then echo "WARNING: Could not sign Mach-O executable for local build: $path" >&2 fi fi } if [[ -d "$FRAMEWORKS_DIR" ]]; then while IFS= read -r -d '' executable_path; do sign_mach_o_file "$executable_path" done < <(find "$FRAMEWORKS_DIR" -type f -print0) while IFS= read -r -d '' bundle_path; do sign_path "$bundle_path" done < <(find "$FRAMEWORKS_DIR" \( -name "*.xpc" -o -name "*.framework" \) -type d -depth -print0) fi while IFS= read -r -d '' executable_path; do sign_mach_o_file "$executable_path" done < <(find "$RESOURCES_DIR" -type f -print0) sign_path "$MACOS_DIR/$APP_NAME" sign_path "$APP_DIR" codesign --verify --deep --verbose=2 "$APP_DIR" || true fi echo "Created $APP_DIR"