Fastlane을 사용하여 CI에서 플러터 앱을 빌드하고 일부 코드를 재사용하는 방법

우리는 Flutter 앱에 fastlane를 사용하고 ios 및 Android 빌드에 대해 다음을 위한 설정 및 재사용 코드를 정의하는 공통 언어를 제공합니다.
  • 변경 로그 스타일을 기반으로 변경 로그 생성
  • 코드 스타일, 서식 및 린트의 유효성 검사
  • 단위 테스트
  • 프로젝트 자동 생성 코드가 제대로 빌드되었는지 확인합니다.
  • 프로젝트가 각 플랫폼에 대해 제대로 빌드되는지 확인합니다.
  • 테스트를 위해 Firebase 앱 배포에 배포
  • 각 앱스토어에 배포

  • 그것을 할 수 있는 몇 가지 트릭이 있습니다:
  • 재사용 가능한 논리가 있고 각 플랫폼fastfileios/fastfile에 대해 하나씩 있는 부모android/fastfile를 만듭니다.
  • 프로젝트의 루트에서 작업을 실행하는 쉬운 방법이 있습니다. 우리의 아이디어는 각 플랫폼sh_on_root에서 모든 flutter 명령을 실행하는 레인fastfile을 만드는 것이었습니다.
  • 각 플랫폼에 대해 Gemfile를 만들고 실행할 정의된 환경을 갖도록 .ruby-version 파일을 정의하면 CI 서버에서 fastlane을 실행하기 위해 특정 루비 버전이 필요한 문제를 피할 수 있습니다.

  • 우리가 알아차린 또 다른 사실은 flutter 명령에서 --no-pub --suppress-analytics 매개변수를 사용하면 좋은 시작을 시도하려는 경우 CI에서 시간을 절약할 수 있다는 것입니다.

    몇 가지 유용한 레인과 이에 대한 몇 가지 워크플로의 예를 보여 드리겠습니다. 필요에 대해 생각하고 각 단계에 필요한 레인을 호출해야 하지만 상위fastfile 파일이 있는 일부 작업은 각 플랫폼에 대해 재사용해야 합니다.

    설정 보여줘


  • 각 플랫폼에 대해 Gemfile 생성
  • android/Gemfile
  • ios/Gemfile

  • 내용물:

    source "https://rubygems.org"
    
    gem 'cocoapods'
    gem "fastlane"
    
    plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
    eval_gemfile(plugins_path) if File.exist?(plugins_path)
    


  • CI(이 경우 Bitrise)가 사용해야 하는 루비 버전에 대한 단서를 갖도록 .ruby-version 생성
  • ios/.ruby-version
  • android/.ruby-version

  • 내용물:

    2.6.5
    


  • 각 플랫폼에서 재사용할 1개, 각 플랫폼에 대해 1개씩 3개의 fastlane 파일을 만듭니다(각 플랫폼 fastlane에서 부모 파일을 import "../../scripts/Fastfile"로 가져오는지 확인하십시오)
  • fastfile :

  • opt_out_usage
    
    # Have an easy way to get the root of the project
    def root_path
      Dir.pwd.sub(/.*\Kfastlane/, '').sub(/.*\Kandroid/, '').sub(/.*\Kios/, '').sub(/.*\K\/\//, '')
    end
    
    # Have an easy way to run flutter tasks on the root of the project
    lane :sh_on_root do |options|
      command = options[:command]
      sh("cd #{root_path} && #{command}")
    end
    
    # Tasks to be reused on each platform flow
    lane :fetch_dependencies do
      sh_on_root(command: "flutter pub get --suppress-analytics")
    end
    
    # Tasks to be reused on each platform flow
    lane :build_autogenerated_code do
      sh_on_root(command: "flutter pub run intl_utils:generate && flutter pub run build_runner build --delete-conflicting-outputs")
    end
    
    # Tasks to be reused on each platform flow
    lane :lint do
      sh_on_root(command: "flutter format --suppress-analytics --set-exit-if-changed -n lib/main.dart lib/src/ test/")
    end
    
    # Tasks to be reused on each platform flow
    lane :test do |options|
      sh_on_root(command: "flutter test --no-pub --coverage --suppress-analytics")
    end
    


  • ios/fastfile :

  • import "../../scripts/Fastfile"
    
    default_platform(:ios)
    
    platform :ios do
      # Updates XCode project settings to use a different code signing based on method
      private_lane :archive do |options|
        method = options[:method]
    
        profile_name = method == 'ad-hoc' ? "Distribution - Staging (adhoc)" : "Production"
        update_code_signing_settings(profile_name: profile_name)
    
        build_app(export_method: method)
      end
    
      private_lane :authenticate_apple_store do
        p8_path = path_for_secret("your .p8 file path")
    
        app_store_connect_api_key(
          key_id: "your key id",
          issuer_id: "your used id",
          key_filepath: p8_path,
          duration: 1200, # optional
          in_house: false,
        )
      end
    
      lane :build do |options|
        # Reuse parent fastfile tasks
        fetch_dependencies
        build_autogenerated_code
    
    
        sign_enabled = options[:sign_enabled] || false
        sign_param = sign_enabled ? '' : '--no-codesign'
    
        config_only = options[:config_only] || false
        config_param = config_only ? '--config-only' : ''
    
        sh_on_root(command: "flutter build ios --no-pub --suppress-analytics --release #{sign_param} #{config_param}")
      end
    
      lane :deploy_staging do
        build(sign_enabled: true)
        archive(method: "ad-hoc")
    
        upload_symbols_to_crashlytics(dsym_path: "your.app.dSYM.zip")
    
        firebase_app_distribution(
          app: "yours app id from firebase",
          ipa_path: "your.ipa",
          groups: "developers, staging",
          release_notes: changelog
        )
      end
    
      lane :deploy_staging_testflight do
        build(sign_enabled: true)
        archive(method: "app-store")
    
        authenticate_apple_store
    
        # Reuse parent fastfile tasks
        test
    
        upload_to_testflight(
          ipa: "your.ipa",
          reject_build_waiting_for_review: true,
          skip_waiting_for_build_processing: false,
          distribute_external: true,
          notify_external_testers: true,
          groups: "Your testers"
        )
      end
    
      lane :deploy_production do
        sh_on_root(command: "sh scripts/cp_env.sh")
    
        # All certificates and .p8 file should be fine on runnning machine
        build(sign_enabled: true, config_only: true)
        archive(method: "app-store")
        authenticate_apple_store
    
        # Reuse parent fastfile tasks
        test
    
        deliver(
          ipa: "your.ipa",
          skip_metadata: true,
          skip_screenshots: true,
          submit_for_review: false,
          force: false,
          automatic_release: false,
          submission_information: {
            add_id_info_serves_ads: false,
            add_id_info_uses_idfa: false,
            export_compliance_uses_encryption: false,
          },
          precheck_include_in_app_purchases: false,
        )
      end
    end
    


  • android/fastfile

  • import "../../scripts/Fastfile"
    
    default_platform(:android)
    
    platform :android do
      private_lane :build_apk do
        # Reuse parent fastfile tasks
        fetch_dependencies
        build_autogenerated_code
    
        sh_on_root(command: "flutter build apk --release")
      end
    
      lane :build do
        # Reuse parent fastfile tasks
        fetch_dependencies
        build_autogenerated_code
    
        sh_on_root(command: "flutter build appbundle --no-pub --release --suppress-analytics")
      end
    
      lane :deploy_staging do
        build_apk
    
        # Reuse parent fastfile tasks
        test
    
        firebase_app_distribution(
          app: "your app id",
          groups: "your testers",
          android_artifact_type: "APK",
          android_artifact_path: "#{root_path}/build/app/outputs/flutter-apk/app-release.apk",
          firebase_cli_path: "/usr/local/bin/firebase"
        )
      end
    
      lane :deploy_production do
        build
    
        # Reuse parent fastfile tasks
        test
    
        supply(
          track: 'beta',
          aab: "../build/app/outputs/bundle/release/app-release.aab",
          json_key: path_for_secret("your play store.json"),
          skip_upload_apk: true, # Upload the aab instead of apk
          skip_upload_metadata: true,
          skip_upload_changelogs: true,
          skip_upload_images: true,
          skip_upload_screenshots: true
        )
      end
    end
    

    좋은 웹페이지 즐겨찾기