Github Actions로 CI/CD를 구현하여 앱 빌드 & TestFlight 업로드 자동화를 만들어야 하는 일이 생겨
아무것도 모르는 상태에서 1주일 동안 삽질해가며 마침내 구현하였고 모르고 시작하는 분들의 어려움을 조금이나마 덜고자 진행 내용에 대한 회고를 공유합니다.
우선 정말 진행의 90%를 보고 따라 했던 좋은 포스팅이 있어 소개합니다.
해당 포스터를 보면서 1편(빌드)과 2편(자동화)을 보면서 정말 많이 이해하고 도움을 받아서 너무 감사합니다 🙏🏻
소개해준 글을 보면서 진행하면 되고 진행하면서 새로 알게 된 내용과 막힌 부분들 위주로 공유하겠습니다. 그럼 시작하겠습니다!
최종 결과물: main branch에 push 하면 build 테스트와 TestFlight에 등록되는 것
삽질 최종 결과물 yml 파일 (위치는 .github/workflows/build_on_main.yml)
실행 job 이 두 개입니다 (1.build, 2.deploy(testflight)) 필요하신 부분만 구성하여 진행하여도 상관없음
# workflow 의 이름
name: Run Build
on:
# main 브랜치에 push 이벤트가 일어났을때 해당 workflow 를 trigger
push:
branches: [ main ]
# workflow의 실행은 하나 이상의 job으로 구성 됨
jobs:
# 이 workflow 는 "build", "deploy" 라는 job 으로 구성
build:
# job이 실행될 환경 - 최신 mac os
runs-on: macos-latest
# Step은 job의 일부로 실행될 일련의 task들을 나타냄
steps:
# uses 키워드를 통해 Github Actions에서 기본으로 제공하는 액션을 사용 가능. 아래 액션은 repository 에 체크아웃하는 것
- uses: actions/checkout@v2
# npm install
- name: Run yarn install 🛠
run: yarn install
# shell 이용해서 하나의 command 수행
- name: Start xcode build 🛠
run: |
cd ios && pod install --repo-update --clean-install && cd ..
xcodebuild -workspace ios/SampleApp.xcworkspace -scheme SampleApp -destination 'platform=iOS Simulator,name=iPhone 11 Pro,OS=latest';
deploy:
# job이 실행될 환경 - 최신 mac os
runs-on: macos-latest
env:
# app archive 및 export 에 쓰일 환경 변수 설정
XC_WORKSPACE: ${{ 'ios/SampleApp.xcworkspace' }}
XC_SCHEME: ${{ 'SampleApp' }}
XC_ARCHIVE: ${{ 'SampleApp.xcarchive' }}
# certificate
ENCRYPTED_CERT_FILE_PATH: ${{ '.github/secrets/certification.p12.gpg' }}
DECRYPTED_CERT_FILE_PATH: ${{ '.github/secrets/certification.p12' }}
CERT_ENCRYPTION_KEY: ${{ secrets.CERTS_ENCRYPTION_PWD }} # gpg로 파일 암호화할 때 사용한 암호
# provisioning
ENCRYPTED_PROVISION_FILE_PATH: ${{ '.github/secrets/SampleApp_GithubActions.mobileprovision.gpg' }}
DECRYPTED_PROVISION_FILE_PATH: ${{ '.github/secrets/SampleApp_GithubActions.mobileprovision' }}
PROVISIONING_ENCRYPTION_KEY: ${{ secrets.PROVISION_ENCRYPTION_PWD }} # gpg로 파일 암호화할 때 사용한 암호
# certification export key
CERT_EXPORT_KEY: ${{ secrets.CERT_EXPORT_PWD }}
# 키체인을 새로 생성하고 초기화 하는데 사용 됩니다. 즉 임시 키 체인을 만들때 필요
KEYCHAIN: ${{ 'test.keychain' }}
# Step은 job의 일부로 실행될 일련의 task들을 나타냄
steps:
# 단계별 task 를 나타낼 이름
- name: Select latest Xcode
# shell 이용해서 하나의 command 수행
run: "sudo xcode-select -s /Applications/Xcode.app"
- name: Checkout project
# uses 키워드를 통해 Github Actions에서 기본으로 제공하는 액션을 사용 가능. 아래 액션은 repository 에 체크아웃하는 것
uses: actions/checkout@v2
- name: Configure Keychain
# 키체인 초기화 - 임시 키체인 생성
run: |
security create-keychain -p "" "$KEYCHAIN"
security list-keychains -s "$KEYCHAIN"
security default-keychain -s "$KEYCHAIN"
security unlock-keychain -p "" "$KEYCHAIN"
security set-keychain-settings
- name : Configure Code Signing
run: |
# certificate 복호화
gpg -d -o "$DECRYPTED_CERT_FILE_PATH" --pinentry-mode=loopback --passphrase "$CERT_ENCRYPTION_KEY" "$ENCRYPTED_CERT_FILE_PATH"
# provisioning 복호화
gpg -d -o "$DECRYPTED_PROVISION_FILE_PATH" --pinentry-mode=loopback --passphrase "$PROVISIONING_ENCRYPTION_KEY" "$ENCRYPTED_PROVISION_FILE_PATH"
# security를 사용하여 인증서와 개인 키를 새로 만든 키 체인으로 가져옴
security import "$DECRYPTED_CERT_FILE_PATH" -k "$KEYCHAIN" -P "$CERT_EXPORT_KEY" -A
security set-key-partition-list -S apple-tool:,apple: -s -k "" "$KEYCHAIN"
# Xcode에서 찾을 수 있는 프로비저닝 프로필 설치하기 위해 우선 프로비저닝 디렉토리를 생성
mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
# 디버깅 용 echo 명령어
echo `ls .github/secrets/*.mobileprovision`
# 모든 프로비저닝 프로파일을 rename 하고 위에서 만든 디렉토리로 복사하는 과정
for PROVISION in `ls .github/secrets/*.mobileprovision`
do
UUID=`/usr/libexec/PlistBuddy -c 'Print :UUID' /dev/stdin <<< $(security cms -D -i ./$PROVISION)`
cp "./$PROVISION" "$HOME/Library/MobileDevice/Provisioning Profiles/$UUID.mobileprovision"
done
- name: Run yarn install 🛠
run: yarn install
- name: Archive app 📁
# 빌드 및 아카이브
run: |
cd ios && pod install --repo-update --clean-install && cd ..
xcodebuild clean archive -workspace $XC_WORKSPACE -scheme $XC_SCHEME -configuration release -archivePath $XC_ARCHIVE
# export 를 통해 ipa 파일 만듦
- name: Export app
run: |
xcodebuild -exportArchive -archivePath $XC_ARCHIVE -exportOptionsPlist ExportOptions.plist -exportPath . -allowProvisioningUpdates
- name: Install private API key P8 🔑 && Upload app to TestFlight 🆙
# key 파일 생성 & TestFlight 업로드
env:
PRIVATE_API_KEY: ${{ secrets.APPSTORE_API_PRIVATE_KEY }}
API_KEY: ${{ secrets.APPSTORE_API_KEY_ID }}
ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }}
run: |
mkdir -p ./private_keys
echo "$PRIVATE_API_KEY" > ./private_keys/AuthKey_$API_KEY.p8
ls -al
xcrun altool --output-format xml --upload-app -f SampleApp.ipa --type ios --apiKey $API_KEY --apiIssuer $ISSUER_ID
우선 소개해준 포스팅에 나와있는 yml 파일을 복붙 하여 놓고 나에게 맞는 부분만 수정하여 진행하였다.
진행하면서 막힌 부분
첫 번째
1. Job - Build (Failed)
1 -1 경로 문제
나의 경우 해당 repo를 가상 환경으로 checkout 한 뒤 build 명령을 실행할 때 workspace 경로(ios/SampleApp.xcworkspace)를 알기까지 삽질하였다.. 예제는 ProjectName/ProjectName.xcworkspace로 되어 있어 SampleApp/SampleApp.xcworkspace 이런 식으로 적었더니 계속 경로를 못 찾아서 삽질하였다. 현재 경로 기준에서(Project)에서 SampleApp.xcworkspace 경로를 적어주며 해결하였다. 나의 경우 ios/SampleApp.xcworkspace
xcodebuild -workspace ios/SampleApp.xcworkspace -scheme SampleApp -destination 'platform=iOS Simulator,name=iPhone 11 Pro,OS=latest';
1-2 Pod install failed
나의 경우 ReactNative로 진행하였는데 pod install 시 계속 실패 에러가 났다.
cd ios && pod install --repo-update --clean-install && cd ..
xcodebuild -workspace ios/SampleApp.xcworkspace -scheme SampleApp -destination 'platform=iOS Simulator,name=iPhone 11 Pro,OS=latest';
실패 에러
[!] Invalid `Podfile` file: cannot load such file -- /Users/runner/work/SampleApp/SampleApp/node_modules/react-native/scripts/react_native_pods.
이유는 node_modules에 있는 의존성이 필요한 상황이었는데 의존성을 설치하지 않고 pod install을 해서 에러가 났다. 그래서 나의 경우는 yarn 명령을 통해 의존성을 install 하고 pod install && build 명령을 실행하였다.
# uses 키워드를 통해 Github Actions에서 기본으로 제공하는 액션을 사용 가능. 아래 액션은 repository 에 체크아웃하는 것
- uses: actions/checkout@v2
# npm install
- name: Run yarn install 🛠
run: yarn install
# shell 이용해서 하나의 command 수행
- name: Start xcode build 🛠
run: |
cd ios && pod install --repo-update --clean-install && cd ..
xcodebuild -workspace ios/SampleApp.xcworkspace -scheme SampleApp -destination 'platform=iOS Simulator,name=iPhone 11 Pro,OS=latest';
pod install 하고 다시 cd .. 해서 나간 이유는 두 번째 명령인 build에 workspace 경로를 ios/SampleApp.xcworkspace로 잡아 놓았기 때문이다.
이렇게 하면 나의 경우 첫 번째 build Job을 성공적으로 마칠 수 있었다.
진행하면서 막힌 부분
두 번째
2. Job - Deploy (Failed)
두 번째 Job은 가상 환경에서 TestFlight에 등록하려면 인증에 필요한 작업과 환경변수 설정만 잘하면 순조롭게 진행이 되는데 한 가지 해당 포스팅에 나와있지 않은 부분과 환경변수 설정을 잘못해서 많은 시간을 삽질하였다...
2-1 권한 에러
나의 경우 해당 에러 원인을 찾기까지 3일 가까이 쓴 것 같다..
문제는 App store private api key를 secrets에서 설정할 때 value를 잘못 넣은 문제였다. value를 넣을 때 다운로드한 그대로
점선으로 구분되는 BEGIN, END 라인까지 전부 복사해서 넣어야 정상적으로 인식하는데 나의 경우 가운데에 있는 api key 만 복사해서 넣었더니 계속 에러가 났다... 멘붕..
-------BEGIN PRIVATE KEY ----------
api key...
------END PRIVATE KEY-----------
그리고 소개해준 포스팅에는 나와있지 않은 내용의 에러를 마주쳤는데 내용을 보면 해당 경로에서 인증키 파일을 찾을 수 없다는 에러이다.
NSLocalizedFailureReason=The file 'AuthKey_***.p8' could not be found in any of these locations: '~/work/SampleApp/SampleApp/private_keys', '~/private_keys', '~/.private_keys', '~/.appstoreconnect/private_keys'., NSLocalizedDescription=Failed to load AuthKey file.
https://github.com/Apple-Actions/upload-testflight-build/issues/27
찾아본 결과 ~/work/SampleApp/SampleApp/private_keys', '~/private_keys', '~/.private_keys', '~/.appstoreconnect/private_keys' 해당 경로를 순서대로 찾아가며 AuthKey 파일을 찾는다고 한다. 그래서 나의 경우는 현재 경로에서 private_keys 폴더를 만들고 그 안에 echo 명령으로 secrets 에 등록했던 api private key 를 파일로 만들어서 넣었다.
- name: Install private API key P8 🔑 && Upload app to TestFlight 🆙
# key 파일 생성 & TestFlight 업로드
env:
PRIVATE_API_KEY: ${{ secrets.APPSTORE_API_PRIVATE_KEY }}
API_KEY: ${{ secrets.APPSTORE_API_KEY_ID }}
ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }}
run: |
mkdir -p ./private_keys
echo "$PRIVATE_API_KEY" > ./private_keys/AuthKey_$API_KEY.p8
ls -al
xcrun altool --output-format xml --upload-app -f SampleApp.ipa --type ios --apiKey $API_KEY --apiIssuer $ISSUER_ID
여기서 현재 경로는 Root Project 의 경로이다. (~/work/SampleApp/SampleApp) 결과물의 경로는 (~/work/SampleApp/SampleApp/private_keys/AuthKey_***.p8) 이 만들어 진다 여기서 새로 알게 된 명령 ex) echo Hello > test.txt 하면 해당 내용이 파일로 만들어 진다는 것.
나의 경우는 소개해준 포스팅에서 사용한 apple-actions/upload-testflight-build@v1 요것을 사용하지 않고 수동으로 작성하였다어차피 같은 명령을 실행하게 되긴함
name: Upload app to TestFlight
uses: apple-actions/upload-testflight-build@v1
with:
app-path: 'HappyChuseok.ipa'
issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }}
api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }}
api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }}
파란 부분의 command가 apple-actions/upload-testflight-build@v1 사용했을 때 자동으로 실행되는 부분이다.
(아래 코드 처럼 수동으로 TestFlight 업로드 명령 실행함)
run: |
mkdir -p ./private_keys
echo "$PRIVATE_API_KEY" > ./private_keys/AuthKey_$API_KEY.p8
ls -al
xcrun altool --output-format xml --upload-app -f SampleApp.ipa --type ios --apiKey $API_KEY --apiIssuer $ISSUER_ID
여기서 ipa 파일 경로는 위에서 실행한 내용 export 에 따라 현재 경로(~/work/SampleApp/SampleApp/SampleApp.ipa)에 있어서 SampleApp.ipa 를 적었음
나의 경우 이렇게 하면 권한문제를 해결하고 다음으로 넘어갈 수 있었다. 이렇게 하면 이제 완료인가 싶었지만..몇 번의 간단한 오류들이 더 나고 성공하였다..!
자잘한 오류들..
- AppIcon 을 만들지 않고 TestFlight에 등록 하려고 한 문제.(AppIcon 등록하여 해결)
- 카메라 권한 요청 시 사용 설명이 없어 에러 (설명 추가 해결)
- 오류 없이 success 가 됐다는걸 확인하고 TestFlight를 확인했지만 없는 경우 (이 경우 메일로 문제가 된 부분이 메일로 날라옴 해당 부분 수정 후 해결)
등 자잘한 이슈들이 많았다. 이 부분에서 더 자세히 알고 싶고 궁금하다면 댓글 남겨주세요
감사합니다.
'설치' 카테고리의 다른 글
Redmine bitnami ubuntu 18.04에 설치하기! (0) | 2020.12.21 |
---|