Fastlane, Travis-CI로 React Native 프로젝트 배포 자동화 만들기 (2)

January 15, 2019

이 글은 React Native 프로젝트의 iOS 빌드 및 Test Flight에 업로드하는 것을 자동화 할 수 없을까?에 대한 궁금증에서 출발했습니다. 여기서 말하는 자동화의 범위는 git push origin master 이후의 모든 태스크는 기계가 알아서 하도록 만드는 것을 말합니다.

이번에 할일

  • 프로젝트에 Fastlane 설정
  • 테스트 플라이트 메뉴얼 업로드
  • 테스트 플라이트 CI 업로드
  • Travis CI에 환경변수 설정
  • .travis.yml 작성

바로 전편에서 이야기 했던 Fastlane 의 역할 중에서 Beta Deployment 기능을 기억하시나요? Fastlane 을 프로젝트에 설정해두면 Xcode를 사용하지 않더라도 터미널에서 테스트 플라이트로 업로드 하는 일이 가능합니다. 개인의 맥에서 이것이 가능하다면 CI 툴이라고 안될 일은 없겠죠? 이 일을 하기 위해서는 프로젝트에 먼저 Fastlane 을 설정해야 합니다.

프로젝트에 Fastlane 설정

NOTE: 앱스토어 테스트 플라이트에 빌드를 자동으로 올리는 것이 처음이라면 먼저 수동으로 한번은 업로드를 해준 후에 시작할 수 있습니다.

Fastlane 설정을 위해서는 리액트 네이티브 프로젝트 루트에 fastlane 디렉토리를 만들어야 합니다. 그리고 그 안에 AppfileFastfile 을 만들어야 합니다. 이 두개의 파일에는 다음과 같은 내용이 담기게 됩니다.

Appfile 에는 프로젝트의 app_identifierapple_id 가 있어야 합니다. 만약 여러분이 사용하는 apple_id가 여러 팀에 속해 있다면 추가로 어떤 팀의 프로젝트인지 확실히 하기 위해서 itc_team_idteam_id 를 추가로 작성해야 합니다.

Appfile

app_identifier("com.example.sandbox") # The bundle identifier of your app
apple_id("apple@example.com") # Your Apple email address

itc_team_id("111111111") # App Store Connect Team ID
team_id("D23GPGDW1A") # Developer Portal Team ID

로컬에서 테스트 플라이트에 업로드 하는 스크립트

Fastfile 파일은 태스크를 정의하는 파일입니다. 이 파일 역시 fastlane 디렉토리 안에 위치해 있으면 됩니다. 일단 CI가 아닌 로컬 맥에서 테스트 플라이트로 업로드 하는 것을 정의하면 다음과 같이 작성할 수 있습니다.

default_platform(:ios)

platform :ios do
  
  desc "Push Example to TestFlight Manually"
  lane :manual_testflight do
    
    # Fetch the necessary certificates and 
    # provisioning profiles into default keychain.
    match(
      readonly: true,
      type: "appstore"
    )
    
    # Increment the build number using the
    # latest Testflight build number.
    increment_build_number(
      build_number: latest_testflight_build_number() + 1,
      xcodeproj: "./ios/example.xcodeproj"
    )

    # Build the application using the
    # specified scheme.
    build_app(
      scheme: "example",
      project: "./ios/example.xcodeproj",
      export_method: "app-store"
    )

    # Upload the application to Testflight
    upload_to_testflight(
      skip_waiting_for_build_processing: true
    )
  end

end

이 스크립트는 다음과 같은 동작을 합니다.

  • match 액선은 빌드에 필요한 인증서를 내려받습니다.
  • increment_build_number 는 앱스토어 커넥트에 마지막으로 업로드 된 빌드 넘버를 확인한 후에 1을 증가시킵니다.
  • build_app 앱을 빌드하여 .ipa 파일을 만듭니다.
  • upload_to_testflight.ipa 파일을 테스트 플라이트로 업로드를 합니다.

이 스크립트를 실행하기 전에 match와 upload에 필요한 환경변수를 로컬에 설정을 해야 합니다. 특히 인증서를 Git에 올리기 위해 만들었던 passphrase 값을 MATCH_PASSWORD 에 지정해야 하는 것을 잊지 마세요.

export FASTLANE_PASSWORD="YOUR_APPLE_ID_PASSWORD"
export MATCH_PASSWORD="YOUR_CERTIFICATES_PASSPHRASE"

이제 이 스크립트는 다음과 같은 CLI 명령어를 통해 실행할 수 있습니다.

fastlane ios manual_testflight

자동으로 테스트 플라이트에 업로드하는 스크립트

이제 CI 툴에서 동작하는 스크립트를 만들 차례입니다. 대부분 위에서 작성한 것과 비슷하지만 약간만 수정을 하면 됩니다. 이번에 만들 lanetravis_testflight 라고 정하도록 하겠습니다.

