ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Flutter Fastlane CI/CD(1) - iOS
    Flutter ν…Œν¬ 2023. 3. 7. 01:00

    1. Fastlane μ„€μΉ˜

    brew install fastlane
    or
    sudo gem install fastlane

    homebrew λ‚˜ RubyGem 을 톡해 Fastlane 을 μ„€μΉ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. κ³΅μ‹λ¬Έμ„œ 에 λ”°λ₯΄λ©΄, Gem 은 Ruby ν™˜κ²½μ— μ˜μ‘΄ν•˜κΈ° λ•Œλ¬Έμ— brew 둜 μ„€μΉ˜ν•˜λŠ” 것을 μΆ”μ²œν•œλ‹€κ³  ν•©λ‹ˆλ‹€

     

    2. Fastlane init

    cd ios/
    fastlane init

    fastlane init λͺ…λ Ήμ–΄λ₯Ό μž…λ ₯ν•˜λ©΄ Setup μ’…λ₯˜λ₯Ό λ¬Όμ–΄λ³΄λŠ” μ½˜μ†”μ°½μ΄ λ‚˜μ˜΅λ‹ˆλ‹€. Manual Setup을 ν•  것이기에 4λ²ˆμ„ μž…λ ₯ν•©λ‹ˆλ‹€

    3. Fastlane Match μ„€μ • (선택적)

    Fastlane Match λž€, Fastlane μ•ˆμ— μžˆλŠ” μ½”λ“œ 사이닝 λ°©λ²•μ˜ ν•œ μ’…λ₯˜μž…λ‹ˆλ‹€.

    κ°„λž΅νžˆ λ§ν•˜μžλ©΄, μΈμ¦μ„œμ™€ ν”„λ‘œνŒŒμΌμ„ ν•˜λ‚˜μ˜ 원격 μ €μž₯μ†Œμ— μ—…λ‘œλ“œ ν•˜κ³ , νŒ€μ›λ“€μ΄ ν•΄λ‹Ή μΈμ¦μ„œμ™€ ν”„λ‘œνŒŒμΌμ„ κ³΅μœ ν•˜μ—¬ μ½”λ“œ 사이닝을 λ‹¨μˆœν™” ν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€.

    Provisioning Profile μ—λŠ” μ•± ID, μ•± 싀행이 ν—ˆμš©λœ Device ID, μ• ν”Œμ—μ„œ λ°œκΈ‰ν•œ μΈμ¦μ„œκ°€ ν¬ν•¨λ˜μ–΄ 있으며, 각 개발자의 ν™˜κ²½μ΄ λ‹€λ₯΄κΈ° λ•Œλ¬Έμ— μ„œλ‘œ λ‹€λ₯Έ Profile 이 μƒμ„±λ©λ‹ˆλ‹€. λ”°λΌμ„œ Match λ₯Ό 톡해 μΈμ¦μ„œμ™€ ν”„λ‘œνŒŒμΌμ„ κ³΅μœ ν•˜λ©΄, μ½”λ“œ 사이닝 관리 μΈ‘λ©΄μ—μ„œ μœ μš©ν•˜κ²Œ μ‚¬μš©λ  수 μžˆμŠ΅λ‹ˆλ‹€.

    μΈμ¦μ„œμ™€ ν”„λ‘œνŒŒμΌμ„ μ €μž₯ ν•  원격 μ €μž₯μ†Œλ₯Ό μƒμ„±ν•΄μ€λ‹ˆλ‹€

     

    3-2. Fastlane Match Init

     

    match init λͺ…λ Ήμ–΄λ₯Ό μž…λ ₯ν•˜λ©΄ Storage mode 둜 git 을 μ„ νƒν•˜κ³ , git μ£Όμ†Œλ₯Ό μž…λ ₯ν•©λ‹ˆλ‹€. 섀정이 λ‹€ λλ‚˜λ©΄ Fastlane 폴더에 MatchFile이 μƒμ„±λ©λ‹ˆλ‹€.

    3-3. MatchFile μˆ˜μ •

    MatchFile μ—μ„œ Default Type 을 appstore 둜 λ³€κ²½ν•΄μ€μ‹œλ‹€.

     

    3-4. 기쑴의 개발, 배포 μΈμ¦μ„œ 파기

    fastlane match nuke development
    fastlane match nuke distribution

    μΈμ¦μ„œ 파기 λͺ…λ Ήμ–΄λ₯Ό μž…λ ₯ν•˜λ©΄, passphase λ₯Ό λ“±λ‘ν•˜λŠ” 화면이 λ‚˜μ˜¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

    3-5. μƒˆλ‘œμš΄ 개발, 배포 μΈμ¦μ„œ 생성

    fastlane match development 
    fastlane match appstore

    νŒ€μ›λ“€μ΄ 곡유 ν•  μΈμ¦μ„œλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. 두 λͺ…λ Ήμ–΄λ₯Ό λͺ¨λ‘ μž…λ ₯ν•˜λ©΄, Matchfile 에 λ“±λ‘λœ 원격 μ €μž₯μ†Œμ— μΈμ¦μ„œμ™€ ν”„λ‘œνŒŒμΌμ΄ μ˜¬λΌκ°€κ²Œ λ©λ‹ˆλ‹€.

     

    3-6. Xcode Code Signing 확인

    TARGETS/Signing&Capabilities/All/Signing/Provisioning Profile

    Xcode에 λ“€μ–΄κ°€ Provisioning Profile 을 match [...] packageName 으둜 λ³€κ²½ν•΄μ€λ‹ˆλ‹€.

    4. Fastlane Firebase Distribution μ„€μ •

    Fastlane 을 μ΄μš©ν•˜μ—¬ Firebase App Distribution 에 λ°°ν¬ν•˜λ €λ©΄ ν”ŒλŸ¬κ·ΈμΈ μ„€μΉ˜, 인증 등을 ν•΄μ•Ό ν•©λ‹ˆλ‹€.

     

    4-1. Firebase Plugin μ„€μΉ˜

     
    fastlane add_plugin firebase_app_distribution

    Firebase ν”ŒλŸ¬κ·ΈμΈμ„ μ„€μΉ˜ν•  λ•Œ Your user account isn't allowed... λΌλŠ” λ©”μ„Έμ§€κ°€ λ‚˜μ˜€λ©΄μ„œ Password λ₯Ό μž…λ ₯ν•˜λΌκ³  ν•  μˆ˜λ„ μžˆλŠ”λ°, κ·Έλƒ₯ Mac password μž…λ ₯ν•˜κ³  λ„˜μ–΄κ°‘λ‹ˆλ‹€.

    4-2. Firebase 둜그인

     
    fastlane run firebase_app_distribution_login​

    CLI둜 Firebase 에 둜그인 ν•˜λŠ” κ³Όμ •μž…λ‹ˆλ‹€. μ»€λ§¨λ“œ μž…λ ₯ μ‹œ 접속할 수 μžˆλŠ” 링크 URL 이 터미널에 보일텐데, μ ‘μ†ν•΄μ„œ Firebase 인증 ν•΄μ£Όμ‹œλ©΄ Refresh Token 이 λ‚ μ•„μ˜΅λ‹ˆλ‹€. ν•΄λ‹Ή 토큰은 잘 λ³΄κ΄€ν•΄μ£Όμ„Έμš”.

     

    5. ν™˜κ²½λ³€μˆ˜[ENV] μ„€μ •

    각 개발자 λ§ˆλ‹€ AppleID, Firebase Refresh Token 등이 ν™˜κ²½μ— 따라 λ‹¬λΌμ§ˆ 수 μžˆλŠ”λ°, Fastlane μ—μ„œλŠ” .env 파일둜 ν™˜κ²½λ³€μˆ˜λ₯Ό κ΄€λ¦¬ν•˜μ—¬ μ„œλ‘œ λ‹€λ₯Έ ν™˜κ²½μ—μ„œλ„ λ™μΌν•œ Lane 을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

     
    # Appfile
    app_identifier("com.test.test") 
    apple_id(ENV["APPLE_ID"]) 
    
    # .env 
    APPLE_ID="..."

    Fastlane 폴더 μ•ˆμ˜ Appfile μž…λ‹ˆλ‹€. app_identifier λŠ” μ–΄λŠ ν™˜κ²½μ—μ„œλ‚˜ λ™μΌν•˜μ§€λ§Œ, apple_id λŠ” 개발자 λ§ˆλ‹€ λ‹€λ₯΄κΈ° λ•Œλ¬Έμ— ν™˜κ²½λ³€μˆ˜λ‘œ μΉ˜ν™˜ν•©λ‹ˆλ‹€.

     

    6. Xcode μˆ˜μ •

    $(FLUTTER_BUILD_NAME)으둜 λ˜μ–΄μžˆλŠ” 뢀뢄을 ν˜„μž¬ 버전(ex 1.0.4)둜 ν•˜λ“œμ½”λ”©ν•©λ‹ˆλ‹€(졜초 1회)

    7. Firebase Distribution Lane 생성

    7-1. Firebase 섀정이 λλ‚¬μœΌλ‹ˆ, 이제 Firebase App Distribution 에 Upload ν•˜λŠ” Lane 을 μƒμ„±ν•©λ‹ˆλ‹€.

      # Firebase Distribution
      desc "Push a new beta build with Firebase App Distribution"
      lane :firebase do
        match(type:"adhoc")
    
        increment_build_number(
            build_number: latest_testflight_build_number + 1
        )
    
        build_app(
          workspace: "Runner.xcworkspace",
          scheme: "Runner",
        )
    
        firebase_app_distribution(
          app: ENV["IOS_FIREBASE_APP_DISTRIBUTION_APP"],
          firebase_cli_token: "4-2. Firebase λ‘œκ·ΈμΈμ—μ„œ 받은 Refresh Token",
          groups: "test",
          release_notes: "test"
        )
      end

    firebase_cli_token: 4-2. Firebase λ‘œκ·ΈμΈμ—μ„œ 받은 Refresh Token

    groups: Firebase App Distribution에 μžˆλŠ” “ν…ŒμŠ€νŠΈ 및 κ·Έλ£Ή”ν•­λͺ©μ˜ ν…ŒμŠ€νŠΈ κ·Έλ£Ή 이름

     

    7-2 Firebase 에 배포

    fastlane firebase λ₯Ό 터미널에 μž…λ ₯ν•©λ‹ˆλ‹€.(fastlane + lane이름)

     

    8. TestFlight Ditribution Lane 생성

    8-1. FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD μ„€μ •

    appleid.apple.com/account/manage μ— λ“€μ–΄κ°€μ„œ λ³΄μ•ˆ μͺ½μ— μ•± μ•”ν˜Έ 생성을 눌러 μ•”ν˜Έλ₯Ό 생성 ν›„ λ³΅λΆ™ν•˜μ—¬ λ³΄κ΄€ν•΄μ£Όμ„Έμš”.

    8-2. dotenv μ„€μ •

    μ•„λž˜ λͺ…λ Ήμ–΄λ‘œ dotenvλ₯Ό μ„€μΉ˜ν•΄μ£Όμ„Έμš” 

    sudo gem install dotenv

    μ•„λž˜ λͺ…λ Ήμ–΄λ‘œ envνŒŒμΌμ„ λ§Œλ“€μ–΄μ£Όμ„Έμš”

    touch .env

    μƒμ„±λœ envνŒŒμΌμ„ μ—΄μ–΄μ£Όμ„Έμš”

    FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD = "chkl-jpdl-xutb-bqmw"μ΄λ ‡κ²Œ μž‘μ„±ν•΄μ€λ‹ˆλ‹€.

     

    8-3. TestFlight 에 배포

      # TestFlight
      desc "build app and upload to testflight"
      lane :beta do
        match(type: "appstore")
    
        increment_build_number(
            build_number: latest_testflight_build_number + 1
        )
        build_app(
          configuration: "Release"
        )
        upload_to_testflight
      end

    fastlane beta λ₯Ό 터미널에 μž…λ ₯ν•©λ‹ˆλ‹€.(fastlane + lane이름)

     


    iOS 버전 및 λΉŒλ“œλ²ˆν˜Έ μžλ™ μˆ˜μ • 배포

     

    [laneμ„€λͺ…]

    firebase_release : private_lane :set_version을 톡해 버전 λΉŒλ“œ 번호λ₯Ό 1μ”© 올린 ν›„ Firebase에 λ°°ν¬ν•©λ‹ˆλ‹€

    firebase : increment_build_number(xcodeproj: "Runner.xcodeproj")λ₯Ό 톡해 λΉŒλ“œ 번호만 1올린 ν›„ Firebase에 λ°°ν¬ν•©λ‹ˆλ‹€

    beta : increment_build_number(xcodeproj: "Runner.xcodeproj")λ₯Ό 톡해 λΉŒλ“œ 번호λ₯Ό 1 올린 ν›„ Testflight에 μ—…λ‘œλ“œν•©λ‹ˆλ‹€

    private_lane :set_version : increment_version_number(xcodeproj: "Runner.xcodeproj"), increment_build_number(xcodeproj: "Runner.xcodeproj")λ₯Ό 톡해 버전 및 λΉŒλ“œ 번호λ₯Ό 1μ”© μ˜¬λ €μ€λ‹ˆλ‹€.

    default_platform(:ios)
    
    platform :ios do
    
      desc "Upload Release IPA fo Firebase Distribution"
      lane :firebase_release do
        match(type:"adhoc")
        set_version
        build
        firebase_upload(release: true)
      end
    
      desc "Version Setting"
      private_lane :set_version do
    
        increment_version_number(xcodeproj: "Runner.xcodeproj")
        increment_build_number(xcodeproj: "Runner.xcodeproj")
    
      end
    
      desc "Build"
      private_lane :build do |options|
        build_app(
          workspace: "Runner.xcworkspace",
          scheme: "Runner",
          configuration: "Release"
        )
      end
    
      desc "Upload Firebase"
      private_lane :firebase_upload do |options|
        firebase_app_distribution(
          app: ENV["IOS_FIREBASE_APP_DISTRIBUTION_APP"],
          firebase_cli_token: "4-2. Firebase λ‘œκ·ΈμΈμ—μ„œ 받은 Refresh Token",
          groups: "test",
          release_notes: "test"
        )
      end
    
      desc "Description of what the lane does"
      lane :custom_lane do
        # add actions here: https://docs.fastlane.tools/actions
      end
      # Firebase Distribution
      desc "Push a new beta build with Firebase App Distribution"
      lane :firebase do
        match(type:"adhoc")
    
        increment_build_number(xcodeproj: "Runner.xcodeproj")
    
        build_app(
          workspace: "Runner.xcworkspace",
          scheme: "Runner",
        )
    
        firebase_app_distribution(
          app: ENV["IOS_FIREBASE_APP_DISTRIBUTION_APP"],
          firebase_cli_token: "4-2. Firebase λ‘œκ·ΈμΈμ—μ„œ 받은 Refresh Token",
          groups: "test",
          release_notes: "test"
        )
      end
    
      # TestFlight
      desc "build app and upload to testflight"
      lane :beta do
        match(type: "appstore")
    
        increment_build_number(xcodeproj: "Runner.xcodeproj")
    
        build_app(
          configuration: "Release"
        )
        upload_to_testflight
      end
    end

     

     

     

     


    iOSλŠ” λ°°ν¬μ™„λ£Œ ν›„ appstore connectμ—μ„œ 수좜 κ·œμ • 정보 섀정을 ν•΄μ€˜μ•Ό ν•©λ‹ˆλ‹€

    ν•˜λ‹¨μ˜ “μœ„μ— μ–ΈκΈ‰λœ μ•Œκ³ λ¦¬μ¦˜μ— λͺ¨λ‘ ν•΄λ‹Ήν•˜μ§€ μ•ŠμŒ”을 μ„ νƒν•΄μ€λ‹ˆλ‹€.

    λŒ“κΈ€

Designed by Tistory.