본문 바로가기

💻 개발IT/Flutter

Flutter 앱 배포 자동화하기 ② (with. Fastlane)

 

Fastlane 설치 및 안드로이드 적용은 1편에 있으니 참고 부탁드립니다!

Flutter 앱 배포 자동화하기 ① (with. Fastlane)

 

Flutter 앱 배포 자동화하기 ① (with. Fastlane)

Fastlane ?다양한 작업을 자동화해주는 라이브러리로 iOS / Android 앱 개발에서 배포 및 빌드 자동화 가능'Fastfile'이라는 설정 파일을 통해 워크플로우를 정의할 수 있음 Fastlane 설치$ brew install fastlane

sunghee2.tistory.com

 

iOS Fastlane 적용

Fastlane 초기화

$ cd ios
$ fastlane init

 

초기화 명령어를 실행하면

 

앱 사용 목적을 물어보는 프롬프트가 나오는데

앱 자동 배포가 목적이라 3을 입력합니다.

 

 

로그인 프롬프트가 나오는데

애플 아이디(이메일), 비밀번호, 이중인증 번호 등을 입력해줍니다

 

 

그리고 앱의 메타데이터를 fastlane으로 관리할거냐는 질문이 나오는데 y 를 입력합니다.

 

 

완료되면 fastlane 폴더와 Gemfile 파일이 생성됩니다.

 

Fastlane 작성을 위한 셋팅

배포할 때 릴리즈 노트를 작성해야하는데 (물론, 배포 옵션에 릴리즈 노트를 skip할 수도 있음)

초기화할 때 메타데이터를 관리한다고 했기 때문에 metadata 폴더가 생성 되어 이곳에서 수정하면 된다.

 

 

fastlane/metadata/ko(언어)/release_notes.txt에서 수정하면

fastlane이 알아서 인식해서 배포할 때 가지고 가게 된다.

 

 

추가로

 

 

 

배포 여부에 따라 Slack 알림이 가는데 이 Slack 웹훅 URL을 가지고 있을 .env 파일을 fastlane 폴더 안에 생성한다.

(.env파일도 gitignore에 추가 필요)

 

 

 

이런 식으로 정의하면 된다.

 

그리고, 메타데이터 가져올 때 리뷰 관련한 정보는 

제대로 못 가져오는 것 같아 fastlane/metadata/review_information 폴더는 삭제한다.

 

Fastlane 암호 설정

https://account.apple.com/account/manage

 

Apple Account

Your account you use for all Apple services

account.apple.com

 

 

앱 암호를 클릭해서 새로 발급을 받는다.

 

 

 

발급받은 암호는

위에서 만든 .env 파일 안에 넣어준다.

 

 

Fastlane 작성

fastlane/Fastfile에 아래와 같이 작성한다.

default_platform(:ios)

# Slack 메시지를 전송하는 함수
def send_slack_message(success:, app_name:, version_name:, version_code:, error_message: nil)
  # 성공 또는 실패에 따른 메시지 구성
  message = if success
              "✅ *#{app_name}* App Store 배포 완료!\n*버전*: #{version_name} (#{version_code})\n\n"
            else
              "❌ *#{app_name}* App Store 배포 실패\n*버전*: #{version_name} (#{version_code})\n*에러 메시지*: #{error_message}"
            end

  UI.message(ENV['SLACK_URL'])
  # Slack 알림 전송
  slack(
    message: message,
    success: success,
    slack_url: ENV['SLACK_URL']
  )
end

# build.gradle 대신 xcodeproj에서 현재 버전 가져오기
def get_version_name
  get_version_number(xcodeproj: "Runner.xcodeproj")
end

def get_version_code
  get_build_number(xcodeproj: "Runner.xcodeproj").to_i
end

# 버전 증가 로직
def increment_version(version_name, version_type)
  return version_name if version_type == 4  # 버전 타입이 4이면 현재 버전을 그대로 반환
  
  version_numbers = version_name.split('.').map(&:to_i)

  case version_type
  when 1
    version_numbers[0] += 1
    version_numbers[1] = 0
    version_numbers[2] = 0
  when 2
    version_numbers[1] += 1
    version_numbers[2] = 0
  when 3
    version_numbers[2] += 1
  else
    UI.user_error!("올바르지 않은 버전 유형입니다.")
  end

  version_numbers.join('.')
end

