Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
관리 메뉴

Leo

[iOS] github actions 배포 자동화 본문

개발 정리/CI·CD

[iOS] github actions 배포 자동화

dohyxx 2025. 1. 13. 14:34

지난 5월, fastlane을 적용하고 팀원분과 각자 로컬에서만 사용을 해왔는데, 어느 순간 git commit 후 fastlane 명령어를 입력해 빌드 배포하는 과정이 불편하게 느껴졌다. 이 반쪽짜리 CI/CD를 완성시키고자 github action을 도입했고, 정말 많은 삽질 끝에 성공적으로 마무리 할 수 있었다. 그 과정을 간략히 적어 보겠다.

 

현재 적용된 flow와 동일한 이미지가 있어서 가져와봤다.

출처: https://blog.mathpresso.com

 

Workflow

1. 브랜치에 관계없이 tag 가 생성된 commit 이 push 되면 자동으로 workflow 실행

백엔드 CI/CD 전략과 동일하게 프론트도 tag를 이용해 workflow가 실행될 수 있도록 설정했다.

# 정규식 패턴을 사용하여 dev/x.x.x 형식의 태그만 허용
on:
  push:
    tags:
      - 'dev/[0-9]+.[0-9]+.[0-9]+'
      - 'qa/[0-9]+.[0-9]+.[0-9]+'
      - 'live/[0-9]+.[0-9]+.[0-9]+'

build를 위한 tag commit

 

2. git 코드 체크아웃

github actions 가상환경에 프로젝트 repo를 설정

// Git Repository Checkout
- uses: actions/checkout@v4

 

3. ignored files 생성

github actions이 가상환경에서 실행된다는 걸 생각해야 한다. Github Actions는 우리가 repo에 올린 파일만 알고 있을 뿐, 다른 설정 관련 파일들의 존재를 모른다. 그렇기 때문에 관련된 파일들을 적절하게 secret value에 반영해야 한다. (.env, GoogleService-Info 등)

 

json 형식의 파일은 base64 encoding 후 secret value에 반영한다.

#encode
cat  vuka-jobs-app/vuka_jobs/ios/GoogleService-Info.plist | base64

 

// Setting file
- name: create setting file
  run: |
   mkdir -p env
   echo "${{ secrets.GOOGLE_SERVICE_INFO }}" | base64 --decode > $(dirname $GITHUB_WORKSPACE)/GoogleService-Info.plist
   echo "${{ secrets.ENV_DEV_CONTENT }}" > env/dev.jobs.app.env
   echo "${{ secrets.ENV_LOCAL_CONTENT }}" > env/local.jobs.app.env

 

4. Project Clean

Actions 실행 과정에서 pod 빌드 오류가 발생해서  ios/flutter 설정 파일들을 삭제 후 재빌드 시켰다.

# 3. project clean
- name: Project Clean
  run: |
   flutter clean
   rm -rf pubspec.lock
   rm -rf flutter-plugins
   rm -rf flutter-plugins-dependencies
   rm -rf ios/.symlinks  
   rm -rf ios/Pods ios/Podfile.lock
   rm -rf ios/Pods
   flutter pub get
   cd ios && pod install

 

4. SSH key 설정 (Match 레포지토리와의 연결)

Github Action이 private repo에 접근하기 위해서는 SSH-Key 공개키를 배포키로 등록해야한다.

Github Secret 변수는 Public Key가 아닌 Private Key를 입력한다.

- uses: shimataro/ssh-key-action@v2
  with:
   key: ${{ secrets.SSH_KEY }}
   known_hosts: ${{ secrets.KNOWN_HOSTS }}
   if_key_exists: replace
   config: |
	Host github.com
	HostName github.com
	User git
	IdentityFile ~/.ssh/id_rsa

 

5. fastlane lane 명령어 실행

# 5. [iOS] fastlane build
	name: Deploy TestFlight
	run: fastlane beta
	working-directory: ios
	env:
          FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
          FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }}
          APP_IDENTIFIER: ${{ secrets.APP_IDENTIFIER }}
          FASTLANE_TEAM_ID: ${{ secrets.TEAM_ID }}
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
          APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }}
          APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
          APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }}
          FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ secrets.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}
          KEYCHAIN_NAME: ${{ secrets.KEYCHAIN_NAME }}
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
          GIT_URL: ${{ secrets.MATCH_GIT_URL }}

 

 