default_platform(:ios)

platform :ios do
  
  desc "Push Example to TestFlight with Travis CI"
  lane :travis_testflight do
    
    # Fetch the keychain env variables
    # securely stored in the travis.yml.
    keychain_name = ENV["MATCH_KEYCHAIN_NAME"]
    keychain_password = ENV["MATCH_KEYCHAIN_PASSWORD"]

    # Create a temporary keychain to 
    # store the certificates.
    create_keychain(
      name: keychain_name,
      password: keychain_password,
      default_keychain: true,
      unlock: true,
      timeout: 3600,
      add_to_search_list: true
    )

    # Fetch the necessary certificates and 
    # provisioning profiles.
    match(
      keychain_name: keychain_name,
      keychain_password: keychain_password,
      git_url: match_git_url,
      type: "appstore",
      readonly: true
    )
    
    # Increment the build number using the
    # latest Testflight build number.
    increment_build_number(
      build_number: latest_testflight_build_number() + 1,
      xcodeproj: "./ios/Example.xcodeproj"
    )

    # Build the application using the
    # specified scheme.
    build_app(
      scheme: "Example",
      project: "./ios/Example.xcodeproj",
      export_method: "app-store"
    )

    # Upload the application to Testflight
    upload_to_testflight(
      skip_waiting_for_build_processing: true
    )

    # Remove the temporary keychain leaving
    # no trace.
    delete_keychain(
      name: keychain_name
    )
  end

end

이 스크립트는 manual_testflight 와 거의 비슷하게 동작합니다. 단지 CI 인스턴스에 키체인을 생성하고 인증서를 저장하는 것이 다르고 맨 마지막에 해당 케체인과 함께 다운로드 받은 인증서를 제거하는 절차가 추가되었을 뿐 입니다. 단, 개인 맥에서 이 스크립트를 실행하지는 마세요. 만약 개인 맥에서 실행하는 경우 원래의 키체인 데이터가 망가질 수도 있습니다. 그러니 CI 머신에서만 실행하도록 주의하세요.

Travis CI Setup

manual_testflight 스크립트는 필요한 환경변수를 로컬 맥에 설정했지만 Travis CI에서는 프로젝트 설정에서 환경변수를 입력할 수 있습니다. 필요한 환경변수는 다음과 같습니다.

  • CI_USER_TOKEN: GitHub Personal Access Token with repo permissions. 프라이빗 저장소에 접근하기 위한 권한이 필요합니다. 여기서 생성할 수 있습니다.
  • FASTLANE_USER: 앱스토어 커넥트, 애플 개발자 포털 사용자 아이디 입니다.
  • FASTLANE_PASSWORD: 앱스토어 커넥트, 애플 개발자 포털 사용자 패스워드 입니다.
  • LANG: Fastlane은 UTF-8에서 정상적으로 동작합니다. CI 툴에서 이 값이 ASCII로 설정되어 있는 경우가 있어서 다음과 같은 값을 입력해주어야 합니다. en_US.UTF-8
  • LC_ALL: en_US.UTF-8
  • MATCH_PASSWORD: match 를 통해 생성한 인증서의 passphrase
  • MATCH_KEYCHAIN_NAME: 키체인 이름 eg. ios-build.keychain
  • MATCH_KEYCHAIN_PASSWORD: 키체인 패스워드
  • MATCH_GIT_URL: certificates가 있는 프라이빗 깃헙 저장소 주소입니다. eg. https://github.com/example/certificates.git

이 환경변수들은 .travis.yml 을 실행할 때 사용하기도 하고 앞서 정의한 Fastlane 파일을 실행할 때 사용하기도 합니다. Travis CI 작업정의 파일인 .travis.yml 은 다음과 같이 작성할 수 있습니다.

cache:
  yarn: true
  directories:
    - $HOME/.yarn-cache
    - node_modules
branches:
  only:
  - master
notifications:
  email:
    on_success: never
    on_failure: always

matrix:
  include:
    - language: objective-c
      os: osx
      osx_image: xcode10.1
      node_js: false
      before_install:
        - nvm install 10
        - node --version
        - npm install -g yarn
        - yarn -version
        - gem install fastlane --no-rdoc --no-ri --no-document --quiet
        - echo -e "machine github.com\n  login $CI_USER_TOKEN" >> ~/.netrc
      install:
        - yarn install
      script:
        - fastlane ios travis_testflight

Time Saving Tip

Travis CI를 동작시킨 후에 앱스토어 커넥트에 Missing Compliance 경고가 뜨는 경우에는 Info.plist 파일을 수정하여 해결할 수 있습니다. Info.plist 파일에 ITSAppUsesNonExemptEncryption 값을 추가하고 NO 로 설정하세요. 이에 대한 내용은 스택오버플로우에서 찾을 수 있습니다.

Ref.

Comments

comments powered by Disqus