def get_version_type_from_user
  version_type = UI.input("증가할 버전 유형을 입력하세요: 1은 Major, 2는 Minor, 3은 Patch, 4는 변경 없음").to_i

  unless [1, 2, 3, 4].include?(version_type)
    UI.user_error!("잘못된 입력입니다. 1, 2, 3 또는 4를 입력하세요.")
  end

  version_type
end

platform :ios do
  desc "배포"
  lane :deploy do
    begin
      app_name = "앱이름"
      
      # 현재 버전 가져오기
      current_version_name = get_version_name
      current_version_code = get_version_code
      version_type = get_version_type_from_user

      # 새 버전 계산
      new_version_name = increment_version(current_version_name, version_type)
      new_version_code = current_version_code + 1
      UI.message("새 버전: #{new_version_name} (#{new_version_code})")

      # 버전 업데이트
      increment_version_number(
        version_number: new_version_name,
        xcodeproj: "Runner.xcodeproj"
      )
      increment_build_number(
        build_number: new_version_code,
        xcodeproj: "Runner.xcodeproj"
      )

      # 빌드 및 배포
      build_app(workspace: "Runner.xcworkspace", scheme: "Runner")
      upload_to_app_store(
        # HTML report를 스킵
        force: true,
        skip_screenshots: true,
        submit_for_review: true,
        automatic_release: true,
      )

      send_slack_message(
        success: true, 
        app_name: app_name, 
        version_name: new_version_name, 
        version_code: new_version_code
      )
    rescue => e
      # 실패시 버전 되돌리기
      increment_version_number(
        version_number: current_version_name,
        xcodeproj: "Runner.xcodeproj"
      )
      increment_build_number(
        build_number: current_version_code,
        xcodeproj: "Runner.xcodeproj"
      )

      send_slack_message(
        success: false, 
        app_name: app_name, 
        version_name: new_version_name, 
        version_code: new_version_code, 
        error_message: e.message
      )
      UI.user_error!("배포 중 오류가 발생했습니다.")
    end
  end

  desc "TestFlight 배포"
  lane :beta do
    begin
      app_name = "앱이름"
      
      # 현재 버전 가져오기
      current_version_name = get_version_name
      current_version_code = get_version_code
      version_type = get_version_type_from_user

      # 새 버전 계산
      new_version_name = increment_version(current_version_name, version_type)
      new_version_code = current_version_code + 1
      UI.message("새 버전: #{new_version_name} (#{new_version_code})")

      # 버전 업데이트
      increment_version_number(
        version_number: new_version_name,
        xcodeproj: "Runner.xcodeproj"
      )
      increment_build_number(
        build_number: new_version_code,
        xcodeproj: "Runner.xcodeproj"
      )

      # 빌드 및 TestFlight 배포
      build_app(workspace: "Runner.xcworkspace", scheme: "Runner")
      upload_to_testflight(
        skip_waiting_for_build_processing: true
      )

      send_slack_message(
        success: true, 
        app_name: "#{app_name} (TestFlight)", 
        version_name: new_version_name, 
        version_code: new_version_code
      )
    rescue => e
      # 실패시 버전 되돌리기
      increment_version_number(
        version_number: current_version_name,
        xcodeproj: "Runner.xcodeproj"
      )
      increment_build_number(
        build_number: current_version_code,
        xcodeproj: "Runner.xcodeproj"
      )

      send_slack_message(
        success: false, 
        app_name: "#{app_name} (TestFlight)", 
        version_name: new_version_name, 
        version_code: new_version_code, 
        error_message: e.message
      )
      UI.user_error!("TestFlight 배포 중 오류가 발생했습니다.")
    end
  end
end

 

 

실행

ios 폴더에서 아래 명령어를 실행하면 위 과정이 자동으로 실행된다.

$ fastlane deploy

 

 

 

중간에 위와 같이 버전 업데이트 어떤 걸 할 건지 물어보는 프롬프트가 뜨는데

원하는 숫자를 입력하면 된다. (변경 안 하려면 4번)

 

 

iOS는 안드로이드보다 한참 걸린다..

Testflight 올라가기까지 기다리고 있기 때문,,,

 

 

아무래도 시간이 많이 걸리기 때문에,

심사 없이 testflight로 올리고 싶으면 아래 명령어를 입력하면 된다.

$ fastlane beta
반응형