6. 슬랙 알림 전송 (success/fail)

App build는 store 배포 또는 QA 테스트 전달을 위해서 가장 많이 사용된다.

빌드가 성공적으로 끝나면 개발팀 슬랙을 통해 noti가 오고, QA 채널에는 수동으로 알림이 가도록 설정해두었다. (Test Build 시에도 모두에게 슬랙 알림이 가는 것을 방지하고자 했다.)

 

7. github action 실행 

- 사내 repo는 github-hosted를 사용하는 것 보단 self-host를 사용하는 게 보안에 좋을 것 같아서, 현재 self-hosted runner를 사용하고 있다.

- self-hosted 설정 및 사용 방법은 download 부분을 따라하면 쉽게 설정 할 수 있다. (아래 참고)

self-hosted-runner 설정

// actions-runner 경로에서 실행 
./run.sh 

// runner 종료 
kill -SIGTERM -- -$(pgrep run.sh)

 

 

 

전체 Workflow 

    deploy_ios:
      runs-on: self-hosted
      steps:
        # 1. Git Repository Checkout
        - uses: actions/checkout@v4

        # 2. Setting file
        - name: create setting file
          run: |
            mkdir -p env
            echo "${{ secrets.GOOGLE_SERVICE_INFO }}" | base64 --decode > $(dirname $GITHUB_WORKSPACE)/GoogleService-Info.plist
            echo "${{ secrets.ENV_DEV_CONTENT }}" > env/dev.jobs.app.env
            echo "${{ secrets.ENV_LOCAL_CONTENT }}" > env/local.jobs.app.env

        # 3. project clean
        - name: Project Clean
          run: |
            flutter clean
            rm -rf pubspec.lock
            rm -rf flutter-plugins
            rm -rf flutter-plugins-dependencies
            rm -rf ios/.symlinks
            rm -rf ios/Pods ios/Podfile.lock
            rm -rf ios/Pods
            flutter pub get
            cd ios && pod install

        # 4. match ssh-key 설정
        - uses: shimataro/ssh-key-action@v2
          with:
            key: ${{ secrets.SSH_KEY }}
            known_hosts: ${{ secrets.KNOWN_HOSTS }}
            if_key_exists: replace
            config: |
                Host github.com
                HostName github.com
                User git
                IdentityFile ~/.ssh/id_rsa

        # 5. [iOS] fastlane build
        - name: Deploy TestFlight
          run: fastlane beta
          working-directory: ios
          env:
            FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
            FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }}
            APP_IDENTIFIER: ${{ secrets.APP_IDENTIFIER }}
            FASTLANE_TEAM_ID: ${{ secrets.TEAM_ID }}
            MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
            APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }}
            APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
            APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }}
            FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ secrets.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}
            KEYCHAIN_NAME: ${{ secrets.KEYCHAIN_NAME }}
            KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
            SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
            GIT_URL: ${{ secrets.MATCH_GIT_URL }}

        # 6. Notification Slack (TestFlight)
        - name: Notification Slack
          uses: 8398a7/action-slack@v3
          with:
            status: custom
            fields: repo,author,message
            custom_payload: |
              {
                attachments: [{
                  color: '${{ job.status }}' === 'success' ? 'good' : '${{ job.status }}' === 'failure' ? 'danger' : 'warning',
                  text: '${{ job.status }}' === 'success' ? '✅ Success Upload' : '${{ job.status }}' === 'failure' ? '⛔️ Failed Build' : '⚠️ Canceled Build',
                  fields: [{
                    title: 'repo',
                    value: `${process.env.AS_REPO}`,
                    short: true
                  },
                  {
                    title: 'author',
                    value: `${process.env.AS_AUTHOR}`,
                    short: true
                  },
                  {
                    title: 'message',
                    value: `${process.env.AS_MESSAGE}`,
                    short: true
                  }],
                }]
              }
          env:
            SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_TESTFLIGHT }}
          if: always()

 

'개발 정리 > CI·CD' 카테고리의 다른 글

[Android] github actions 배포 자동화  (0) 2025.01.13
fastlane match  (0) 2025.01.06