fastrane의 CircleaCI를 사용하여 ReactNative 애플리케이션을 자동으로 구축하는 이야기

안녕하세요.
이 기사는 airCloset Advent Calendar 2021 6일 만에 투고되었다.
프리랜서 엔지니어로 활동하면서 주식회사 에어클로셋(이하 에어크로)의 일부 개발을 소개할 수 있도록 허락해 주십시오.

개요


항공사에서는 베트남의 해외 시장을 활용해 테스트 인원을 외부로 아웃소싱해 인원 출입이 많았고, PL을 비롯한 비엔지니어들이 프로젝트 단위로 개발해 실기 확인을 앞두고 협업이 까다로워졌다.
그래서
누가 제출했든지 간에 누구든지 즉시 실기로 확인할 수 있다
필요조건으로 하다
fastlane의 CircleaCI를 사용한 ReactNative 애플리케이션 구축 자동화
그래서 내용을 소개합니다.

개발 환경


ReactNative에서 ios/android를 위한 응용 프로그램을 제공하고 CI 환경으로서CircleaCI를 사용합니다.
또 동작 확인 방법으로 디플로이 게이트에서 무대 환경을 목표로 한 애플리케이션을 펼쳐 프로젝트 관계자에게 공유했다.

대응 방침


이용 방법


fastlane.
루비제 도구로 문서 잘 정돈됐고, iOS 앱을 만들 때 간지러운 부분도 충분해 패스트라인을 적용했다.그 가려운 곳에 관해서는 다음과 같이 알기 쉽다.
https://engineering.visional.inc/blog/_161/ios-manage-certificates/
특히 공중에 있는 블랙은 위에서 말한 바와 같이 해안 소프트웨어를 이용했기 때문에 엔지니어의 출입이 많을 것으로 추정되며, iOS 개발용 증서 발행부터 설치하기가 상당히 번거롭다.
따라서 Giithub Teams의read/write 권한 수여와 Giithub Repository에 암호화된 Provisioning Profile을 설치하면 각자 필요한 인증서를 취득하면 업무 효율이 크게 향상되는 것이 장점이라고 생각합니다.

CircleaCI에 MacOS VM 필요


ios 프로그램을 구축할 때 xcode가 필요하기 때문에 maxOS VM을 사용합니다.
이 경우 CircleaCI계획에 대한 upgrade가 필요합니다.

공기 흐름에 적응하는 작업 절차


Stage용 앱을 DeployGate에 제출하고 사내 구성원 등이 동작을 확인하고 있다.
이에 따라 CircleaCI에서 DeployGate에 업로드될 때까지 구현됐다.
또한 슬랙에서 커뮤니케이션이 이루어졌기 때문에 성공 또는 오류 알림을 올리면 슬랙에게 알립니다.

ReactNative에서 fastlane 가져오기


Gemfile


source "https://rubygems.org"

# setup_circle_ci action was released in 2.107.0
gem 'fastlane', '>=  2.191'
가능한 공동 환경을 위해 Gemfile에서 가져옵니다.
ReactNative 응용 프로그램의 root dir 에 배치됩니다.

fastlane


fastlane는 상호작용 모드에서 동작을 지정할 수 있지만,CircleaCI에서 이동하려면 어느 정도의 지정과 간단한 이동task를 OS/android로 각각 표현해야 한다.

android/fastlane


안드로이드는 간단합니다.
건물은fastlane에gradle준비되어있어이용만 한다.
보고 싶은 사람 다 여기를 눌러주세요.
android/fastlane/Fastfile
fastlane_version "2.191.0"

default_platform :android

platform :android do
  before_all do
    # https://docs.fastlane.tools/best-practices/continuous-integration/circle-ci/
    setup_circle_ci
  end

  desc "Build a new version for release"
  lane :build_release do
     gradle(
      task: "assemble",
      build_type: "Release"
    )
  end

  desc "Build a new version to upload to deploygate"
  lane :build_for_deploygate do
    commit = last_git_commit
    build_release
    deploygate(
      api_token: ENV["DEPLOYGATE_API_TOKEN"],
      user: ENV["DEPLOYGATE_USER"],
      apk: "./app/build/outputs/apk/release/app-release.apk",
      message: "uploaded with fastlane, branch: #{git_branch}, last commit message: #{commit[:message]}, last commit hash: #{commit[:abbreviated_commit_hash]}"
    )
    slack(
      message: "assembleReleaseなandroidアプリビルドとdeploygateへのアップロードに成功しました",
      payload: {
        "App Name" => "{YOUR_APP_NAME}"",
	"Circle Build Url" => ENV["CIRCLE_BUILD_URL"]
      }
    )
  end

  desc "Build a new version to upload to beta"
  lane :build_for_beta do
    build_release
    # 省略
  end

  # error block is executed when a error occurs
  error do |lane, exception|
    puts exception
    slack(
      # message with short human friendly message
      message: exception.to_s,
      success: false,
      # Output containing extended log output
      payload: {
        "Output" => exception.to_s,
        "Circle Build Url" => ENV["CIRCLE_BUILD_URL"]
      }
    )
  end

