This repository was archived by the owner on Nov 17, 2025. It is now read-only.
Convert test suite from XCTest to Swift Testing framework #183
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: macOS CodeLooper App | |
| # Code Signing Setup Instructions: | |
| # 1. Export Developer ID Application certificate from Keychain Access: | |
| # - Open Keychain Access | |
| # - Find "Developer ID Application: [Your Name]" certificate | |
| # - Expand to show private key | |
| # - Select BOTH certificate and private key | |
| # - Right-click → Export Items → Save as .p12 with password | |
| # 2. Convert to base64: base64 -i certificate.p12 | pbcopy | |
| # 3. Add GitHub Secrets: | |
| # - MACOS_SIGNING_CERTIFICATE_P12_BASE64: The base64 string | |
| # - MACOS_SIGNING_CERTIFICATE_PASSWORD: The P12 password | |
| # - APP_STORE_CONNECT_API_KEY_P8: Your API key content | |
| # - APP_STORE_CONNECT_KEY_ID: Your API key ID | |
| # - APP_STORE_CONNECT_ISSUER_ID: Your issuer ID | |
| permissions: | |
| contents: read | |
| packages: write | |
| pull-requests: write | |
| on: | |
| # Removed push trigger to eliminate duplicate checks | |
| push: | |
| branches: [main] | |
| paths: | |
| - 'Sources/**' | |
| - 'CodeLooper/**' | |
| - 'Resources/**' | |
| - 'scripts/**' | |
| - '.github/workflows/build-mac-app.yml' | |
| - 'Project.swift' | |
| - 'Package.swift' | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - 'Sources/**' | |
| - 'CodeLooper/**' | |
| - 'Resources/**' | |
| - 'scripts/**' | |
| - '.github/workflows/build-mac-app.yml' | |
| - 'Project.swift' | |
| - 'Package.swift' | |
| workflow_dispatch: # Allows manual triggering | |
| inputs: | |
| notarize: | |
| description: 'Notarize the build (default: true for PRs/tags, respects input for dispatch)' | |
| type: boolean | |
| default: true | |
| create_release: | |
| description: 'Create a release (only for tags)' | |
| type: boolean | |
| default: false | |
| # Prevent multiple workflow runs for the same ref | |
| concurrency: | |
| group: "${{ github.workflow }}-${{ github.ref }}" | |
| cancel-in-progress: true | |
| # Use default Xcode on GitHub runner | |
| # env: | |
| # DEVELOPER_DIR: /Applications/Xcode_16.2.app | |
| jobs: | |
| build_and_process_mac_app: | |
| name: Build, Sign & Optionally Notarize macOS CodeLooper App | |
| runs-on: macos-15 | |
| timeout-minutes: 60 | |
| outputs: | |
| build_outcome: ${{ steps.build_sign_notarize_step.outcome }} | |
| notarize_status: ${{ steps.build_sign_notarize_step.outputs.notarization_status || 'skipped' }} | |
| app_version: ${{ steps.set-version.outputs.version }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| submodules: recursive | |
| # Set up Swift environment - use Xcode 16.3 which includes Swift 6.0.3 | |
| - name: Setup Xcode 16.3 with Swift 6.0.3 | |
| run: | | |
| # List available Xcode versions for debugging | |
| echo "Available Xcode versions:" | |
| ls /Applications/ | grep Xcode || true | |
| # Select Xcode 16.3 (macOS 15 runner should have it) | |
| # Try multiple versions in order of preference | |
| for version in "16.3" "16.2" "16.1" "16.0"; do | |
| if [ -d "/Applications/Xcode_${version}.app" ]; then | |
| echo "Using Xcode ${version}" | |
| sudo xcode-select -s "/Applications/Xcode_${version}.app" | |
| break | |
| fi | |
| done | |
| # Verify we have Swift 6.0 | |
| echo "Swift version:" | |
| swift --version | |
| echo "Xcode version:" | |
| xcodebuild -version | |
| # Verify Swift tools version | |
| SWIFT_TOOLS_VERSION=$(swift package tools-version 2>/dev/null || echo "unknown") | |
| echo "Swift tools version required: $SWIFT_TOOLS_VERSION" | |
| # Check if Swift version is at least 6.0 | |
| SWIFT_VERSION=$(swift --version | grep -o 'Swift version [0-9.]*' | grep -o '[0-9.]*' || echo "0.0") | |
| MAJOR_VERSION=$(echo $SWIFT_VERSION | cut -d. -f1) | |
| if [ "$MAJOR_VERSION" -lt "6" ]; then | |
| echo "::error::Swift 6.0 or higher is required, but found Swift $SWIFT_VERSION" | |
| exit 1 | |
| fi | |
| - name: Set build version and cache fingerprints | |
| id: set-version | |
| run: | | |
| # Generate build number based on date and github run number | |
| RUN_NUMBER="${{ github.run_number }}" | |
| BUILD_NUMBER="$(date +'%Y%m%d')${RUN_NUMBER}" | |
| VERSION_FROM_INFO=$(defaults read "$(pwd)/CodeLooper/Info.plist" CFBundleShortVersionString 2>/dev/null || echo "1.0.0") | |
| VERSION="${VERSION_FROM_INFO}-b${BUILD_NUMBER}" | |
| echo "App Version: $VERSION" | |
| SWIFT_VERSION="$(swift --version | head -n 1)" | |
| SWIFT_VERSION_HASH="$(echo "${SWIFT_VERSION}" | shasum -a 256 | cut -d ' ' -f 1)" | |
| PACKAGE_HASH="$(shasum -a 256 Package.swift 2>/dev/null | cut -d ' ' -f 1 || echo "none")" | |
| BUILD_SCRIPT_HASH="$(shasum -a 256 scripts/build.sh 2>/dev/null | cut -d ' ' -f 1 || echo "none")" | |
| SWIFT_FILES_HASH="$(find Sources -name "*.swift" -type f | sort -r | head -5 | xargs -I{} shasum -a 256 "{}" 2>/dev/null | shasum -a 256 | cut -d ' ' -f 1 || echo "none")" | |
| { | |
| printf "build_number=%s\\n" "${BUILD_NUMBER}" | |
| printf "swift_version=%s\\n" "${SWIFT_VERSION}" | |
| printf "swift_hash=%s\\n" "${SWIFT_VERSION_HASH}" | |
| printf "package_hash=%s\\n" "${PACKAGE_HASH}" | |
| printf "build_script_hash=%s\\n" "${BUILD_SCRIPT_HASH}" | |
| printf "swift_files_hash=%s\\n" "${SWIFT_FILES_HASH}" | |
| printf "version=%s\\n" "${VERSION}" | |
| } >> $GITHUB_OUTPUT | |
| - name: Cache Swift packages | |
| id: cache-spm | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| .build | |
| .swiftpm | |
| Package.resolved | |
| key: ${{ runner.os }}-spm-${{ hashFiles('Package.swift') }}-${{ steps.set-version.outputs.swift_version }} | |
| restore-keys: | | |
| ${{ runner.os }}-spm-${{ hashFiles('Package.swift') }}- | |
| ${{ runner.os }}-spm- | |
| # Temporarily disabled to ensure fresh signed builds | |
| # - name: Cache build artifacts | |
| # id: cache-build | |
| # uses: actions/cache@v4 | |
| # with: | |
| # path: | | |
| # binary | |
| # key: ${{ runner.os }}-binary-${{ hashFiles('Sources/**/*.swift', 'Package.swift', 'scripts/build.sh') }}-${{ steps.set-version.outputs.swift_version }}-signed-v2 | |
| # restore-keys: | | |
| # ${{ runner.os }}-binary-${{ hashFiles('Sources/**/*.swift') }}- | |
| # ${{ runner.os }}-binary- | |
| # Temporarily disabled to ensure fresh builds | |
| # - name: Cache Xcode DerivedData | |
| # uses: actions/cache@v4 | |
| # with: | |
| # path: | | |
| # ~/Library/Developer/Xcode/DerivedData | |
| # ~/Library/Caches/org.swift.swiftpm | |
| # key: ${{ runner.os }}-xcode-${{ hashFiles('Package.swift') }}-${{ github.sha }} | |
| # restore-keys: | | |
| # ${{ runner.os }}-xcode-${{ hashFiles('Package.swift') }}- | |
| # ${{ runner.os }}-xcode- | |
| - name: Prepare P12 Certificate File from Secret | |
| id: prepare_p12_cert | |
| # This step runs if the secret is likely populated AND signing is intended | |
| if: env.P12_BASE64_CONTENT != '' | |
| env: | |
| P12_BASE64_CONTENT: ${{ secrets.MACOS_SIGNING_CERTIFICATE_P12_BASE64 }} | |
| run: | | |
| echo "Preparing P12 certificate file from Base64 secret..." | |
| mkdir -p .private_keys | |
| P12_FILE_PATH="${{ github.workspace }}/.private_keys/ci_signing_cert.p12" | |
| echo "$P12_BASE64_CONTENT" | base64 --decode > "$P12_FILE_PATH" | |
| if [ $? -ne 0 ] || [ ! -s "$P12_FILE_PATH" ]; then | |
| echo "::error::Failed to decode MACOS_SIGNING_CERTIFICATE_P12_BASE64 secret into $P12_FILE_PATH" | |
| echo "p12_file_path=" >> $GITHUB_OUTPUT | |
| else | |
| echo "Successfully decoded P12 to $P12_FILE_PATH" | |
| echo "p12_file_path=$P12_FILE_PATH" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Prepare App Store Connect API Key File (P8) | |
| id: prepare_p8_key | |
| # This step runs if notarization is intended AND the secret is populated | |
| if: env.P8_KEY_CONTENT != '' && !((github.event_name == 'workflow_dispatch' && inputs.notarize == false)) | |
| env: | |
| P8_KEY_CONTENT: ${{ secrets.APP_STORE_CONNECT_API_KEY_P8 }} | |
| run: | | |
| echo "Preparing App Store Connect API Key (.p8) file..." | |
| mkdir -p .private_keys | |
| P8_FILE_PATH="${{ github.workspace }}/.private_keys/AuthKey_ci_rcodesign.p8" | |
| # Write P8 key content directly | |
| echo "$P8_KEY_CONTENT" > "$P8_FILE_PATH" | |
| if [ $? -ne 0 ] || [ ! -s "$P8_FILE_PATH" ]; then | |
| echo "::error::Failed to write APP_STORE_CONNECT_API_KEY_P8 secret to $P8_FILE_PATH" | |
| echo "p8_file_path=" >> $GITHUB_OUTPUT | |
| else | |
| echo "Successfully created P8 key file at $P8_FILE_PATH" | |
| echo "p8_file_path=$P8_FILE_PATH" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Make scripts executable | |
| run: | | |
| chmod +x scripts/build-and-notarize.sh | |
| chmod +x scripts/build.sh | |
| chmod +x scripts/codesign-app.sh | |
| - name: Install mise | |
| run: | | |
| # Install mise (formerly rtx) for tool version management | |
| curl https://mise.run | sh | |
| echo "$HOME/.local/bin" >> $GITHUB_PATH | |
| - name: Install build tools (Tuist via mise, xcbeautify, SwiftLint, SwiftFormat) | |
| run: | | |
| # Install Tuist via mise to ensure consistent version | |
| mise install | |
| eval "$(mise activate bash)" | |
| # Install other tools via Homebrew | |
| brew install xcbeautify swiftlint swiftformat | |
| # Verify Tuist installation | |
| tuist version | |
| tuist --help | head -5 | |
| - name: Generate Xcode project with Tuist | |
| run: | | |
| # Ensure mise environment is activated | |
| eval "$(mise activate bash)" | |
| # Generate Xcode project with Swift 6.0 | |
| ./scripts/generate-xcproj.sh | |
| - name: Run SwiftFormat (check only) | |
| continue-on-error: true | |
| run: | | |
| echo "::group::SwiftFormat Check" | |
| chmod +x scripts/ci-swiftformat.sh | |
| scripts/ci-swiftformat.sh --verbose || echo "::warning::SwiftFormat found formatting issues" | |
| echo "::endgroup::" | |
| - name: Run SwiftLint | |
| continue-on-error: true | |
| run: | | |
| echo "::group::SwiftLint Check" | |
| chmod +x scripts/ci-swiftlint.sh | |
| scripts/ci-swiftlint.sh --verbose || echo "::warning::SwiftLint found linting issues" | |
| echo "::endgroup::" | |
| - name: Run Tests | |
| continue-on-error: true | |
| run: | | |
| echo "::group::Running Tests" | |
| echo "Running test suite..." | |
| # Run Swift Package tests directly to avoid xcodebuild code signing issues | |
| echo "Running Swift Package Manager tests..." | |
| swift test --verbose || echo "::warning::Some tests failed" | |
| # Also run tests in subpackages | |
| echo "Running AXorcist tests..." | |
| cd AXorcist && swift test || echo "::warning::AXorcist tests failed" | |
| cd .. | |
| echo "Running DesignSystem tests..." | |
| cd DesignSystem && swift test || echo "::warning::DesignSystem tests failed" | |
| cd .. | |
| echo "::endgroup::" | |
| - name: Build, Sign, and Optionally Notarize macOS App | |
| id: build_sign_notarize_step | |
| env: | |
| MACOS_SIGNING_P12_FILE_PATH: ${{ steps.prepare_p12_cert.outputs.p12_file_path }} | |
| MACOS_SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_SIGNING_CERTIFICATE_PASSWORD }} | |
| APP_STORE_CONNECT_P8_FILE_PATH: ${{ steps.prepare_p8_key.outputs.p8_file_path }} | |
| APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }} | |
| APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} | |
| APP_VERSION_FOR_SCRIPT: ${{ steps.set-version.outputs.version }} | |
| run: | | |
| echo "Starting Xcode-based build, sign, optional notarization, and DMG creation..." | |
| # Build with xcodebuild using Swift 6.0 and xcbeautify for clean output | |
| echo "Building with Xcode using Swift 6.0..." | |
| set -o pipefail | |
| xcodebuild -workspace CodeLooper.xcworkspace \ | |
| -scheme CodeLooper \ | |
| -configuration Release \ | |
| -destination "generic/platform=macOS" \ | |
| build | xcbeautify | |
| # Find the built app | |
| APP_PATH=$(find ~/Library/Developer/Xcode/DerivedData -name "CodeLooper.app" -type d | head -1) | |
| if [ -z "$APP_PATH" ]; then | |
| echo "::error::Could not find built CodeLooper.app" | |
| exit 1 | |
| fi | |
| echo "Found app at: $APP_PATH" | |
| # Create binary directory and copy app | |
| mkdir -p binary | |
| cp -R "$APP_PATH" binary/ | |
| # Import P12 certificate and sign with hardened runtime | |
| if [ -n "$MACOS_SIGNING_P12_FILE_PATH" ] && [ -f "$MACOS_SIGNING_P12_FILE_PATH" ]; then | |
| echo "Importing P12 certificate to keychain..." | |
| # Create a temporary keychain | |
| TEMP_KEYCHAIN="temp-signing.keychain" | |
| TEMP_KEYCHAIN_PASSWORD="temp-password-$(date +%s)" | |
| # Create and unlock temporary keychain | |
| security create-keychain -p "$TEMP_KEYCHAIN_PASSWORD" "$TEMP_KEYCHAIN" | |
| security set-keychain-settings -lut 21600 "$TEMP_KEYCHAIN" | |
| security unlock-keychain -p "$TEMP_KEYCHAIN_PASSWORD" "$TEMP_KEYCHAIN" | |
| # Add to keychain search list | |
| security list-keychains -d user -s "$TEMP_KEYCHAIN" $(security list-keychains -d user | sed s/\"//g) | |
| # Debug: Check P12 file | |
| echo "P12 file size: $(ls -la "$MACOS_SIGNING_P12_FILE_PATH" | awk '{print $5}') bytes" | |
| echo "P12 file info:" | |
| file "$MACOS_SIGNING_P12_FILE_PATH" | |
| # Additional debugging for certificate issues | |
| echo "Checking P12 contents (without exposing sensitive data):" | |
| openssl pkcs12 -in "$MACOS_SIGNING_P12_FILE_PATH" -info -passin pass:"$MACOS_SIGNING_CERTIFICATE_PASSWORD" -noout 2>&1 | grep -E "MAC:|Certificate:|Private Key:" || echo "Unable to parse P12 file" | |
| # Import certificate - ensure we import both cert and private key | |
| echo "Importing certificate..." | |
| if security import "$MACOS_SIGNING_P12_FILE_PATH" \ | |
| -k "$TEMP_KEYCHAIN" \ | |
| -P "$MACOS_SIGNING_CERTIFICATE_PASSWORD" \ | |
| -T /usr/bin/codesign \ | |
| -T /usr/bin/security \ | |
| -T /usr/bin/productbuild \ | |
| -x \ | |
| -A; then | |
| echo "Certificate import succeeded" | |
| else | |
| echo "::error::Certificate import failed with exit code $?" | |
| exit 1 | |
| fi | |
| # Set keychain partition list to allow codesign access | |
| security set-key-partition-list -S apple-tool:,apple: -s -k "$TEMP_KEYCHAIN_PASSWORD" "$TEMP_KEYCHAIN" | |
| # Debug: Check what's in the keychain | |
| echo "Certificates in keychain:" | |
| security find-certificate -a "$TEMP_KEYCHAIN" | grep -E "alis|labl" | head -20 || echo "No certificates found" | |
| echo "All identities in keychain:" | |
| security find-identity -v "$TEMP_KEYCHAIN" | |
| # Try to dump certificate info | |
| echo "Certificate details:" | |
| security find-certificate -c "Developer" "$TEMP_KEYCHAIN" -p 2>/dev/null | openssl x509 -text -noout 2>/dev/null | grep -E "Subject:|Issuer:|Not" | head -10 || echo "No certificate details available" | |
| # Find the signing identity | |
| echo "Listing all certificates in keychain:" | |
| security find-identity -v -p codesigning "$TEMP_KEYCHAIN" | |
| # Try multiple approaches to find the identity | |
| SIGNING_IDENTITY=$(security find-identity -v -p codesigning "$TEMP_KEYCHAIN" | grep -E "(Developer|Development|Distribution)" | head -1 | grep -o '"[^"]*"' | sed 's/"//g' || true) | |
| # If that fails, try without the codesigning policy | |
| if [ -z "$SIGNING_IDENTITY" ]; then | |
| echo "No codesigning identity found, trying without policy filter..." | |
| SIGNING_IDENTITY=$(security find-identity -v "$TEMP_KEYCHAIN" | grep -E "(Developer|Development|Distribution)" | head -1 | grep -o '"[^"]*"' | sed 's/"//g' || true) | |
| fi | |
| if [ -z "$SIGNING_IDENTITY" ]; then | |
| echo "::warning::Could not find Developer ID Application certificate in keychain" | |
| echo "Available certificates:" | |
| security find-identity -v -p codesigning "$TEMP_KEYCHAIN" || true | |
| echo "::warning::Continuing without code signing" | |
| SKIP_CODESIGN=true | |
| else | |
| SKIP_CODESIGN=false | |
| fi | |
| if [ "$SKIP_CODESIGN" != "true" ]; then | |
| echo "Found signing identity: $SIGNING_IDENTITY" | |
| # Sign embedded frameworks and helpers first | |
| echo "Signing embedded frameworks and helpers..." | |
| find binary/CodeLooper.app -name "*.framework" -o -name "*.dylib" -o -name "*.bundle" | while read -r item; do | |
| echo "Signing: $item" | |
| codesign --force --options runtime \ | |
| --sign "$SIGNING_IDENTITY" \ | |
| --keychain "$TEMP_KEYCHAIN" \ | |
| "$item" || echo "Warning: Failed to sign $item" | |
| done | |
| # Sign the main app with deep signing | |
| echo "Signing main app with hardened runtime..." | |
| codesign --force --deep --options runtime \ | |
| --sign "$SIGNING_IDENTITY" \ | |
| --keychain "$TEMP_KEYCHAIN" \ | |
| binary/CodeLooper.app | |
| else | |
| echo "::warning::Skipping code signing due to missing identity" | |
| fi | |
| # Clean up keychain | |
| security delete-keychain "$TEMP_KEYCHAIN" | |
| else | |
| echo "::warning::No P12 certificate file available, skipping code signing" | |
| fi | |
| # Test notarization if credentials are available and app was signed | |
| if [ -n "$APP_STORE_CONNECT_KEY_ID" ] && [ "$SKIP_CODESIGN" != "true" ]; then | |
| echo "Testing notarization..." | |
| # Download rcodesign | |
| mkdir -p tools/rcodesign/bin | |
| curl -L "https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz" | tar -xz -C tools/rcodesign --strip-components=1 | |
| chmod +x tools/rcodesign/rcodesign | |
| mv tools/rcodesign/rcodesign tools/rcodesign/bin/ | |
| # Create API key JSON | |
| if tools/rcodesign/bin/rcodesign encode-app-store-connect-api-key \ | |
| "$APP_STORE_CONNECT_ISSUER_ID" \ | |
| "$APP_STORE_CONNECT_KEY_ID" \ | |
| "$APP_STORE_CONNECT_P8_FILE_PATH" \ | |
| --output-path /tmp/api_key.json; then | |
| echo "API key JSON created successfully" | |
| else | |
| echo "::warning::Failed to create API key JSON. The P8 key might not be in the correct PEM format." | |
| echo "::warning::Please ensure APP_STORE_CONNECT_API_KEY_P8 secret contains the full P8 file content including:" | |
| echo "::warning::-----BEGIN PRIVATE KEY-----" | |
| echo "::warning::[key content]" | |
| echo "::warning::-----END PRIVATE KEY-----" | |
| echo "notarization_status=failed_api_key" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Submit for notarization | |
| tools/rcodesign/bin/rcodesign notary-submit \ | |
| --api-key-path /tmp/api_key.json \ | |
| --wait \ | |
| binary/CodeLooper.app | |
| echo "notarization_status=success" >> $GITHUB_OUTPUT | |
| elif [ "$SKIP_CODESIGN" = "true" ]; then | |
| echo "::warning::Skipping notarization because app was not signed" | |
| echo "notarization_status=skipped_unsigned" >> $GITHUB_OUTPUT | |
| else | |
| echo "notarization_status=skipped_no_creds" >> $GITHUB_OUTPUT | |
| fi | |
| # Create DMG file | |
| echo "Creating DMG..." | |
| mkdir -p artifacts | |
| # Create a temporary directory for DMG contents | |
| DMG_TEMP="dmg-temp" | |
| mkdir -p "$DMG_TEMP" | |
| cp -R binary/CodeLooper.app "$DMG_TEMP/" | |
| ln -s /Applications "$DMG_TEMP/Applications" | |
| # Create DMG with app and Applications symlink | |
| DMG_NAME="CodeLooper-macOS-${APP_VERSION_FOR_SCRIPT}.dmg" | |
| hdiutil create -volname "CodeLooper" -srcfolder "$DMG_TEMP" -ov -format UDZO "artifacts/$DMG_NAME" | |
| # Clean up | |
| rm -rf "$DMG_TEMP" | |
| echo "✅ Build, sign, notarize, and DMG creation completed successfully." | |
| - name: Verify build artifacts | |
| if: steps.build_sign_notarize_step.outcome == 'success' | |
| run: | | |
| echo "Checking build artifacts..." | |
| APP_PATH="binary/CodeLooper.app" | |
| if [ -d "$APP_PATH" ]; then | |
| echo "✅ CodeLooper.app found at $APP_PATH" | |
| ls -la "$APP_PATH/Contents/MacOS" | |
| if [ -f "$APP_PATH/Contents/MacOS/CodeLooper" ]; then | |
| echo "✅ CodeLooper executable found" | |
| file "$APP_PATH/Contents/MacOS/CodeLooper" | |
| else | |
| echo "::error::CodeLooper executable not found in $APP_PATH/Contents/MacOS/" | |
| exit 1 | |
| fi | |
| else | |
| echo "::error::CodeLooper.app not found at $APP_PATH" | |
| exit 1 | |
| fi | |
| - name: Create Build Artifacts | |
| id: create_artifacts | |
| if: steps.build_sign_notarize_step.outcome == 'success' | |
| env: | |
| APP_VERSION: ${{ steps.set-version.outputs.version }} | |
| run: | | |
| echo "Creating artifacts directory..." | |
| mkdir -p artifacts | |
| APP_BUNDLE_DIR="binary" | |
| APP_NAME="CodeLooper.app" | |
| ZIP_NAME="CodeLooper-macOS-${APP_VERSION}.zip" | |
| DMG_NAME="CodeLooper-macOS-${APP_VERSION}.dmg" | |
| echo "Creating ZIP artifact: artifacts/$ZIP_NAME" | |
| cd "$APP_BUNDLE_DIR" | |
| ditto -c -k --keepParent "$APP_NAME" "../artifacts/$ZIP_NAME" | |
| cd ../ | |
| echo "✅ ZIP created." | |
| echo "artifact_zip_path=artifacts/$ZIP_NAME" >> $GITHUB_OUTPUT | |
| if [ -f "artifacts/$DMG_NAME" ]; then | |
| echo "DMG found at artifacts/$DMG_NAME" | |
| echo "artifact_dmg_path=artifacts/$DMG_NAME" >> $GITHUB_OUTPUT | |
| echo "dmg_status=success" >> $GITHUB_OUTPUT | |
| else | |
| echo "::warning::DMG file artifacts/$DMG_NAME not found." | |
| echo "artifact_dmg_path=" >> $GITHUB_OUTPUT | |
| echo "dmg_status=missing" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Upload Build Artifacts | |
| if: steps.build_sign_notarize_step.outcome == 'success' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: CodeLooper-macOS-Build-${{ steps.set-version.outputs.version }} | |
| path: | | |
| ${{ steps.create_artifacts.outputs.artifact_zip_path }} | |
| ${{ steps.create_artifacts.outputs.artifact_dmg_path }} | |
| retention-days: 14 | |
| if-no-files-found: error | |
| - name: Upload Lint and Format Reports | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: CodeLooper-Lint-Format-Reports-${{ steps.set-version.outputs.version }} | |
| path: | | |
| lint-summary.md | |
| format-summary.md | |
| lint-results.txt | |
| format-results.txt | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| - name: Create PR Comment | |
| if: github.event_name == 'pull_request' && always() | |
| uses: marocchino/sticky-pull-request-comment@v2 | |
| with: | |
| header: macos-codelooper-build | |
| message: | | |
| **🍎 macOS CodeLooper App (${{ steps.set-version.outputs.version }})** | |
| Build & Sign: ${{ steps.build_sign_notarize_step.outcome == 'success' && '✅ Success' || '❌ Failed' }} | |
| Notarization: ${{ steps.build_sign_notarize_step.outputs.notarization_status || 'Unknown/Skipped' }} | |
| ${{ steps.build_sign_notarize_step.outcome == 'success' && '[Download Artifacts](https://github.com/' }}${{ github.repository }}${{ '/actions/runs/' }}${{ github.run_id }}${{ ')' || '' }} | |
| ${{ steps.build_sign_notarize_step.outcome != 'success' && '⚠️ **Build/Sign Failed** - Check logs.' || '' }} | |
| ${{ steps.build_sign_notarize_step.outcome == 'success' && (steps.build_sign_notarize_step.outputs.notarization_status == 'failed' || steps.build_sign_notarize_step.outputs.notarization_status == 'attempted_failure') && '⚠️ **Notarization Issue** - Check logs.' || '' }} | |
| - name: Create Release | |
| id: create_release | |
| if: startsWith(github.ref, 'refs/tags/') && steps.build_sign_notarize_step.outcome == 'success' && (github.event_name != 'workflow_dispatch' || inputs.create_release == true) | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| name: CodeLooper macOS ${{ steps.set-version.outputs.version }} | |
| files: | | |
| ${{ steps.create_artifacts.outputs.artifact_zip_path }} | |
| ${{ steps.create_artifacts.outputs.artifact_dmg_path }} | |
| draft: false | |
| prerelease: false | |
| generate_release_notes: true |