총 4개의 스텝으로 진행됩니다.
1. Tuist Generate in Git Actions
2. Build Project by Using fastlane in Local
3. fastlane match
4. Tuist + fastlane in Git Actions
https://littlemoom.tistory.com/50
지난 편에 이어서 진행해보도록 하겠습니다.
프로젝트를 Git에 연동하시는 경우 키 값 및 이메일 등 중요 정보가 Public Repository에 노출되지 않도록 주의해주세요!
3. fastlane match
match - fastlane docs
type Define the profile type, can be appstore, adhoc, development, enterprise, developer_id, mac_installer_distribution, developer_id_installer development
docs.fastlane.tools
match은 개발 팀 전체에서 하나의 Code Signing ID를 공유하여 공동 서명 설정을 단순화하고 코드 서명 문제를 방지합니다.
필요한 Certificates 및 Provisioning Profiles을 생성하여 별도의 git 저장소에 저장합니다.
인증 파일이 생성된 git 저장소에 액세스할 수 있는 모든 팀 구성원은 Code Signing에 해당 자격 증명을 사용할 수 있습니다.
먼저 할 일은 App Store Connect API Key를 발급받는 것 입니다.
AppStoreConnect > 사용자 및 액세스 > 키 에서 발급받을 수 있습니다.
활성화된 키를 다운로드하고, Issuer ID와 키 ID를 기억해주세요.
다음 명령어로 match 설정을 초기화합니다.
$ fastlane match init
인증서 저장소를 선택합니다.
fastlane match supports multiple storage modes, please select the one you want to use:
1. git
2. google_cloud
3. s3
4. gitlab_secure_files
?
저는 git을 선택했습니다.
git에 인증서를 위한 private repository를 생성하고, 해당 repository 주소를 입력합니다.
Please create a new, private git repository to store the certificates and profiles there
URL of the Git Repo: https://github.com/{USER_NAME}/{REPO_NAME}.git
Successfully created './fastlane/Matchfile'. You can open the file using a code editor.
그럼 해당 Git URL이 포함된 Matchfile
이 생성됩니다.
Matchfile
을 다음과 같이 편집합니다.
git_url("https://github.com/newrun0307/PerfectDay-iOS-Certificate.git")
storage_mode("git")
type("appstore")
app_identifier("{BUNDLE_ID}")
username("{USER_NAME}") # Your Apple Developer Portal username
TestFlight에 본격적으로 업로드 할 예정이기 때문에 type
은 appstore로 설정합니다.
app_identifier
은 App Bundle ID를 username
은 Developer 이메일 계정을 입력합니다.
아래 명령어를 통해 git 저장소에 인증서를 생성합니다.
match 뒤의 옵션은 Matchfile
에서 설정한 type에 맞게 넣습니다.
$ fastlane match appstore
...
+-------------------------------------------------------------------+
| Detected Values from './fastlane/Matchfile' |
+----------------+--------------------------------------------------+
| git_url | https://github.com/*******/******************** |
| | *********.git |
| storage_mode | git |
| type | appstore |
| app_identifier | com.***********.******* |
| username | ********@*******.*** |
+----------------+--------------------------------------------------+
...
중간에 설정하게 되는 Passpharse는 저장소 접근을 위한 패스워드이므로 반드시 기억해두어야합니다.
Passphrase for Match storage: **************
개발자 계정 (이중)인증 절차가 발생할 수 있습니다!
과정이 완료되면, 아래와 같이 git repository에 인증서 파일이 생성됩니다.
이제 Fastfile
을 수정하여 TestFlight에 업로드까지 진행해보겠습니다.
lane :{LANE_NAME} do
app_store_connect_api_key(
key_id: "{KEY_ID}",
issuer_id: "{ISSUER_ID}",
key_filepath: "{P8_KEY_FILE_PATH}"
)
match(type: "appstore")
latest_build_number=latest_testflight_build_number(version:"{APP_VERSION}")
increment_build_number(
build_number: latest_build_number + 1,
xcodeproj: "{PATH}/{PROJECT_NAME}.xcodeproj"
)
build_app(
workspace: "{WORKSPACE_NAME}.xcworkspace",
scheme: "{SCHEME_NAME}"
clean: true,
export_method: "app-store",
include_bitcode: false,
)
upload_to_testflight(
username: "{USER_EMAIL}",
app_identifier: "{APP_BUNDLE_ID}"
)
지난 포스팅에서 작성한 Fastfile
에서 export_method
를 app-store로 변경하고,
빌드 번호 증가와 TestFlight 업로드 커맨드를 추가했습니다.
인증서 저장소 접근을 위해 match 비밀번호를 입력하게 됩니다.
개발자 계정 (이중)인증 절차가 발생할 수 있습니다!
프로세스가 완료되면 다음과 같은 성공 로그와 summary가 표시됩니다.
Successfully finished processing the build 6.1.7 - 16 for IOS
...
Successfully distributed build to Internal testers 🚀
+-----------------------------------------------------+
| fastlane summary |
+------+--------------------------------+-------------+
| Step | Action | Time (in s) |
+------+--------------------------------+-------------+
| 1 | default_platform | 0 |
| 2 | xcode_select | 0 |
| 3 | app_store_connect_api_key | 0 |
| 4 | match | 73 |
| 5 | latest_testflight_build_number | 2 |
| 6 | increment_build_number | 0 |
| 7 | build_app | 93 |
| 8 | upload_to_testflight | 337 |
+------+--------------------------------+-------------+
4. Tuist + fastlane in Git Actions
* Private Repository의 GitActions 무료 가동 시간은 월 2,000분(=33시간 30분)입니다.
* Public Repository에서만 무료이니 참고하시기 바랍니다.
이제 지금까지의 모든 과정을 합쳐 Git Actions에서 작동하도록 해봅시다!
이번 단계에서 추가로 필요한 설정은 Git SSH Key 설정과 Secret Value 설정과 입니다.
현재 Git Private Repository에 인증서가 존재하기 때문에
Git workflow 환경에서 해당 Repository에 접근하기위한 SSH Key가 필요합니다.
또한 여러가지 Key들이 외부에 노출되면 보안상에 문제가 발생할 수 있기 때문에 Secret Value를 설정하여 키 값을 숨깁니다.
4-1. Git SSH Key 설정
ssh-keygen
이라는 도구를 사용하여 key를 생성합니다.
workflow에서 사용하기 위한 SSH Key와 known hosts 값이 필요합니다.
$ ssh-keyscan github.com | pbcopy -
pbcopy
는 커맨드의 output을 클립보드에 저장하는 커맨드입니다.
명령어가 실행 후 메모장 등에 붙여놓고 보관합니다.
이후 known hosts 값으로 사용됩니다.
SSH Key를 생성해봅시다.
$ ssh-keygen
ssh key의 이름을 붙여줍니다.
$ ssh-keygen -p -m PEM -f {KEY_FILE_NAME}
# -p: changing the passphrase
# -m: format
# -f: keyfile
ssh key를 PEM 형식으로 재설정하면서 비밀번호를 재설정합니다.
~/.ssh
에서 생성된 ssh 키를 확인할 수 있습니다.
이제 인증서가 있는 Git Private Repository로 갑니다.
Settings > Deploy keys > Add deploy key를 통해 Key를 추가합니다.
이때 Key값은 방금 생성된 pub 파일의 값을 넣어줍니다.
# in ~/.ssh
$ pbcopy < {KEY_FILE_NAME}.pub
# or
$ echo {KEY_FILE_NAME}.pub
# 내용 직접 복사
4-2. Secret Value 설정
이제 Xcode프로젝트 Git Repository로 가봅시다.
Settings > Security > Secrets and variables > Actions 에서 Repository secrets에 아래 값들을 추가합니다.
SSH_KEY: ssh key 값입니다.
$ pbcopy < {KEY_FILE_NAME}
혹은
$ echo {KEY_FILE_NAME}
명령으로 값을 복사해서 붙여넣습니다.
KNOWN_HOSTS: Key 생성단계에서 만든 known host값입니다.
MATCH_PASSWORD: Match 설정시 사용했던 패스워드입니다.
FASTLANE_USER: Apple 개발자 계정 이메일입니다.
FASTLANE_PASSWORD: Apple 개발자 계정 패스워드입니다.
APP_STORE_CONNECT_API_KEY_ID: AppStoreConnect API Key ID입니다.
APP_STORE_CONNECT_API_ISSUER_ID: AppStoreConnect API Issuer ID입니다.
APP_STORE_CONNECT_API_KEY_CONTENT: AppStoreConnect API p8 file content 입니다.
$ pbcopy < {P8_KEY_FILE_NAME}
혹은
$ echo {P8_KEY_FILE_NAME}
명령으로 값을 복사해서 붙여넣습니다.
KEYCHAIN_NAME: match에 사용될 keychain 이름입니다. 임의로 설정합니다.
KEYCHAIN_PASSWORD: match에 사용될 keychain 패스워드입니다. 임의로 설정합니다.
이제는 인증서 저장소에 SSH 접속을 하기 때문에 Matchfile
의 git_url
값을 수정해야합니다.
# git_url("https://github.com/{USER_NAME}/{REPO_NAME}.git")
git_url("git@github.com:{USER_NAME}/{REPO_NAME}.git")
마지막으로 workflow 파일과 Fastfile
을 수정합니다.
# workflow.yml
...(생략)
jobs:
build:
name: CI/CD by using Tuist & fastlane
runs-on: macos-latest
steps:
# 1. Git Repository Checkout
- name: Checkout Repository
uses: actions/checkout@v3
# 2. 프로젝트 build를 수행할 Xcode의 버전 설정
- name: Select Xcode Version
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
# 3. Install fastlane
- name: Install fastlane
run: brew install fastlane
- name: fastlane Version Check
run: fastlane --version
# 4. Install Tuist
- name: Install Tuist
run: curl -Ls https://install.tuist.io | bash
- name: Tuist Version Check
run: tuist version
# 5. Fetch Tuist & Generate
- name: Fetch Tuist
run: tuist fetch
- name: Generate Tuist
run: tuist generate -n
# 6. Pod install
- name: Cocoapod install
run: pod install
# 7. 인증서 정보를 가져올 Private Repository 접근을 위한 SSH 설정
- name: Install SSH key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.SSH_KEY }}
known_hosts: ${{ secrets.KNOWN_HOSTS }}
# 8. fastlane 실행
- name: Run Fastlane
run: fastlane {LANE_NAME}
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }}
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY_CONTENT: ${{ secrets.APP_STORE_CONNECT_API_KEY_CONTENT }}
KEYCHAIN_NAME: ${{ secrets.KEYCHAIN_NAME }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
# Fastfile
...(생략)
lane :{LANE_NAME} do
create_keychain(
name: ENV["KEYCHAIN_NAME"],
password: ENV["KEYCHAIN_PASSWORD"],
timeout: 1800,
default_keychain: true,
unlock: true,
lock_when_sleeps: false
)
app_store_connect_api_key(
key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"],
issuer_id: ENV["APP_STORE_CONNECT_API_ISSUER_ID"],
key_content: ENV["APP_STORE_CONNECT_API_KEY_CONTENT"]
)
match(
type: "appstore",
readonly: is_ci,
keychain_name: ENV["KEYCHAIN_NAME"],
keychain_password: ENV["KEYCHAIN_PASSWORD"]
)
latest_build_number=latest_testflight_build_number(
app_identifier: "{APP_BUNDLE_ID}"
username: "{USER_NAME}"
)
increment_build_number(
build_number: latest_build_number + 1,
xcodeproj: "{PROJECT_FILE_PATH}"
)
build_app(
workspace: "{WORKSPACE_NAME}.xcworkspace",
scheme: "{SCHEME_NAME}",
clean: true,
export_method: "app-store",
output_directory: "./build",
include_bitcode: false,
)
upload_to_testflight(
username: "{USER_NAME}",
app_identifier: "{APP_BUNDLE_ID}",
)
마무리
이제 개발 후 배포 브랜치에 액션이 발생하면, 자동으로 Test 빌드가 TestFlight에 업로드 됩니다.
이전에 직접 Archive부터 업로드를 했던 과정이 사라졌으며,
다른 사람과의 협업 시에도 저장소를 통해 인증서에 접근이 가능한 환경이 되었습니다.
또한 Xcode13 이상부터 사용할 수 있는 Xcode Cloud라는 CD 도구도 있으니 사용을 고려해보는 것도 좋을 거 같습니다.
'iOS > iOS' 카테고리의 다른 글
[iOS] Target & Scheme (2) | 2024.09.30 |
---|---|
[iOS] Xcode 15 Rosetta (0) | 2024.01.30 |
[iOS] CI/CD 환경 구축하기 - 1. Tuist in Git Actions & fastlane (0) | 2024.01.09 |
[iOS] Tuist - 4. Project 생성 (0) | 2023.02.11 |
[iOS] Tuist - 3. Target 생성 (0) | 2023.02.03 |