end

ios/fastlane


ios는 상당히 번거롭다.
구축할 때 Provisioning Profile이 필요합니다.
보고 싶은 사람 다 여기를 눌러주세요.
ios/fastlane/Fastfile
fastlane_version "2.191.0"

default_platform :ios

platform :ios do
  before_all do
    # https://docs.fastlane.tools/best-practices/continuous-integration/circle-ci/
    setup_circle_ci
  end

  desc "Build a new version for adhoc"
  lane :build_adhoc do |options|
    sync_code_signing(type: "adhoc", readonly: true)
    update_code_signing_settings(
      path: "{YOUR_APP_NAME}.xcodeproj",
      code_sign_identity: ENV["IOS_CODE_SIGN_ID"],
      profile_name: ENV["IOS_ADHOC_PROFILE_NAME"],
      use_automatic_signing: false,
    )
    build_ios_app(
      export_method: "ad-hoc",
      output_directory: "./fastlane/build/adhoc/",
      export_options: {
        provisioningProfiles: {
          "{YOUR_APP_BUNDLE_ID}" => ENV["IOS_ADHOC_PROFILE_NAME"]
        }
      }
    )
    update_code_signing_settings(
      path: "{YOUR_APP_NAME}.xcodeproj",
      code_sign_identity: ENV["IOS_CODE_SIGN_ID"],
      profile_name: ENV["IOS_ADHOC_PROFILE_NAME"],
      use_automatic_signing: true,
    )
  end

  desc "Build a new version to upload to deploygate"
  lane :build_for_deploygate do
    commit = last_git_commit
    build_adhoc
    deploygate(
      api_token: ENV["DEPLOYGATE_API_TOKEN"],
      user: ENV["DEPLOYGATE_USER"],
      ipa: "./fastlane/build/adhoc/aircloset.ipa",
      message: "uploaded with fastlane, branch: #{git_branch}, last commit message: #{commit[:message]}, last commit hash: #{commit[:abbreviated_commit_hash]}"
    )
    slack(
      message: "adhoc用のiosアプリビルドとdeploygateへのアップロードに成功しました",
      payload: {
        "App Name" => "{YOUR_APP_NAME}",
        "Circle Build Url" => ENV["CIRCLE_BUILD_URL"]
      }
    )
  end

  desc "Build a new version for appstore"
  lane :build_appstore do
        # 省略
  end

  desc "Build a new version to upload to testflight"
  lane :build_for_testflight do
    # 省略
  end

  # error block is executed when a error occurs
  error do |lane, exception|
    slack(
      # message with short human friendly message
      message: exception.to_s,
      success: false,
      # Output containing extended log output
      payload: {
        "Output" => exception.to_s,
        "Circle Build Url" => ENV["CIRCLE_BUILD_URL"]
      }
    )
  end

end
sync_code_signing(type: "adhoc", readonly: true)
Giithub Repository에 있는 Provisioning Profile에 액세스합니다.readonly, write 권한이 필요 없고, read 권한이 있는 계정은 Provisioning Profile을 획득할 수 있습니다.
따라서 로컬에서 ios 응용 프로그램을 처음 만들기 위해 Provisioning Profile이 필요할 때github 계정에read 권한이 있으면
bundle exec fastlane match { development|adhoc|appstore } --readonly
는 CLI에서 사용할 수 있습니다.편리
그나저나 matchsync_code_signing의 별명이다.
update_code_signing_settings(
  path: "{YOUR_APP_NAME}.xcodeproj",
  code_sign_identity: ENV["IOS_CODE_SIGN_ID"],
  profile_name: ENV["IOS_ADHOC_PROFILE_NAME"],
  use_automatic_signing: false,
)
구축 시 Provisioning Profile을 지정하기 위해서는 update_code_signing_settingsuse_automatic_signing: false 후build가 필요합니다.use_automatic_signing: true 제자리에 둔 것은 현지에서도 이동할 수 있기 때문이다.)
https://qiita.com/nokono/items/1d3d200eef7ea7d705e3
이것은 참고 가치를 띠고 있다.
이상은fastlane측의 설정입니다.
참고로 ENV 지정을 통해CircleaCI Project Settings Environment Variables로 설정하여 호출할 수 있습니다.로컬에서 사용할 때dotenv &direnv를 넣는 것도 괜찮을 것 같아요.
Matchfile을 많이 지정했지만 중요하지 않으니 사랑을 끊는 것을 허락해 주세요.

