diff --git a/.gitea/actions/macos-notarize/action.yml b/.gitea/actions/macos-notarize/action.yml index 7c3a47be..062da8cf 100644 --- a/.gitea/actions/macos-notarize/action.yml +++ b/.gitea/actions/macos-notarize/action.yml @@ -692,6 +692,113 @@ runs: if [ -f "$DMG_FILE" ]; then echo "✅ Created DMG package: $DMG_FILE" + + # Sign the DMG with the same certificate used for the app + echo "Signing DMG file..." + + # Decide which keychain to use + if [ "${{ steps.setup-cert.outputs.use_system_cert }}" = "true" ]; then + echo "Using system keychain identity" + # Get certificate hash instead of name to avoid ambiguity + IDENTITY_HASH=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -1 | awk '{print $2}') + echo "Using certificate hash: $IDENTITY_HASH" + else + # Make sure keychain is unlocked + security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + echo "Using custom keychain identity" + # Get certificate hash instead of name to avoid ambiguity + IDENTITY_HASH=$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" | grep "Developer ID Application" | head -1 | awk '{print $2}') + echo "Using certificate hash: $IDENTITY_HASH" + fi + + if [ -n "$IDENTITY_HASH" ]; then + # Sign the DMG + codesign --force --sign "$IDENTITY_HASH" --options runtime --timestamp "$DMG_FILE" + + # Verify DMG signature + echo "Verifying DMG signature..." + codesign -vvv "$DMG_FILE" + + # Only notarize DMG if the app was successfully notarized + if [ "${{ steps.notarize.outputs.notarized }}" = "true" ]; then + echo "Notarizing DMG file..." + + # Check if we're using API key notarization method + if [ "${{ inputs.notarization-method }}" = "api-key" ] && [ -n "$API_KEY_ID" ] && [ -n "$API_ISSUER_ID" ] && [ -n "$API_KEY_PATH" ]; then + # Use the same API key setup from the notarize step + echo "Using App Store Connect API key for DMG notarization..." + + echo "Submitting DMG for notarization..." + DMG_SUBMIT_OUTPUT=$(xcrun notarytool submit "$DMG_FILE" \ + --key ~/private_keys/AuthKey_${API_KEY_ID}.p8 \ + --key-id "$API_KEY_ID" \ + --issuer "$API_ISSUER_ID" --wait 2>&1) + + echo "DMG notarization submission output:" + echo "$DMG_SUBMIT_OUTPUT" + + # Extract DMG submission ID + DMG_SUBMISSION_ID=$(echo "$DMG_SUBMIT_OUTPUT" | grep -o "id: [a-f0-9\-]*" | head -1 | cut -d ' ' -f 2) + + if [ -n "$DMG_SUBMISSION_ID" ] && echo "$DMG_SUBMIT_OUTPUT" | grep -q "status: Accepted"; then + echo "✅ DMG notarization completed successfully!" + + # Staple the DMG + echo "Stapling notarization ticket to DMG..." + xcrun stapler staple "$DMG_FILE" + + # Verify DMG stapling + echo "Verifying DMG stapling..." + xcrun stapler validate "$DMG_FILE" + + echo "DMG is now fully signed, notarized, and stapled!" + else + echo "⚠️ DMG notarization may have failed or is still in progress." + echo "The app itself is still properly notarized, but the DMG may need manual verification." + fi + elif [ "${{ inputs.notarization-method }}" = "app-password" ] && [ -n "$APPLE_ID" ] && [ -n "$APP_PASSWORD" ] && [ -n "$APPLE_TEAM_ID" ]; then + # Use App-specific password for DMG notarization + echo "Using App-specific password for DMG notarization..." + + echo "Submitting DMG for notarization..." + DMG_SUBMIT_OUTPUT=$(xcrun notarytool submit "$DMG_FILE" \ + --apple-id "$APPLE_ID" \ + --password "$APP_PASSWORD" \ + --team-id "$APPLE_TEAM_ID" --wait 2>&1) + + echo "DMG notarization submission output:" + echo "$DMG_SUBMIT_OUTPUT" + + # Extract DMG submission ID + DMG_SUBMISSION_ID=$(echo "$DMG_SUBMIT_OUTPUT" | grep -o "id: [a-f0-9\-]*" | head -1 | cut -d ' ' -f 2) + + if [ -n "$DMG_SUBMISSION_ID" ] && echo "$DMG_SUBMIT_OUTPUT" | grep -q "status: Accepted"; then + echo "✅ DMG notarization completed successfully!" + + # Staple the DMG + echo "Stapling notarization ticket to DMG..." + xcrun stapler staple "$DMG_FILE" + + # Verify DMG stapling + echo "Verifying DMG stapling..." + xcrun stapler validate "$DMG_FILE" + + echo "DMG is now fully signed, notarized, and stapled!" + else + echo "⚠️ DMG notarization may have failed or is still in progress." + echo "The app itself is still properly notarized, but the DMG may need manual verification." + fi + else + echo "⚠️ DMG not notarized due to missing credentials." + echo "The app itself is properly notarized, but the DMG is only signed." + fi + else + echo "App was not notarized, skipping DMG notarization." + fi + else + echo "⚠️ No valid identity found for DMG signing. DMG will be created but not signed." + fi + # Use DMG as the primary package if available echo "::set-output name=package-path::$DMG_FILE" echo "::set-output name=zip-package-path::$ZIP_FILE" diff --git a/.gitea/workflows/test-macos-build.yml b/.gitea/workflows/test-macos-build.yml index d1e86531..826c285d 100644 --- a/.gitea/workflows/test-macos-build.yml +++ b/.gitea/workflows/test-macos-build.yml @@ -314,26 +314,6 @@ jobs: echo "STAPLED_APP_PATH=$APP_PATH" >> "$GITHUB_ENV" shell: bash - # Create a properly archived ZIP of the stapled app (preserves stapling) - - name: Create Stapled App Archive (ZIP) - if: steps.sign-and-notarize.outputs.notarized == 'true' && steps.sign-and-notarize.outputs.signed != 'none' - run: | - STAPLED_APP_PATH="${{ env.STAPLED_APP_PATH }}" - APP_NAME=$(basename "$STAPLED_APP_PATH" .app) - ARCHIVE_DIR="./ArchivedApps" - mkdir -p "$ARCHIVE_DIR" - - # Create the ZIP archive (preserving all metadata) - echo "Creating ZIP archive of stapled app..." - cd "$(dirname "$STAPLED_APP_PATH")" - # Use ditto to preserve all metadata and permissions - ditto -c -k --keepParent "$(basename "$STAPLED_APP_PATH")" "$GITHUB_WORKSPACE/$ARCHIVE_DIR/$APP_NAME.zip" - cd "$GITHUB_WORKSPACE" - - echo "ZIP archive created at: $ARCHIVE_DIR/$APP_NAME.zip" - echo "STAPLED_APP_ZIP=$ARCHIVE_DIR/$APP_NAME.zip" >> "$GITHUB_ENV" - shell: bash - # Create a DMG file (macOS disk image) for easy distribution - name: Create DMG for Distribution if: steps.sign-and-notarize.outputs.notarized == 'true' && steps.sign-and-notarize.outputs.signed != 'none' @@ -343,77 +323,17 @@ jobs: ARCHIVE_DIR="./ArchivedApps" DMG_FILE="$ARCHIVE_DIR/$APP_NAME.dmg" - # Create a DMG file - echo "Creating DMG file..." - hdiutil create -volname "$APP_NAME" -srcfolder "$STAPLED_APP_PATH" -ov -format UDZO "$DMG_FILE" + # Note: The actual DMG creation, signing, notarization and stapling + # are now handled by the macos-notarize action - # Sign the DMG with the same certificate - echo "Signing DMG file..." - # Extract certificate info from the previously signed app - CERT_IDENTITY=$(codesign -dvv "$STAPLED_APP_PATH" 2>&1 | grep "Authority" | head -1 | sed -e 's/.*Authority=//g') - echo "Using certificate identity: $CERT_IDENTITY" - - # Sign the DMG - codesign --sign "$CERT_IDENTITY" --options runtime --timestamp "$DMG_FILE" - - # Verify DMG signature - echo "Verifying DMG signature..." - codesign -vvv "$DMG_FILE" - - # Notarize the DMG - echo "Notarizing DMG file..." - # Select which authentication method to use for notarization - if [ -n "${{ secrets.NOTARY_API_KEY_ID }}" ] && [ -n "${{ secrets.NOTARY_API_KEY_ISSUER_ID }}" ]; then - # Use API Key authentication (preferred) - echo "Using Notary API Key authentication..." - UUID=$(xcrun notarytool submit "$DMG_FILE" \ - --key "${{ secrets.NOTARY_API_KEY_PATH }}" \ - --key-id "${{ secrets.NOTARY_API_KEY_ID }}" \ - --issuer "${{ secrets.NOTARY_API_KEY_ISSUER_ID }}" \ - --wait | grep "id:" | awk '{print $2}') - elif [ -n "${{ secrets.APPLE_ID }}" ] && [ -n "${{ secrets.APPLE_TEAM_ID }}" ]; then - # Use Apple ID authentication - echo "Using Apple ID authentication..." - UUID=$(xcrun notarytool submit "$DMG_FILE" \ - --apple-id "${{ secrets.APPLE_ID }}" \ - --password "${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}" \ - --team-id "${{ secrets.APPLE_TEAM_ID }}" \ - --wait | grep "id:" | awk '{print $2}') + # Just ensure we have the correct path for outputs + if [ -f "${{ steps.sign-and-notarize.outputs.package-path }}" ]; then + echo "Using DMG created by the macos-notarize action" + echo "DMG file location: ${{ steps.sign-and-notarize.outputs.package-path }}" + echo "STAPLED_APP_DMG=${{ steps.sign-and-notarize.outputs.package-path }}" >> "$GITHUB_ENV" else - echo "⚠️ No notarization credentials available. DMG will not be notarized." - UUID="" + echo "⚠️ DMG not found from the macos-notarize action output" fi - - echo "Notarization UUID: $UUID" - - # Check notarization status - if [ -n "$UUID" ]; then - # Use the same authentication method for UUID info - if [ -n "${{ secrets.NOTARY_API_KEY_ID }}" ] && [ -n "${{ secrets.NOTARY_API_KEY_ISSUER_ID }}" ]; then - xcrun notarytool info "$UUID" \ - --key "${{ secrets.NOTARY_API_KEY_PATH }}" \ - --key-id "${{ secrets.NOTARY_API_KEY_ID }}" \ - --issuer "${{ secrets.NOTARY_API_KEY_ISSUER_ID }}" - elif [ -n "${{ secrets.APPLE_ID }}" ] && [ -n "${{ secrets.APPLE_TEAM_ID }}" ]; then - xcrun notarytool info "$UUID" \ - --apple-id "${{ secrets.APPLE_ID }}" \ - --password "${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}" \ - --team-id "${{ secrets.APPLE_TEAM_ID }}" - fi - - # Staple the DMG - echo "Stapling notarization ticket to DMG..." - xcrun stapler staple "$DMG_FILE" - - # Verify stapling - echo "Verifying DMG stapling..." - xcrun stapler validate "$DMG_FILE" - else - echo "⚠️ Notarization UUID not found. DMG may not be properly notarized." - fi - - echo "DMG file created at: $DMG_FILE" - echo "STAPLED_APP_DMG=$DMG_FILE" >> "$GITHUB_ENV" shell: bash # Upload stapled app directly (this is the most reliable approach) @@ -425,22 +345,32 @@ jobs: path: ${{ env.STAPLED_APP_PATH }} retention-days: 30 - # Upload the ZIP archive (proper archiving that preserves stapling) + # Create a properly archived ZIP of the stapled app (preserves stapling) + - name: Create Stapled App Archive (ZIP) + if: steps.sign-and-notarize.outputs.notarized == 'true' && steps.sign-and-notarize.outputs.signed != 'none' && steps.sign-and-notarize.outputs.zip-package-path != '' + run: | + # The ZIP is now created by the macos-notarize action + echo "Using ZIP created by the macos-notarize action" + echo "ZIP file location: ${{ steps.sign-and-notarize.outputs.zip-package-path }}" + echo "STAPLED_APP_ZIP=${{ steps.sign-and-notarize.outputs.zip-package-path }}" >> "$GITHUB_ENV" + shell: bash + + # Upload the ZIP archive from the macos-notarize action - name: Upload Stapled App ZIP Archive uses: actions/upload-artifact@v3 - if: steps.sign-and-notarize.outputs.notarized == 'true' && steps.sign-and-notarize.outputs.signed != 'none' + if: steps.sign-and-notarize.outputs.notarized == 'true' && steps.sign-and-notarize.outputs.signed != 'none' && steps.sign-and-notarize.outputs.zip-package-path != '' with: name: LuckyWorld-macOS-Stapled-ZIP - path: ${{ env.STAPLED_APP_ZIP }} + path: ${{ steps.sign-and-notarize.outputs.zip-package-path }} retention-days: 30 - # Upload the DMG file + # Upload the DMG file from the macos-notarize action - name: Upload Stapled App DMG uses: actions/upload-artifact@v3 if: steps.sign-and-notarize.outputs.notarized == 'true' && steps.sign-and-notarize.outputs.signed != 'none' with: name: LuckyWorld-macOS-Stapled-DMG - path: ${{ env.STAPLED_APP_DMG }} + path: ${{ steps.sign-and-notarize.outputs.package-path }} retention-days: 30 # Upload signed app (might be DMG or other package format)