1

I'm trying to set up a CI/CD pipeline for a flutter desktop app on Github Actions. While the build for windows works fine, the macos job keeps on hanging (I set a timeout at 30 mins). By setting verbose on flutter build and modifying the autogenerated scripts to dump logs in a file I was able to determine the step it's stuck on is the codesign (I manage certificates with fastlane match), but I can't find any errors in the log, and running the same command locally works fine. The job is defined like this:

build_macos:
 if: ${{ github.actor != 'github-actions' }}
 needs: mac_check
 runs-on: macos-latest
 env:
 ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
 ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
 ASC_KEY_P8: ${{ secrets.ASC_KEY_P8 }}
 MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
 MATCH_STORAGE_MODE: "google_cloud"
 GCLOUD_KEY_JSON: ${{ secrets.GCLOUD_KEY_JSON }}
 APP_IDENTIFIER: "<APP_IDENTIFIER>"
 TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
 steps:
 - name: Skip if disabled
 if: needs.mac_check.outputs.macos_build != 'true'
 run: echo "Skipping MacOS Build"
 - uses: actions/checkout@v3
 if: needs.mac_check.outputs.macos_build == 'true'
 - name: Setup Flutter
 if: needs.mac_check.outputs.macos_build == 'true'
 uses: subosito/flutter-action@v2
 with:
 flutter-version-file: pubspec.yaml
 - name: Enable macOS Desktop
 if: needs.mac_check.outputs.macos_build == 'true'
 run: flutter config --enable-macos-desktop
 - name: Check Flutter installation and path
 run: |
 which flutter
 flutter doctor -v
 - name: Print macos_assemble.sh content 
 run: |
 cat /Users/runner/hostedtoolcache/flutter/stable-3.32.5-arm64/packages/flutter_tools/bin/macos_assemble.sh
 - name: Clean ephemeral and cache 
 if: needs.mac_check.outputs.macos_build == 'true'
 run: |
 flutter clean 
 rm -rf macos/Flutter/ephemeral
 - name: Install dependencies
 if: needs.mac_check.outputs.macos_build == 'true'
 run: flutter pub get
 - name: Install fastlane
 if: needs.mac_check.outputs.macos_build == 'true'
 run: |
 gem install fastlane
 which fastlane
 - name: Save GCS credentials
 if: needs.mac_check.outputs.macos_build == 'true'
 run: |
 mkdir -p .credentials
 echo "${GCLOUD_KEY_JSON}" | base64 --decode > .credentials/credentials.json
 echo "GOOGLE_APPLICATION_CREDENTIALS=$PWD/.credentials/credentials.json" >> $GITHUB_ENV
 - name: Run Fastlane macos_signing lane
 if: needs.mac_check.outputs.macos_build == 'true'
 working-directory: macos
 run: fastlane macos_signing --verbose
 - name: List code signing identities
 run: |
 security find-identity -v -p codesigning
 
 - name: Build Flutter macOS release
 if: needs.mac_check.outputs.macos_build == 'true'
 timeout-minutes: 30
 continue-on-error: true
 run: |
 flutter build macos --release --build-number=${{ github.run_number }} -v --split-debug-info=build/debug-info
 
 - name: Show assemble_debug.log
 if: always()
 run: |
 LOG_FILE="${{ github.workspace }}/macos/Flutter/ephemeral/assemble_debug.log"
 if [ -f "$LOG_FILE" ]; then
 echo "Contents of assemble_debug.log:"
 cat "$LOG_FILE"
 else
 echo "No assemble_debug.log file found."
 fi
 - name: Run Fastlane release lane
 if: needs.mac_check.outputs.macos_build == 'true'
 working-directory: macos
 run: fastlane upload_macos_build --verbose
 - name: Upload macOS installer for gh release
 if: github.ref == 'refs/heads/main' && needs.mac_check.outputs.macos_build == 'true'
 uses: actions/upload-artifact@v4
 with:
 name: macos-installer
 path: build/macos/Build/Products/Release/*.app

and this is the content of the fastfile (initially I was calling flutter build from there, I put it in a separate step of the build just to see which step was stuck):

default_platform(:macos)
platform :macos do
 before_all do
 load_asc_api_token
 end
 desc "Load the App Store Connect API token"
 lane :load_asc_api_token do
 app_store_connect_api_key(
 key_id: ENV["ASC_KEY_ID"],
 issuer_id: ENV["ASC_ISSUER_ID"],
 key_content: ENV["ASC_KEY_P8"],
 is_key_content_base64: true,
 in_house: false
 )
 end
 desc "Release a new macOS build to TestFlight"
 lane :release_beta do
 commit = last_git_commit
 puts "*** Starting macOS release for commit(#{commit[:abbreviated_commit_hash]}) ***"
 api_key = lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
 sync_code_signing(
 api_key: api_key,
 app_identifier: "<APP_IDENTIFIER>",
 type: "appstore",
 readonly: true
 )
 build_number = ENV["GITHUB_RUN_NUMBER"] || Time.now.strftime("%Y%m%d%H%M")
 puts "*** Build Flutter macOS app for build number #{build_number} ***"
 Dir.chdir("../..") do
 sh("flutter", "build", "macos", "--release", "--build-number=#{build_number}")
 end
 puts "*** Create .pkg for notarization ***"
 app_path = "../../build/macos/Build/Products/Release/ivaservizi.app"
 pkg_path = "ivaservizi.pkg"
 
 sh("productbuild", "--component", app_path, "/Applications", pkg_path)
 puts "*** Upload to TestFlight (App Store Connect) ***"
 upload_to_testflight(
 api_key: api_key,
 skip_waiting_for_build_processing: true,
 pkg: pkg_path
 )
 end
 lane :macos_signing do
 api_key = lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
 sync_code_signing(
 api_key: api_key,
 app_identifier: "<APP_IDENTIFIER>",
 type: "appstore",
 readonly: true
 )
 end
 desc "Upload a prebuilt macOS app to TestFlight"
 lane :upload_macos_build do
 app_path = "../../build/macos/Build/Products/Release/ivaservizi.app"
 pkg_path = "ivaservizi.pkg"
 unless File.exist?(app_path)
 UI.user_error!("Missing built app at #{app_path}. Run `flutter build macos` first.")
 end
 load_asc_api_token
 api_key = lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
 puts "*** Create .pkg for notarization ***"
 sh("productbuild", "--component", app_path, "/Applications", pkg_path)
 puts "*** Upload to TestFlight (App Store Connect) ***"
 upload_to_testflight(
 api_key: api_key,
 skip_waiting_for_build_processing: true,
 pkg: pkg_path
 )
 end
end

I checked the certificates used by the workflow with security find-identity -v -p codesigning and they seem correct. I'm honestly stumped, I can't see any issues from the workflow log that could at least point me in the right direction.

asked Jul 3, 2025 at 17:01
1
  • 1
    Usually this type of hang is caused by some UI showing on the runner that in invisible in logs. For example some Keychain access confirmations or iCloud account login confirmation. Commented Jul 4, 2025 at 8:13

1 Answer 1

0

The issue was with keychain access. I was able to solve it by creating a temporary one and unlocking it. I also had some trouble getting code signing and notarization to work, so here's the full job definition and fastfile for reference:

build_macos:
 if: ${{ github.actor != 'github-actions' }}
 needs: mac_check
 runs-on: macos-latest
 env:
 ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
 ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
 ASC_KEY_P8: ${{ secrets.ASC_KEY_P8 }}
 MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
 MATCH_STORAGE_MODE: "google_cloud"
 GCLOUD_KEY_JSON: ${{ secrets.GCLOUD_KEY_JSON }}
 APP_IDENTIFIER: "<APP IDENTIFIER>"
 TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
 steps:
 - name: Skip if disabled
 if: needs.mac_check.outputs.macos_build != 'true'
 run: echo "Skipping MacOS Build"
 - uses: actions/checkout@v3
 if: needs.mac_check.outputs.macos_build == 'true'
 - name: Setup Flutter
 if: needs.mac_check.outputs.macos_build == 'true'
 uses: subosito/flutter-action@v2
 with:
 flutter-version-file: pubspec.yaml
 - name: Enable macOS Desktop
 if: needs.mac_check.outputs.macos_build == 'true'
 run: flutter config --enable-macos-desktop
 - name: Check Flutter installation and path
 run: |
 which flutter
 flutter doctor -v
 - name: Create temporary keychain
 id: create_keychain
 if: needs.mac_check.outputs.macos_build == 'true'
 run: |
 KEYCHAIN_NAME=temp_build.keychain-db
 KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
 echo "Creating temporary keychain: $KEYCHAIN_NAME"
 security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
 security set-keychain-settings -lut 21600 "$KEYCHAIN_NAME"
 security default-keychain -s "$KEYCHAIN_NAME"
 security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
 security list-keychains -d user -s "$KEYCHAIN_NAME"
 echo "password=$KEYCHAIN_PASSWORD" >> $GITHUB_OUTPUT
 echo "FASTLANE_KEYCHAIN_PATH=$KEYCHAIN_NAME" >> $GITHUB_ENV
 echo "FASTLANE_KEYCHAIN_PASSWORD=$KEYCHAIN_PASSWORD" >> $GITHUB_ENV
 - name: Print macos_assemble.sh content 
 run: |
 cat /Users/runner/hostedtoolcache/flutter/stable-3.32.5-arm64/packages/flutter_tools/bin/macos_assemble.sh
 - name: Clean ephemeral and cache 
 if: needs.mac_check.outputs.macos_build == 'true'
 run: |
 flutter clean 
 rm -rf macos/Flutter/ephemeral
 - name: Install dependencies
 if: needs.mac_check.outputs.macos_build == 'true'
 run: flutter pub get
 - name: Install fastlane
 if: needs.mac_check.outputs.macos_build == 'true'
 run: |
 gem install fastlane
 which fastlane
 - name: Save GCS credentials
 if: needs.mac_check.outputs.macos_build == 'true'
 run: |
 mkdir -p .credentials
 echo "${GCLOUD_KEY_JSON}" | base64 --decode > .credentials/credentials.json
 echo "GOOGLE_APPLICATION_CREDENTIALS=$PWD/.credentials/credentials.json" >> $GITHUB_ENV
 - name: Run Fastlane macos_signing lane
 if: needs.mac_check.outputs.macos_build == 'true'
 working-directory: macos
 env:
 FASTLANE_KEYCHAIN_PASSWORD: ${{ needs.build_macos.outputs.keychain_password }}
 run: fastlane macos_signing --verbose
 - name: Setting up keychain for codesign and productbuild
 if: needs.mac_check.outputs.macos_build == 'true'
 run: |
 security set-key-partition-list -S apple-tool:,apple: -s -k "$FASTLANE_KEYCHAIN_PASSWORD" "$FASTLANE_KEYCHAIN_PATH"
 - name: Run Fastlane release lane
 if: needs.mac_check.outputs.macos_build == 'true'
 working-directory: macos
 env:
 FASTLANE_KEYCHAIN_PASSWORD: ${{ needs.build_macos.outputs.keychain_password }}
 run: fastlane release_beta --verbose
 - name: Upload macOS installer for gh release
 if: github.ref == 'refs/heads/main' && needs.mac_check.outputs.macos_build == 'true'
 uses: actions/upload-artifact@v4
 with:
 name: macos-installer
 path: build/macos/Build/Products/Release/*.app
 
 - name: Delete temporary keychain
 if: always() && needs.mac_check.outputs.macos_build == 'true'
 run: |
 KEYCHAIN_NAME=temp_build.keychain-db
 echo "Deleting keychain $KEYCHAIN_NAME"
 security delete-keychain "$KEYCHAIN_NAME" || echo "Keychain not found or already deleted"
default_platform(:mac)
platform :mac do
 before_all do
 load_asc_api_token
 end
 desc "Load the App Store Connect API token"
 lane :load_asc_api_token do
 app_store_connect_api_key(
 key_id: ENV["ASC_KEY_ID"],
 issuer_id: ENV["ASC_ISSUER_ID"],
 key_content: ENV["ASC_KEY_P8"],
 is_key_content_base64: true,
 in_house: false
 )
 end
 desc "Release a new macOS build to TestFlight"
 lane :release_beta do
 commit = last_git_commit
 puts "*** Starting macOS release for commit(#{commit[:abbreviated_commit_hash]}) ***"
 api_key = lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
 macos_signing
 build_number = ENV["GITHUB_RUN_NUMBER"] || Time.now.strftime("%Y%m%d%H%M")
 puts "*** Build Flutter macOS app for build number #{build_number} ***"
 Dir.chdir("../..") do
 sh("flutter", "build", "macos", "--release", "--build-number=#{build_number}")
 end
 increment_build_number({build_number:build_number})
 build_mac_app(
 configuration: "Release",
 skip_package_pkg: false,
 output_directory: ".build",
 # xcodebuild_formatter: "xcbeautify",
 silent: true,
 export_method: "app-store",
 export_options: {
 provisioningProfiles: {
 "<APP IDENTIFIER>" => "match AppStore <APP IDENTIFIER> macos"
 }
 }
 )
 puts "*** Upload to TestFlight (App Store Connect) ***"
 upload_to_testflight(
 api_key: api_key,
 skip_waiting_for_build_processing: true
 )
 end
 lane :macos_signing do
 api_key = lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
 keychain_name = ENV["FASTLANE_KEYCHAIN_PATH"] || "login.keychain-db"
 keychain_password = ENV["FASTLANE_KEYCHAIN_PASSWORD"] || ""
 sync_code_signing(
 api_key: api_key,
 app_identifier: "<APP IDENTIFIER>",
 type: "appstore",
 readonly: true,
 platform: "macos",
 keychain_name: keychain_name,
 keychain_password: keychain_password,
 additional_cert_types: ["mac_installer_distribution", "developer_id_installer"]
 )
 end
end
answered Jul 9, 2025 at 10:54
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.