You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter iOS通过Fastlane+GitHub Action打包归档Run Script阶段失败

Flutter iOS通过Fastlane+GitHub Action部署TestFlight时Run Script阶段失败的解决方法

问题场景

使用Fastlane结合GitHub Action将Flutter iOS应用部署至TestFlight,所有密钥与配置信息均匹配,但构建始终在Run Script阶段失败,报错信息如下:

PhaseScriptExecution Run\ Script /Users/runner/Library/Developer/Xcode/DerivedData/Runner-avobjsyvghznfxgtyjuolmxrrfls/Build/Intermediates.noindex/ArchiveIntermediates/prod/IntermediateBuildFilesPath/Runner.build/Release-prod-iphoneos/Runner.build/Script-9740EEB61CF901F6004384FC.sh (in target 'Runner' from project 'Runner')

引发问题的是Xcode Run Script阶段的脚本:

/bin/sh "${FLUTTER_ROOT}/packages/flutter_tools/bin/xcode_backend.sh" build

本地配置相同环境变量后Fastfile可正常运行,但GitHub Action环境下执行失败,已尝试给FLUTTER_ROOT添加{}包裹、配置Project Runner Info,问题仍未解决。

相关配置代码

GitHub Workflow yaml

name: Flutter CI

on:
  push:
    branches: xxx

jobs:
  deploy:
    name: Deploying to Testflight
    runs-on: macOS-latest
    
    steps:

      - name: Setup XCode on Machine    
        uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: '13.4'

      - name: Setup Flutter
        uses: subosito/flutter-action@v2.8.0
        with:
          channel: 'stable'
          flutter-version: '3.3.9'

      - name: Give permission to private repo
        uses: shaunco/ssh-agent@git-repo-mapping
        with:
          ssh-private-key: |
            ${{ secrets.SECRET_REPO_DEPLOY_KEY }}
          repo-mappings: | 
            github.com/anfin21/package_library

      - name: Checkout repository
        uses: actions/checkout@v1
    
      - name: Install packages
        run: flutter pub get 

      - name: Deploy iOS Beta to TestFlight via Fastlane
        uses: maierj/fastlane-action@v1.4.0
        with:
          lane: closed_beta
          subdirectory: ios
        env:
          APP_STORE_CONNECT_TEAM_ID: '${{ secrets.ITC_TEAM_ID }}'
          DEVELOPER_APP_ID: '${{ secrets.APPLICATON_ID }}'
          DEVELOPER_APP_IDENTIFIER: '${{ secrets.BUNDLE_IDENTIFIER }}'
          DEVELOPER_PORTAL_TEAM_ID: '${{ secrets.DEVELOPER_PORTAL_TEAM_ID }}'
          FASTLANE_APPLE_ID: '${{ secrets.FASTLANE_APPLE_EMAIL_ID }}'
          FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: '${{ secrets.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}'
          MATCH_PASSWORD: '${{ secrets.MATCH_PASSWORD }}'
          GIT_AUTHORIZATION: '${{ secrets.GIT_AUTHORIZATION }}'
          PROVISIONING_PROFILE_SPECIFIER: '${{ secrets.PROVISIONING_PROFILE_SPECIFIER }}'
          TEMP_KEYCHAIN_PASSWORD: '${{ secrets.TEMP_KEYCHAIN_PASSWORD }}'
          TEMP_KEYCHAIN_USER: '${{ secrets.TEMP_KEYCHAIN_USER }}'
          APPLE_KEY_ID: '${{ secrets.APPLE_KEY_ID }}'
          APPLE_ISSUER_ID: '${{ secrets.APPLE_ISSUER_ID }}'
          APPLE_KEY_CONTENT: '${{ secrets.APPLE_KEY_CONTENT }}'

iOS Fastfile代码

default_platform(:ios)
DEVELOPER_APP_ID = ENV["DEVELOPER_APP_ID"]
DEVELOPER_APP_IDENTIFIER = ENV["DEVELOPER_APP_IDENTIFIER"]
PROVISIONING_PROFILE_SPECIFIER = ENV["PROVISIONING_PROFILE_SPECIFIER"]
TEMP_KEYCHAIN_USER = ENV["TEMP_KEYCHAIN_USER"]
TEMP_KEYCHAIN_PASSWORD = ENV["TEMP_KEYCHAIN_PASSWORD"]
APPLE_ISSUER_ID = ENV["APPLE_ISSUER_ID"]
APPLE_KEY_ID = ENV["APPLE_KEY_ID"]
APPLE_KEY_CONTENT = ENV["APPLE_KEY_CONTENT"]
GIT_AUTHORIZATION = ENV["GIT_AUTHORIZATION"]

