Leo
[iOS] github actions 배포 자동화 본문
지난 5월, fastlane을 적용하고 팀원분과 각자 로컬에서만 사용을 해왔는데, 어느 순간 git commit 후 fastlane 명령어를 입력해 빌드 배포하는 과정이 불편하게 느껴졌다. 이 반쪽짜리 CI/CD를 완성시키고자 github action을 도입했고, 정말 많은 삽질 끝에 성공적으로 마무리 할 수 있었다. 그 과정을 간략히 적어 보겠다.
현재 적용된 flow와 동일한 이미지가 있어서 가져와봤다.
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]+'
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 부분을 따라하면 쉽게 설정 할 수 있다. (아래 참고)
// 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 |