CircleaCI에서 fastlane 이동


circleaci 설정을 다 보신 분들은 여기를 눌러주세요.
.circleci/config.yml
version: 2.1

orbs:
  node: circleci/[email protected]
  android: circleci/[email protected]

executors:
  ios-build-machine:
    macos:
      xcode: "12.5.1"

commands:
  setup_to_build:
    description: "アプリビルドに必要な共通セットアップ処理"
    steps:
      ## 省略 - node_modulesのinstallなどが必要です。##
      ## install gems ##
      - restore_cache:
          keys:
            - gem-v1-{{ arch }}-{{ .Branch }}-{{ checksum "Gemfile.lock" }}
      - run: bundle check || bundle install --path vendor/bundle
      - save_cache:
          key: gem-v1-{{ arch }}-{{ .Branch }}-{{ checksum "Gemfile.lock" }}
          paths:
            - vendor/bundle
  setup_to_build_ios:
    description: "iosビルドに必要な共通セットアップ処理"
    steps:
      - run:
          name: jsbundle init
          command: yarn ios-bundle # npx react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ios/main.jsbundle --assets-dest ios
      ## install cocoapods ##
      - restore_cache:
          keys:
            - cocoapods-v1-{{ checksum "ios/Podfile.lock" }}
            - cocoapods-v1
      - run:
          name: pod install
          command: cd ios && bundle exec pod install
      - save_cache:
          key: cocoapods-v1-{{ checksum "ios/Podfile.lock" }}
          paths:
            - ios/Pods
  setup_to_build_android:
    description: "androidビルドに必要な共通セットアップ処理"
    steps:
      ## build ##
      - run:
          name: yarn android-bundle
          command: yarn android-bundle # npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android /app/src/main/assets/index.android.bundle
          no_output_timeout: 30m
  setup_env_to_stg:
    description: "stgの環境変数をセットする"
    steps:
      - run:
          name: set env stg
          command: grep -v '^\s*#' env.staging | grep -v '^\s*$' | while read line; do echo "export ${line}" >> $BASH_ENV; done
      - run:
          name: reload BASH_ENV
          command: |
            source $BASH_ENV
            echo $AC_API_URL
  ## setup_env_to_prod: 省略 ##
 

jobs:
  ## npm_dependencies: 省略 ##

  ## test: 省略 ##
  
  build_ios_stg:
    executor: ios-build-machine
    working_directory: /Users/distiller/project
    steps:
      - checkout
      - setup_env_to_stg
      - setup_to_build
      - run:
          name: set env to staging
          command: yarn switch-config:stg
      - setup_to_build_ios
      ## build ##
      - run:
          name: fastlane ios for staging
          command: cd ios && bundle exec fastlane build_for_deploygate
      - run:
          name: zip
          command: cd ios/fastlane/build && zip -r ipa.zip adhoc
          when: always
      - store_artifacts:
          path: /Users/distiller/project/ios/fastlane/build
 
  build_android_stg:
    executor:
      name: android/android-machine
      resource-class: medium
    environment:
      JVM_OPTS: -Xmx3200m
      GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-Xmx3200m -XX:+HeapDumpOnOutOfMemoryError"'
    working_directory: ~/project
    steps:
      - checkout
      - setup_env_to_stg
      - setup_to_build
      - run:
          name: set env to staging
          command: yarn switch-config:stg
      - setup_to_build_android
      - run:
          name: fastlane android for staging
          command: cd android && bundle exec fastlane build_for_deploygate
      - run:
          name: zip
          command: cd android/app/build/outputs && zip -r apk.zip apk
          when: always
      - store_artifacts:
          path: /home/circleci/project/android/app/build/outputs

  ## build_ios_prod: 省略 ##
  
  ## build_android_prod: 省略 ##
  
  workflows:
  version: 2
  build-test:
    jobs:
      - test:
          requires:
            - npm_dependencies
      - build_ios_stg:
          requires:
            - test
          filters:
            branches:
              only:
                - /feature.*/
                - master
      - build_ios_prod:
          filters:
            tags:
              only: /.*/
            branches:
              ignore: /.*/
      - build_android_stg:
          requires:
            - test
          filters:
            branches:
              only:
                - /feature.*/
                - master
      - build_android_prod:
          filters:
            tags:
              only: /.*/
            branches:
              ignore: /.*/

