Fastlane 자동배포

Ios_Roy·2023년 3월 5일
1

CI/CD

목록 보기
1/1
post-thumbnail

Fastlane 이란 ??

  • ruby 기반 클라이언트 자동 빌드 오픈소스 라이브러리

iOS 배포 과정 (아래 작업을 fastlane하나로 CI / CD 가능)

  • info.plist에서 버전과 빌드 올리는 작업
  • 타겟이 여러 개인 경우, 모든 타겟이 버전과 빌드가 동일한지 체크
  • 빌드
  • 3인 이상의 개발자가 동시에 개발하는 경우, 1년에 한 번 씩 certificate, provisioning profile을 발급받고 공유
  • archive를 누른 후 끝날 때까지 대기
  • archive가 완료되면 배포 버튼 누르고, 배포가 될 때 까지 대기
  • 노티

fastlane 기능

  • match: certificate와 provisioning profile을 생성 및 유지, git 레포지토리에 저장하여 동기화
  • gym: iOS 어플리케이션을 빌드하고 사이닝
  • deliver: 스크린샷, metaData(앱스토어에 앱을 올릴 때 필요한 데이터) 및 앱 번들 관리, 앱스토에 있는 앱을 업로드

fastlane설치

xcode-select --install

fastlane 추가

// 홈 블루 방식
brew install fastlane

//gem 방식 
sudo gem install fastlane

Fastlane 환경 설정

  • 설치 이후 설정하는 부분은 OS와 상관없이 동일하게 프로젝트 경로에서 진행합니다.
fastlane init

Fastlane을 어떤 목적으로 사용할지 묻는 질문에는 목적에 따라 번호를 누르면 되지만 저희는 4번 수동 방법으로 차후에 업로드 하는 방법을 정리 해보도록 하겠습니다

자 그러면 프로젝트 폴더에 다시 들어가보면 총 4개의 파일이 생성되어 있을거에요.

GemFile
GemFile.lock
AppFile --> 번들ID, 애플ID, 팀ID 등의 정보가 담긴 파일
FastFile --> 자동화할 명령어들이 담긴 파일

위 두개 GemFile 관련된 건 fastlane 자체 버전 관리 파일이라 신경쓸 일이 없습니다.

우리가 자주 사용할 건 AppFile, FastFile이겠죠?

빌드 스크립트 세팅하기

fastlane/appfile 파일을 열어봅시다.

여기는 앞서 말한듯이 앱 번들ID, 애플ID, 팀ID가 담겨있다고 했죠.

app_identifier("여기프로젝트번들ID") # The bundle identifier of your app
apple_id("여기여기이메일") # Your Apple Developer Portal username

app_identifier에는 ("번들ID")

apple_id에는 ("애플계정 이메일")을 입력 후 저장해주세요.

근데 여기서 다수의 인원이 프로젝트를 함께 할 경우 git으로 코드베이스를 받아 사용하게 되면 파일이 왔다갔다해서 apple_id 부분에 혼선이 생길 수 있습니다.

app_identifier(ENV["APP_IDENTIFIER"])
apple_id(ENV["APPLE_ID"])

apple_id 부분을 위와같이 수정 후 따로 파일을 만들어서 거기에 apple_id를 설정할거에요.

fastlane 폴더 안에 .env 파일을 생성 후 이메일을 적어줍시다.

APPLE_ID="자신의 애플 아이디"
APP_IDENTIFIER = "프로젝트 번틀 아이디 "

그리고 gitignore 파일을 열고 예외처리 해줍시다.

앱스토어 아이디를 로그인 하라고 나오면

Apple ID

암호를 생성하면 (ex: Test 입력)

ABCD-DEDE-ASDA와 같은 앱 암호가 뜨게 됩니다.

지금 바로 써야하니 따로 저장해서 보관해둡시다.

vim faslane/.env 를 해서 환경 변수 파일에 들어 갑니다

FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD="위에서 발급한 암호"

저장 해줍니다

