diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml new file mode 100644 index 0000000..557c62a --- /dev/null +++ b/.github/workflows/build-and-release.yml @@ -0,0 +1,169 @@ +name: Build & Release iOS and macOS Extensions + +on: + push: + branches: + - main + paths: + - '**.swift' + - 'ios/**' + - 'mos/**' + - '.github/workflows/build-and-release.yml' + workflow_dispatch: + inputs: + draft: + description: 'Create as draft (for testing)' + required: true + default: 'true' + type: choice + options: + - 'true' + - 'false' + +jobs: + check-version: + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + version: ${{ steps.extract.outputs.version }} + tag_exists: ${{ steps.check_tag.outputs.exists }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Extract version from Xcode project + id: extract + run: | + VERSION=$(grep -m1 'MARKETING_VERSION = ' ios/Flean.xcodeproj/project.pbxproj | awk -F ' = ' '{ gsub(/;/,"",$2); print $2; exit }') + echo "version=v$VERSION" >> $GITHUB_OUTPUT + echo "Detected version: v$VERSION" + + - name: Check if tag exists + id: check_tag + run: | + if git ls-remote --tags --exit-code origin "refs/tags/${{ steps.extract.outputs.version }}" >/dev/null 2>&1; then + echo "exists=true" >> $GITHUB_OUTPUT + echo "Tag already exists!" + else + echo "exists=false" >> $GITHUB_OUTPUT + echo "Tag does not exist - ready to build" + fi + + build-and-release: + needs: check-version + if: needs.check-version.outputs.tag_exists == 'false' + runs-on: macos-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + + # iOS Build + - name: Build iOS App + run: | + cd ios + mkdir -p build + + xcodebuild build \ + -project Flean.xcodeproj \ + -target Flean \ + -configuration Release \ + -destination 'generic/platform=iOS' \ + -skipPackagePluginValidation \ + SYMROOT=build \ + CODE_SIGN_IDENTITY="" \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGN_ENTITLEMENTS="" + + # Package the .app as a proper IPA (Payload/Flean.app zipped) + APP_PATH=$(find build -name "Flean.app" -type d | head -1) + if [ -z "$APP_PATH" ]; then + echo "❌ Flean.app not found" + exit 1 + fi + mkdir -p build/ipa/Payload + cp -r "$APP_PATH" build/ipa/Payload/ + ( cd build/ipa && zip -r ../Flean.ipa Payload ) + echo "✅ iOS build succeeded" + + # macOS Build + - name: Build macOS App + run: | + cd mos + mkdir -p build + + xcodebuild build \ + -project Flean.xcodeproj \ + -target Flean \ + -configuration Release \ + -skipPackagePluginValidation \ + SYMROOT=build \ + CODE_SIGN_IDENTITY="" \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGN_ENTITLEMENTS="" + + # Find and zip the .app into a fixed location matching the upload step + APP_PATH=$(find build -name "Flean.app" -type d | head -1) + if [ -z "$APP_PATH" ]; then + echo "❌ Flean.app not found" + exit 1 + fi + + # Create a zip whose top-level entry is Flean.app + OUT_ZIP="$(pwd)/build/Flean.app.zip" + APP_DIR="$(dirname "$APP_PATH")" + APP_NAME="$(basename "$APP_PATH")" + ( cd "$APP_DIR" && zip -r "$OUT_ZIP" "$APP_NAME" ) + echo "✅ macOS build succeeded" + + # Create tag + - name: Create Git Tag + run: | + git config user.name "github-actions" + git config user.email "github-actions@github.com" + VERSION="${{ needs.check-version.outputs.version }}" + # Create local tag (no-op if already exists locally from a shallow clone) + git tag "$VERSION" 2>/dev/null || echo "Local tag $VERSION already exists, proceeding with push" + # Push to remote; fail clearly if the tag was created by a concurrent run + if ! git push origin "$VERSION"; then + echo "::error::Tag $VERSION already exists on remote. A release for this version may already exist." + exit 1 + fi + + # Create Release + - name: Create Release + uses: actions/create-release@v1 + id: create_release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ needs.check-version.outputs.version }} + release_name: "Flean ${{ needs.check-version.outputs.version }}" + draft: ${{ github.event.inputs.draft == 'true' }} + prerelease: false + + # Upload iOS App + - name: Upload iOS App + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./ios/build/Flean.ipa + asset_name: Flean.ipa + asset_content_type: application/octet-stream + + # Upload macOS App + - name: Upload macOS App + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./mos/build/Flean.app.zip + asset_name: Flean.app.zip + asset_content_type: application/zip diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml new file mode 100644 index 0000000..71e3f1e --- /dev/null +++ b/.github/workflows/prerelease.yml @@ -0,0 +1,122 @@ +name: Create Prerelease on PR Ready + +on: + pull_request: + branches: + - main + types: + - ready_for_review + paths: + - '.github/workflows/**' + - '**.swift' + - 'ios/**' + - 'mos/**' + +jobs: + create-prerelease: + runs-on: macos-latest + permissions: + contents: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Extract version from Xcode project + id: extract + run: | + VERSION=$(grep -m1 'MARKETING_VERSION = ' ios/Flean.xcodeproj/project.pbxproj | awk -F ' = ' '{ gsub(/;/,"",$2); print $2; exit }') + echo "version=v$VERSION" >> $GITHUB_OUTPUT + echo "Detected version: v$VERSION" + + - name: Build iOS + run: | + cd ios + mkdir -p build + + xcodebuild build \ + -project Flean.xcodeproj \ + -target Flean \ + -configuration Release \ + -destination 'generic/platform=iOS' \ + -skipPackagePluginValidation \ + SYMROOT=build \ + CODE_SIGN_IDENTITY="" \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGN_ENTITLEMENTS="" + + # Package the .app as a proper IPA (Payload/Flean.app zipped) + APP_PATH=$(find build -name "Flean.app" -type d | head -1) + if [ -z "$APP_PATH" ]; then + echo "❌ Flean.app not found" + exit 1 + fi + mkdir -p build/ipa/Payload + cp -r "$APP_PATH" build/ipa/Payload/ + ( cd build/ipa && zip -r ../Flean.ipa Payload ) + echo "✅ iOS build succeeded" + + - name: Build macOS + run: | + cd mos + mkdir -p build + + xcodebuild build \ + -project Flean.xcodeproj \ + -target Flean \ + -configuration Release \ + -skipPackagePluginValidation \ + SYMROOT=build \ + CODE_SIGN_IDENTITY="" \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGN_ENTITLEMENTS="" + + # Find and zip the .app + APP_PATH=$(find build -name "Flean.app" -type d | head -1) + if [ -z "$APP_PATH" ]; then + echo "❌ Flean.app not found" + exit 1 + fi + OUT_ZIP="$(pwd)/build/Flean.app.zip" + APP_DIR="$(dirname "$APP_PATH")" + APP_NAME="$(basename "$APP_PATH")" + ( cd "$APP_DIR" && zip -r "$OUT_ZIP" "$APP_NAME" ) + echo "✅ macOS build succeeded" + + - name: Create Prerelease + uses: actions/create-release@v1 + id: create_prerelease + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.extract.outputs.version }}-prerelease-${{ github.run_number }} + release_name: "Flean ${{ steps.extract.outputs.version }} - Prerelease (PR #${{ github.event.pull_request.number }})" + body: | + 🧪 **Prerelease Build** from PR #${{ github.event.pull_request.number }} + + **Branch:** ${{ github.event.pull_request.head.ref }} + **Author:** @${{ github.event.pull_request.user.login }} + + This is a prerelease. Testing and feedback welcome! + draft: false + prerelease: true + + - name: Upload iOS App + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_prerelease.outputs.upload_url }} + asset_path: ./ios/build/Flean.ipa + asset_name: Flean.ipa + asset_content_type: application/octet-stream + + - name: Upload macOS App + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_prerelease.outputs.upload_url }} + asset_path: ./mos/build/Flean.app.zip + asset_name: Flean.app.zip + asset_content_type: application/zip diff --git a/ios/Flean.xcodeproj/project.pbxproj b/ios/Flean.xcodeproj/project.pbxproj index 894c7cb..510ea3b 100644 --- a/ios/Flean.xcodeproj/project.pbxproj +++ b/ios/Flean.xcodeproj/project.pbxproj @@ -576,20 +576,17 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Flean/Flean.entitlements; CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = PWL627GZ4Y; - ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDisplayName = Flean; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSMainStoryboardFile = Main; - INFOPLIST_KEY_NSPrincipalClass = NSApplication; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "@executable_path/../Frameworks", + "@executable_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.14; MARKETING_VERSION = 2.1.3; OTHER_LDFLAGS = ( "-framework", @@ -599,7 +596,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = slf.Flean; PRODUCT_NAME = "$(TARGET_NAME)"; - REGISTER_APP_GROUPS = YES; + SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; @@ -615,20 +612,17 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Flean/Flean.entitlements; CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = PWL627GZ4Y; - ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDisplayName = Flean; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSMainStoryboardFile = Main; - INFOPLIST_KEY_NSPrincipalClass = NSApplication; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "@executable_path/../Frameworks", + "@executable_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.14; MARKETING_VERSION = 2.1.3; OTHER_LDFLAGS = ( "-framework", @@ -638,7 +632,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = slf.Flean; PRODUCT_NAME = "$(TARGET_NAME)"; - REGISTER_APP_GROUPS = YES; + SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/ios/Flean/iOS/FleanApp.swift b/ios/Flean/iOS/FleanApp.swift index 360973f..f0d8771 100644 --- a/ios/Flean/iOS/FleanApp.swift +++ b/ios/Flean/iOS/FleanApp.swift @@ -1,16 +1,11 @@ -// iOS-only app entry. Guard so the file can live in the repo without -// conflicting with the macOS `@main` app in `Flean/AppDelegate.swift`. -#if os(iOS) import SwiftUI import WebKit @main struct FleanApp: App { - var body: some Scene { - WindowGroup { - WebViewContainer() - } + var body: some Scene { + WindowGroup { + WebViewContainer() } + } } - -#endif