version: 2.1

orbs:
  node: circleci/[email protected]
  android: circleci/[email protected]
version을 2.1로 설정하여 orbs를 사용합니다.
gradle도circleaci/android를 사용하면default install가 완성됩니다.
commands:
  setup_to_build_ios:
    description: "iosビルドに必要な共通セットアップ処理"
    steps:
      - run:
          name: jsbundle init
          command: yarn ios-bundle # npx react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ios/main.jsbundle --assets-dest ios
      ## install cocoapods ##
      - restore_cache:
          keys:
            - cocoapods-v1-{{ checksum "ios/Podfile.lock" }}
            - cocoapods-v1
      - run:
          name: pod install
          command: cd ios && bundle exec pod install
      - save_cache:
          key: cocoapods-v1-{{ checksum "ios/Podfile.lock" }}
          paths:
            - ios/Pods
최종적으로 applstore에 업로드하여ios가 구축한 유니버설 설정 처리로 편집합니다.
리액트네이티브로 ios를 구축하기 위해서는 npx react-native bundle해야 한다.
안드로이드 쪽도 마찬가지다.
job:
  build_ios_stg:
    executor: ios-build-machine
    working_directory: /Users/distiller/project
    steps:
      - checkout
      - setup_env_to_stg
      - setup_to_build
      - run:
          name: set env to staging
          command: yarn switch-config:stg
      - setup_to_build_ios
      ## build ##
      - run:
          name: fastlane ios for staging
          command: cd ios && bundle exec fastlane build_for_deploygate
      - run:
          name: zip
          command: cd ios/fastlane/build && zip -r ipa.zip adhoc
          when: always
      - store_artifacts:
          path: /Users/distiller/project/ios/fastlane/build
ios 디렉터리bundle exec fastlane build_for_deploygate에서 하면 이전에 표시된fastlane/Fastlane를 볼 수 있습니다.
또 아이파 파일을 ARTIFACTS에 토해냄으로써 디플로이 게이트 계정을 초대하지 않더라도 베트남 이안국으로부터 디플로이 게이트가 공유하는 앱을 간단하게 받을 수 있다.(이것이 가장 환영받는 것이다.)
job:
  (省略)
  build_android_stg:
    executor:
      name: android/android-machine
      resource-class: medium
    environment:
      JVM_OPTS: -Xmx3200m
      GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-Xmx3200m -XX:+HeapDumpOnOutOfMemoryError"'
    working_directory: ~/project
    steps:
      (省略)
android 측에서 주의해야 할 것은 JVM_OPTSGRADLE_OPTS의 지정이다.
메모리를 확보하지 않으면 정상적으로 광택을 낼 수 있다.일단 위처럼 설정하면 돼요.
https://circleci.com/blog/how-to-handle-java-oom-errors/
  workflows:
  version: 2
  build-test:
    jobs:
      - test:
          requires:
            - npm_dependencies
      - build_ios_stg:
          requires:
            - test
          filters:
            branches:
              only:
                - /feature.*/
                - master
      - build_ios_prod:
          filters:
            tags:
              only: /.*/
            branches:
              ignore: /.*/
      - build_android_stg:
          requires:
            - test
          filters:
            branches:
              only:
                - /feature.*/
                - master
      - build_android_prod:
          filters:
            tags:
              only: /.*/
            branches:
              ignore: /.*/

작업 절차로서test가 성공하는 전제에서 동작을 구축한다.이렇게 되면 병렬 빌딩이 이동하기 때문에 상당한 시간이 줄어들 것이다.
또한feature 지점, 마스터 지점에 제출된 경우만buildios_stg 및buildandroid_stg 이동.
그리고tapush가 한 일 때문에 이번에 생략한buildios_prood 및buildandroid_프로드를 움직일 수 있게 하다.

총결산


병렬 구축이 가능하고 환경 구축 등 번거로운 일을 줄여 업무 효율을 높이는 데 도움이 된다.
(코드가 공유되면 긴 부분은 아코디언으로 접는데 궁금하시면 접어주세요.)

최후


그나저나 백엔드 엔지니어라서 리액트내티브에 별로 접촉하지 않은 상태에서 개발했습니다.
다행히도 항공사에서는 나처럼 프리랜서 엔지니어도 새로운 경험이 많다.
여러분 관심 있으면 꼭 가세요! ->사이트 축소판 그림

좋은 웹페이지 즐겨찾기