이제 빌드 스크립트를 작성 해볼까요 ??

ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "600" 
일정시간 이후에도 빌드 될수 있게 구현을 해줍니다

패키지가 있으면 해당 패키지를 다운로드 해주어야 하겠죠 ?

desc "Runs `pod install`"   --> 설명 
  puts "Runs `pod install`"   
  lane :pod_install do   --> 실행  명령어 
    cocoapods(
      clean_install: true,
      podfile: "./Podfile"
    )
  end
순서대로 설명하면
1. lane : beta do ~
이 부분은 fastlane 실행 시 원하는 행동을 "지정"할 때 사용됩니다.
만약  pod_install 부분이  작업일경우 터미넝레서서  fastalane pod_install을 입력 하면  자동으로 스크립트가  실행됩니다 
lane : pod_install 부분을 lane : test로 바꾸면 터미널 실행 명령어는
fastlane test 가 되겠죠.

swiftlint 을 사용할경우

desc "Run swift code validation using SwiftLint"
  puts "Run swift code validation using SwiftLint"
  lane :lint do
    swiftlint(
      mode: :lint,
      ignore_exit_status: true,
      raise_if_swiftlint_error: true,
      executable: "./Pods/SwiftLint/swiftlint"
    )
  end

uinit test를 할경우

desc "Incrementing Build Number"

  lane:unit_Test do
  run_tests(
     workspace: "MarketApp.xcworkspace",  해당 프로젝트 워크 스페이스
      scheme: "MarketApp",  해당 프로젝트 스킴 
      devices: ["iPhone 12"],
      reinstall_app: true,
      result_bundle: true,
      app_identifier: "com.shoping.MarketApps",  해당 프로젝트 번들아잉디
      clean: true)
      slack(         성공했을경우 슬래에 메세지 가게 구현 
      message: "Unit test  성공", 
      slack_url: ENV["SLACK_URL_TEST"],
      payload: { "Build Date" => Time.new.to_s }
    )

 # ✅ 에러 처리.
   error do |lane, exception, options|
    slack(
      message: "에러 발생 : #{exception}",
      success: false,
      slack_url: ENV["SLACK_URL_TEST"]
    )
     end
   end

여기서 주의s lack 채널 알림 기능은 채널의 링크를 복사한다고 되는 게 아닌데요.

에 로그인하셔서 알림을 원하는 해당 채널에 권한을 준뒤 web hook url을 가져와 넣어주면 됩니다.

명령어를 더 넣을수도 있는데요자세한 사항은 fastlane - slack 을 참고해서 입맛에 맞게 추가하시면 됩니다~!!

이제 저장한 뒤

터미널에

fastlane unit_Test

이제 빌드 앱을 한후 아카이빙을 해볼까요??

desc "Install Profiles and Build My App"
  lane :build_ipa do
   clear_derived_data
   scan
    slack(
      message: "Unit test  성공",
      slack_url: ENV["SLACK_URL_TEST"],
      payload: { "Build Date" => Time.new.to_s }
    )