def delete_temp_keychain(name)
  delete_keychain(
    name: name
  ) if File.exist? File.expand_path("~/Library/Keychains/#{name}-db")
end

def create_temp_keychain(name, password)
  create_keychain(
    name: name,
    password: password,
    unlock: false,
    timeout: 0
  )
end

def ensure_temp_keychain(name, password)
  delete_temp_keychain(name)
  create_temp_keychain(name, password)
end

platform :ios do

  lane :closed_beta do
    keychain_name = TEMP_KEYCHAIN_USER
    keychain_password = TEMP_KEYCHAIN_PASSWORD
    ensure_temp_keychain(keychain_name, keychain_password)

    
    api_key = app_store_connect_api_key(
      key_id: APPLE_KEY_ID,
      issuer_id: APPLE_ISSUER_ID,
      key_content: APPLE_KEY_CONTENT,            
      duration: 1200,            
      in_house: false
    )

    cocoapods(
      clean_install: true,
    )

    match(
      type: 'appstore',
      app_identifier: "#{DEVELOPER_APP_IDENTIFIER}",
      git_basic_authorization: Base64.strict_encode64(GIT_AUTHORIZATION),
      readonly: false,
      keychain_name: keychain_name,
      keychain_password: keychain_password,
      api_key: api_key
    )

    gym(
      configuration: "Release",
      sdk: "iphoneos16.1",
      workspace: "Runner.xcworkspace",
      scheme: "prod",
      export_method: "app-store",
      export_options: {
        provisioningProfiles: { 
            DEVELOPER_APP_ID => PROVISIONING_PROFILE_SPECIFIER
        }
      }
    )

    pilot(
      apple_id: "#{DEVELOPER_APP_ID}",
      app_identifier: "#{DEVELOPER_APP_IDENTIFIER}",
      skip_waiting_for_build_processing: true,
      skip_submission: true,
      distribute_external: false,
      notify_external_testers: false,
      ipa: "./Runner.ipa"
    )

    delete_temp_keychain(keychain_name)
  end
end

解决步骤

1. 修复FLUTTER_ROOT环境变量传递问题

GitHub Action中通过subosito/flutter-action安装Flutter后,FLUTTER_ROOT会被自动设置,但Fastlane执行gym时可能未继承该变量。可以在Workflow的Fastlane步骤中显式传递:
在Deploy iOS Beta to TestFlight via Fastlane的env中添加:

FLUTTER_ROOT: ${{ env.FLUTTER_ROOT }}

或者在Fastfile的closed_beta lane开头手动设置:

ENV['FLUTTER_ROOT'] = `which flutter`.chomp.split('/bin/flutter')[0]

2. 匹配Xcode与SDK版本

当前使用Xcode 13.4,其最高支持的iOS SDK版本为iphoneos15.5,但Fastfile中gym指定的sdk: "iphoneos16.1"属于Xcode 14.1+的SDK,这会导致构建时找不到对应SDK。修改gym的sdk配置:

sdk: "iphoneos15.5"

或升级Xcode版本至14.1+,同时更新Workflow中的Xcode版本配置。

3. 修正Fastfile中export_options的语法错误

Fastfile中gym的export_options里,provisioningProfiles的键应使用Bundle ID(DEVELOPER_APP_IDENTIFIER)而非App Store应用ID(DEVELOPER_APP_ID),同时修正哈希语法:

export_options: {
  provisioningProfiles: { 
    DEVELOPER_APP_IDENTIFIER => PROVISIONING_PROFILE_SPECIFIER
  }
}

4. 验证Flutter环境初始化

在flutter pub get步骤前添加环境检查,确认Flutter与Xcode配置正常:

- name: Check Flutter environment
  run: flutter doctor -v

5. 确保脚本执行权限

GitHub Action Runner环境中可能存在脚本权限不足的问题,添加步骤给xcode_backend.sh赋予执行权限:

- name: Grant execute permission to xcode_backend.sh
  run: chmod +x "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh"

内容的提问来源于stack exchange,提问作者Lam Kiet Vuong

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 08:25:58