# ✅ 에러 처리.
error do |lane, exception, options|
    slack(
      message: "에러 발생 : #{exception}",
      success: false,
      slack_url: ENV["SLACK_URL_TEST"]
    )
     end
    gym(
      configuration: "Release",
      workspace: "MarketApp.xcworkspace",
      output_name: "MarketApp.ipa",
      scheme: "MarketApp",
      export_method: "app-store",
      xcodebuild_command: "xcodebuild",
      include_bitcode: false,
     skip_build_archive: false,
      xcodebuild_formatter: "xcpretty",
      output_directory: "./output/",
      clean: true,
      silent: true,
      include_symbols: false,
      xcargs: "ARCHIVE=YES",
     # disable_package_automatic_updates: true,
      skip_package_ipa: false,
     #skip_package_dependencies_resolution: false,
      #skip_package_pkg: false,
        export_options: {
         signingStyle: "automatic",
          uploadBitcode: false,
          provisioningProfiles: {
            "com.shoping.MarketApps" => "Affinity",
            "com.shoping.MarketApps" => "com.shoping.MarketApps AppStore"
             }

slack(
      message: "빌드 성공",
      slack_url: ENV["SLACK_URL_BUILD"],
      payload: { "Build Date" => Time.new.to_s }
    )

 # ✅ 에러 처리.
  error do |lane, exception, options|
    slack(
      message: "에러 발생 : #{exception}",
      success: false,
      slack_url: ENV["SLACK_URL_BUILD"]
    )
    end
  end
clear_derived_data

해당 프로젝트에 빌드 아키이빙 한걸 로그를 없애는 역활을 합니다

여기서 provisioningProfiles 은 앱스토어에 배포 준비를 할때 만든 인증서에 이름입니다 해당 프로젝트 번틀 아이디랑 만들어놓은 인증서 이름을 적어 주시면 됩니다

이제 테스트 플라이트 에 올려 보도록 할까요 ??

desc "Description of what the lane does"
   lane :upload_testflight do
    version = get_version_number(
        xcodeproj: "MarketApp.xcodeproj",
        target: "MarketApp"
       )
     build = get_build_number
    get_certificates
    get_provisioning_profile
    increment_build_number(
        build_number: latest_testflight_build_number + 1
    )
    lint
    build_ipa
    upload_to_testflight(
    demo_account_required: true,
    beta_app_description:"신발 커뮤니티 앱",
    api_key_path: "fastlane/apikey.json",  apikey가 있는 장소 
    ipa:"./output/MarketApp.ipa",  해당 ipa 파일이 있는 장소 
    changelog:"변경사항",
    skip_waiting_for_build_processing: true)
    slack(
      message: "Testflight 배포에 성공했습니다!",
      slack_url: ENV["SLACK_URL_UPLOAD"],
       payload: {
        "Version": version + " (" + build + ")",
        "Build Date" => Time.new.to_s
      }
    )
 # ✅ 에러 처리.
    error do |lane, exception, options|
    slack(
      message: "에러 발생 : #{exception}",
      success: false,
      slack_url: ENV["SLACK_URL_UPLOAD"]
    )
    end
  end

TestFlight에 업로드할 때 가장 최근 빌드 번호를 가져와서 그 다음 번호로 올리는 코드입니다.

저희는 **App Store Connect API Key 를 사용해서 appstore 커넥트에 연결하도록 해볼께요**

  1. 키 생성App Store Connect에서 API Key를 발급 받습니다.https://appstoreconnect.apple.com/access/users
  2. 생성된 키 다운로드(.p8)새로 고침을 하면 키를 다운받을 수 없으니 주의하세요.
  3. fastlane 디렉토리에 apikey.json 파일을 생성하고 아래와 같이 채워넣습니다.
{
  "key_id": "키아디",
  "issuer_id": "이슈아이디",
  "key": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHknlhdlYdLu\n-----END PRIVATE KEY-----",
  "duration": 1200,
  "in_house": false
}

파이어베이스 app distribution에 앱을 올려보도록 해용

desc:"upload Firebase App Distribution"
  lane:upload_firebase do
   version = get_version_number(
        xcodeproj: "MarketApp.xcodeproj",
        target: "MarketApp"
       )
     build = get_build_number
    firebase_app_distribution(
     app: ENV["IOS_FIREBASE_APP_DISTRIBUTION_APP"],
      groups: "Affinity",
      #googleservice_info_plist_path: "./Firebase/GoogleService-Info.plist",
       release_notes: "upload test",debug: true)
         slack(
          message: "파이어베스  배포에 성공했습니다!",
           slack_url: ENV["SLACK_URL_UPLOAD"],
            payload: {
            "Version": version + " (" + build + ")",
             "Build Date" => Time.new.to_s
              }
             )
         # ✅ 에러 처리.
        error do |lane, exception, options|
         slack(
           message: "에러 발생 : #{exception}",
           success: false,
           slack_url: ENV["SLACK_URL_UPLOAD"]
            )
        end
     end

API 엑세스 설정

개발자 페이지로 가서 API 엑세스 -> 새 OAuth 클라이언트 만들기 -> Google Cloud Platform 순서대로 들어와 준다. 새 OAuth 클라이언트 만들기가 클릭 안될거같이 생겼는데, 그냥 배경색만 없는 버튼이더라,,

그럼 이 화면으로 넘어오는데, 프로젝트 설정 -> 전체 -> Google Play Console Developer 순으로 선택해준다.

TMI로 오른족 상단에 내 프사가 잼민스러운데,,ㅋㅋㅋ 구글 개발자 계정을 중1때 만들어서… 내가 코딩을 마크로 시작했고, 중학생때는 마크만 엄청 했었어서, 이 개발자 계정 아이디가 gravityminecraft5304 이다 😳😳 중력마크!

상단에 서비스 계정 만들기를 눌러준다.

서비스 계정 세부정보 입력 해주고,

서비스 계정, 서비스 계정 사용자 같은 라인으로 위치 맞추느라 힘들었다

다음 단계인 여기서 꼭! 서비스 계정 -> 서비스 계정 사용자로 해야 한다. 나머지 한 단계는 그냥 완료 해주면 서비스 계정이 생성되고,

오른쪽 끝에 메뉴를 눌러서 키 관리로 와준다.

원래 유형 저 부분에 표시할 행이 없습니다 라고 뜬다. (난 미리 해놔서…)

키 추가를 눌러서 JSON 그대로 만들기를 해준다. 그럼 json 파일이 다운로드 된다. 다음으로 다시 구글 개발자 페이지로 돌아와서,

서비스 계정에 보면 위에서 만든 계정이 추가돼 있다! 이제 마지막으로 엑세시 권한을 부여해 주면 끝난다. 엑세스 권한 부여를 눌러서

위에서 발급 받은 json을 .env 파일에 json 파일 위치를 저장 해준다

GOOGLE_APPLICATION_CREDENTIALS=$PWD/fastlane/firebase_login_credentials.json

앱스토어 배포

lane :get_dev_certs  do
    cert(development: true)
    sign(development: true)
 end

 desc "Description of what the lane does"
   desc "Push a new release build to the App Store"
  lane :release do |options|
  # ✅ 매개변수를 넣어서
  # fastlane release version:"2.1.0"
  # 과 같이 사용할 수 있다.
    if options[:version]
    #enable_automatic_code_signing
      increment_version_number(version_number: options[:version])
       version = get_version_number(
        xcodeproj: "MarketApp.xcodeproj",
        target: "MarketApp"
       )
     build = get_build_number
      get_certificates
      get_provisioning_profile
      build_ipa
       unit_Test
       upload_to_app_store(
        username: "shuwj81@daum.net",
        team_id: "N94CS4N6VR",
        api_key_path: "fastlane/apikey.json",
        skip_metadata: false,
        metadat_path: "./metadata",
        skip_screenshots: false,
        force: true,
        submit_for_review: true,
        automatic_release: true,
        precheck_include_in_app_purchases: false,
        submission_information: {
        add_id_info_uses_idfa: false,
        export_compliance_encryption_updated: false,
        export_compliance_uses_encryption: false,
        content_rights_contains_third_party_content: false}
           )

    #성공했을때  슬랙 메세지
      slack(
      message: "배포  성공",
      slack_url: ENV["SLACK_URL_UPLOAD"],
      payload: {
        "Version": version + " (" + build + ")",
         "Build Date" => Time.new.to_s
      }
    )

 # ✅ 에러 처리.
   error do |lane, exception, options|
    slack(
      message: "에러 발생 : #{exception}",
      success: false,
      slack_url: ENV["SLACK_URL_UPLOAD"]
    )
     end
    # ✅ if 문을 종료하기 위한 end
    end
   end

References

https://docs.fastlane.tools/app-store-connect-api/#using-fastlane-api-key-json-filehttps://docs.fastlane.tools/best-practices/continuous-integration/

profile
iOS 개발자 공부하는 Roy

0개의 댓글