GitHub 액션을 사용하여 Match를 통해 iOS 빌드를 자동화하는 방법
Capacitor 앱의 CI/CD 설정은 복잡하고 시간이 많이 걸립니다. 알아야 할 내용은 다음과 같습니다.
새로운 빌드에 권장: Capgo 빌드를 사용
__CAPGO_KEEP_0__를 사용하는 것을 이제 추천합니다 Capgo Capgo CLI로 빌드하세요. 자연스러운 Capacitor 빌드용으로. 이 Fastlane Match 가이드는 기존 GitHub Actions pipeline을 유지 관리하는 팀을 위해 유지되지만, 새로운 iOS 빌드는 Fastlane, Match 저장소, Xcode 러너, 인증서 및 업로드 스크립트를 유지 관리하지 않도록 하기 위해 Capgo CLI를 사용하세요.
필수 조건
시작하기 전에 다음을 설정해야 합니다.
- 관리자 권한이 있는 GitHub 계정
- iOS 개발자 프로그램 회원 가입
- App Store Connect API에 대한 접근 권한
- GitHub Actions 워크플로에 대한 이해
- Fastlane 및 Match 구성에 대한 지식
- pipeline 유지 및 디버깅에 필요한 시간
- 많은 개발자 팀이 있는 경우, 그렇지 않다면 __CAPGO_KEEP_0__를 사용하는 것을 추천합니다. fastlane 인증 더 간단한 워크플로우를 위해
프로페셔널 CI/CD 설정을 Capgo으로
복잡성을 피하세요. Capgo __CAPGO_KEEP_0__은 CI/CD PIPELINE을 직접 선호하는 플랫폼에서 구성합니다.:
- 플랫폼 독립성: GitHub 액션, GitLab CI, 또는 다른 것과 함께 작동
- 무결합 통합: 플랫폼 switch가 필요하지 않습니다. 현재 프로세스와 함께 작동
- 맞춤형 구성: 프로젝트 요구 사항에 맞춘 설정
- 전문가 지침: 50+ 앱에 대해 CI/CD를 이미 설정했습니다
가격
- 한 번의 설정 비용: $2,600
- 운영 비용: ~$300/년
- 다른 사내 솔루션과 비교: $6,000/년
- 5년 동안 $26,100를 절약하세요
수동 설정 안내서
자신이 모든 것을 설정하고 싶다면, 다음이 필요합니다:
iOS에서 Fastlane과 GitHub 액션을 사용하여 Continuous Delivery
사전 요구 사항
이 튜토리얼을 계속하기 전에…
- 개발 환경에 Fastlane이 설치되어 있는지 확인하세요. 설치된 개발 머신에.
- iOS 개발자 프로그램 회원입니다.
- 읽고 싶은 마음이 생기셨나요…
- 많은 개발자 팀이 있습니다. 그렇다면 더 간단한 워크플로우를 위해 fastlane cert 를 사용하는 것을 추천합니다.
가격에 대한 중요한 정보

https://github.com/features/actions
서비스는 제한에 따라 선택한 기기에 따라 '무료'입니다.
우리는 macOS 기기를 사용할 것입니다. 스크린샷에서 가격과 제한을 볼 수 있습니다 (가격은 튜토리얼이 생성된 시점의 가격으로, 미래에 변경될 수 있습니다).
🔴 요구 사항과 가격에 대해 경고한 후, 만약에 좋다면, 계속하겠습니다.
📣 포스트에서 우리는 iTunes Connect에서 앱을 생성했다고 가정합니다. 우리는 Apple 생태계의 인증서를 가지고 있으며, Fastlane!가 모든 것을 복사할 것입니다.
들어가볼까요 🤿
포스트에서 따르면 될 단계
- App Store Connect API을 사용하여 Fastlane Match
- 요구 사항
- App Store Connect API 키를 생성하는 단계
- App Store Connect API 키를 사용하는 단계
- 빠른 Fastlane 파일 복사
- Fastlane match 구성
- Fastlane match 구성
1. App Store Connect API을 사용하는 Fastlane Match의 사용
2021년 2월 이후로 모든 사용자가 App Store Connect에 로그인하기 위해 2단계 인증 또는 2단계 인증이 필요합니다. Apple ID에 대한 추가 보안层는 계정에 대한 액세스를 제한하여 계정에 대한 액세스를 보장합니다.
From Apple Support
match를 사용하기 위해 시작하는 것은 기존 인증서를 취소해야 하는 것을 의미합니다. 그러나 걱정하지 마세요. 새로운 인증서를 직접 받을 것입니다.
요구 사항
App Store Connect API을 사용하기 위해 Fastlane이 필요한 것은 세 가지입니다. three 세
- 발행자 ID.
- 키 ID.
- 키 파일 또는 키 내용.
앱 스토어 연결 API 키 생성
키를 생성하려면 App Store Connect에서 Admin 권한이 있어야 합니다. 권한이 없다면 해당 사람에게 이 문서를 알려주고 다음 지침을 따르세요.
1 — 앱 스토어 연결에 로그인하세요. 앱 스토어 연결.
2 — 사용자 및 액세스 선택. 앱 스토어 연결 사용자 액세스.

앱 스토어 연결 __CAPGO_KEEP_0__ 통합

4 — API 키를 생성하거나 (+) 버튼을 클릭하세요.

5 — 키 이름을 입력하세요. 키 이름은 키 자체의 일부가 아니며, 사용자만 참조할 수 있습니다.

6 — 액세스에서 키의 역할을 선택하세요. 키의 역할은 팀 내 사용자에게 적용되는 역할과 동일합니다. 자세한 내용은 역할 권한을 참조하세요. 권장하는 방법은 앱 매니저.
7 — 생성을 클릭하세요.
API 키의 액세스는 특정 앱으로 제한할 수 없습니다.
새 키의 이름, 키 ID, 다운로드 링크 및 기타 정보가 페이지에 나타납니다.

Capgo에서 필요한 모든 정보를 여기서 얻을 수 있습니다.
<1> 문제 ID.
<2> 키 ID.
<3> "다운로드 API 키"를 클릭하여 API 개인 키를 다운로드하세요. 다운로드 링크는 개인 키가 다운로드되지 않은 경우에만 나타납니다. 애플은 개인 키의 복사본을 유지하지 않습니다. 따라서 개인 키를 한번만 다운로드할 수 있습니다.
🔴 Store your private key in a safe place. You should never share your keys, store keys in a code repository, or include keys in client-side code.
애플 스토어 연결 API 키 사용
API 키 파일 (다운로드한 p8 파일), 키 ID, 발급자 ID가 필요하여 JWT 토큰을 생성하기 위해 인증을 위해 필요합니다. 이 정보를 Fastlane에 입력하는 방법은 여러 가지가 있습니다. Fastlane의 새로운 액션을 사용하여 app_store_connect_api_key Capgo에서 다른 방법을 알아보세요. Fastlane 문서.이 방법을 보여주기 때문에 CI에서 가장 쉽게 작업할 수 있는 방법입니다.
App Store Connect API 키를 사용하여 Fastlane을 관리할 수 있습니다. 좋습니다!
2. Fastlane 파일 복사
빠른 라인은 안드로이드 개발을 자동화하기 위해 Ruby 라이브러리를 만든 것입니다. 빠른 라인을 사용하면, 사용자 정의 '레인'을 구성할 수 있습니다. 이 레인에는 '액션'이 포함되어 있습니다. 이 액션은 안드로이드 스튜디오에서 수행하는 일반적인 작업을 수행합니다. 빠른 라인은 많은 일을 할 수 있지만, 이 튜토리얼의 목적을 위해, 우리는 몇 가지 핵심 액션만 사용할 것입니다.
프로젝트의 루트 폴더에 빠른 라인 폴더를 만들고, 다음 파일을 복사하세요: 빠른 라인 파일
default_platform(:ios)
DEVELOPER_APP_IDENTIFIER = ENV["DEVELOPER_APP_IDENTIFIER"]
DEVELOPER_APP_ID = ENV["DEVELOPER_APP_ID"]
PROVISIONING_PROFILE_SPECIFIER = ENV["PROVISIONING_PROFILE_SPECIFIER"]
TEMP_KEYCHAIN_USER = ENV["TEMP_KEYCHAIN_USER"]
TEMP_KEYCHAIN_PASSWORD = ENV["TEMP_KEYCHAIN_PASSWORD"]
APPLE_ISSUER_ID = ENV["APPLE_ISSUER_ID"]
APPLE_KEY_ID = ENV["APPLE_KEY_ID"]
APPLE_KEY_CONTENT = ENV["APPLE_KEY_CONTENT"]
GIT_USERNAME = ENV["GIT_USERNAME"]
GIT_TOKEN = ENV["GIT_TOKEN"]
def delete_temp_keychain(name)
delete_keychain(
name: name
) if File.exist? File.expand_path("~/Library/Keychains/#{name}-db")
end
def create_temp_keychain(name, password)
create_keychain(
name: name,
password: password,
unlock: false,
timeout: 0
)
end
def ensure_temp_keychain(name, password)
delete_temp_keychain(name)
create_temp_keychain(name, password)
end
platform :ios do
lane :build do
build_app(
configuration: "Release",
workspace: "./ios/App/App.xcworkspace",
scheme: "App",
export_method: "app-store",
export_options: {
provisioningProfiles: {
DEVELOPER_APP_ID => "#{PROVISIONING_PROFILE_SPECIFIER}"
}
}
)
end
lane :refresh_profiles do
match(
type: "development",
force: true)
match(
type: "adhoc",
force: true)
end
desc "Register new device"
lane :register_new_device do |options|
device_name = prompt(text: "Enter the device name: ")
device_udid = prompt(text: "Enter the device UDID: ")
device_hash = {}
device_hash[device_name] = device_udid
register_devices(
devices: device_hash
)
refresh_profiles
end
lane :closed_beta do
keychain_name = TEMP_KEYCHAIN_USER
keychain_password = TEMP_KEYCHAIN_PASSWORD
ensure_temp_keychain(keychain_name, keychain_password)
api_key = app_store_connect_api_key(
key_id: APPLE_KEY_ID,
issuer_id: APPLE_ISSUER_ID,
key_content: APPLE_KEY_CONTENT,
duration: 1200,
in_house: false
)
match(
type: 'appstore',
git_basic_authorization: Base64.strict_encode64("#{GIT_USERNAME}:#{GIT_TOKEN}"),
readonly: true,
keychain_name: keychain_name,
keychain_password: keychain_password,
api_key: api_key
)
gym(
configuration: "Release",
workspace: "./ios/App/App.xcworkspace",
scheme: "App",
export_method: "app-store",
export_options: {
provisioningProfiles: {
DEVELOPER_APP_ID => "#{PROVISIONING_PROFILE_SPECIFIER}"
}
}
)
pilot(
apple_id: "#{DEVELOPER_APP_ID}",
app_identifier: "#{DEVELOPER_APP_IDENTIFIER}",
skip_waiting_for_build_processing: true,
skip_submission: true,
distribute_external: false,
notify_external_testers: false,
ipa: "./App.ipa"
)
delete_temp_keychain(keychain_name)
end
lane :submit_review do
version = ''
Dir.chdir("..") do
file = File.read("package.json")
data = JSON.parse(file)
version = data["version"]
end
deliver(
app_version: version,
submit_for_review: true,
automatic_release: true,
force: true, # Skip HTMl report verification
skip_metadata: false,
skip_screenshots: false,
skip_binary_upload: true
)
end
end
앱 파일
app_identifier(ENV["DEVELOPER_APP_IDENTIFIER"])
apple_id(ENV["FASTLANE_APPLE_ID"])
itc_team_id(ENV["APP_STORE_CONNECT_TEAM_ID"])
team_id(ENV["DEVELOPER_PORTAL_TEAM_ID"])
빠른 라인 설정
빠른 라인 match iOS 앱의 code 인증서에 대한 새로운 접근 방식입니다. 빠른 라인 match는 팀이 iOS 앱에 필요한 인증서와 배포 프로파일을 관리하기 쉽게 만듭니다.
새로운 개인 저장소 이름을 만들고 certificates, 예를 들어, GitHub 개인 계정 또는 조직에 등록하세요.
iOS 앱에 대한 빠른 라인 match를 초기화하세요.
fastlane match init
그런 다음 Git 저장소 옵션 #1을 선택하세요.
[01:00:00]: fastlane match supports multiple storage modes, please select the one you want to use:1. git2. google_cloud3. s3?
새로 생성된 저장소의 URL을 Assign하세요.
[01:00:00]: Please create a new, private git repository to store the certificates and profiles there[01:00:00]: URL of the Git Repo: <YOUR_CERTIFICATES_REPO_URL>
Fastlane 폴더 내에 파일이 생성되었습니다. 이름은 Matchfile입니다. Matchfile은 HTTPS 인증서 저장소의 URL로 설정되어야 합니다. SSH를 사용할 수도 있지만, 다른 단계를 실행해야 합니다. 다음으로 인증서를 생성하고 Fastlane Match를 사용하여 인증서 저장소에 인증합니다.
_git_url___CAPGO_KEEP_0__ 액션에서 인증서 저장소 암호화를 위해 사용할 패스프레이즈를 입력하라는 메시지가 나옵니다.
# ios/Matchfilegit_url("https://github.com/gitusername/certificates")storage_mode("git")type("appstore")
정상적으로 진행되면 다음과 같은 화면이 나타납니다.
GitHub과 관련된 문제가 발생하거나 권한이 부족한 경우, git에 인증 토큰을 생성하는 방법에 대한 포스트를 참고하세요.
fastlane match appstore
인증서와 프로비전 프로파일은 인증서 저장소에 업로드됩니다.
[01:40:52]: All required keys, certificates and provisioning profiles are installed 🙌
If you experienced any problem with GitHub and the necessary permissions, maybe this Generated certificates and provisioning profiles are uploaded to the certificates repository resources Now you have inside Fastlane folder a file named
Matchfile

마지막으로, project Xcode에서 열고, 앱의 릴리스 구성에 대한 배포 프로파일을 업데이트하세요.

몇 가지 주의 사항 💡
MATCH
CI/CD가 인증서 저장소에 접근할 수 있도록 하려면, 인증서 저장소에 접근하거나 개인 저장소 읽기 권한이 있는 개인 접근 토큰을 생성해야 합니다.
이미 GitHub에서 설정 → 개발자 설정 → 개인 접근 토큰 → 클릭 Generate New Token → 개인 저장소 읽기 권한을 체크 repo scope → 그 다음 클릭 Generate token.

개인 접근 토큰을 생성한 후 복사해 두세요. 나중에 환경 변수를 위해 사용하겠습니다. GIT_TOKEN.
Fastlane 폴더 내에서 생성된 match 파일을 대체하세요. Matchfile
CERTIFICATE_STORE_URL = ENV["CERTIFICATE_STORE_URL"]
GIT_USERNAME = ENV["GIT_USERNAME"]
GIT_TOKEN = ENV["GIT_TOKEN"]
FASTLANE_APPLE_ID = ENV["FASTLANE_APPLE_ID"]
git_url(CERTIFICATE_STORE_URL)
storage_mode("git")
type("appstore")
git_basic_authorization(Base64.strict_encode64("#{GIT_USERNAME}:#{GIT_TOKEN}"))
username(FASTLANE_APPLE_ID)
이것은 GitHub Actions에 의해 인증서 및 배포 프로파일을 가져오기 위해 사용됩니다. 그리고 GitHub Secrets에서 var가 설정되며, 파일에 하드 코딩된 대신 사용됩니다.
빌드 처리
GitHub Actions에서 당신은 CI/CD 워크플로우를 실행하는 데 사용한 분의 수에 따라 요금이 청구됩니다. 경험상, 앱 스토어 연결에서 빌드를 처리하는 데 10–15분이 걸립니다. 개인 프로젝트의 경우, 예상 비용은 1 건당 $0.08/분 x 15분 = $1.2
또는 더 많은 비용이 발생할 수 있습니다. 이는 프로젝트의 구성 또는 의존성에 따라 달라집니다. __CAPGO_KEEP_0____CAPGO_KEEP_1__
If you share the same concerns for the pricing as I do for private projects, you can keep the skip_waiting_for_build_processing to true.
What’s the catch? You have to manually update the compliance of your app in App Store Connect after the build has been processed, for you to distribute the build to your users.
This is just an optional parameter to update if you want to save on the build minutes for private projects. For free projects, this shouldn’t be a problem at all. See pricing.
3. Setup GitHub Actions
Configure GitHub secrets
Ever wonder where the values of the ENV are coming from? Well, it’s not a secret anymore – it’s from your project’s secret.

1. APP_STORE_CONNECT_TEAM_ID - 앱 스토어 연결 팀의 ID입니다. (여러 팀에 속해 있다면).
2. DEVELOPER_APP_ID - 앱 스토어 연결에서 앱 → 앱 정보 → 앱의 General Information 부분으로 스크롤 다운하고 Apple ID.
3. DEVELOPER_APP_IDENTIFIER - 앱의 번들 식별자입니다.
4. DEVELOPER_PORTAL_TEAM_ID - 개발자 포털 팀의 ID를 찾으세요. 여러 팀에 속해 있다면.
5. FASTLANE_APPLE_ID - 앱을 관리하는 데 사용하는 Apple ID 또는 개발자 이메일입니다.
6. GIT_USERNAME 및 GIT_TOKEN - Git 사용자 이름과 개인 액세스 토큰.
7. MATCH_PASSWORD - match를 초기화할 때 assign한 패스 프레이즈가 사용됩니다. 인증서와 배포 프로파일을 암호화할 때 사용됩니다.
8. PROVISIONING_PROFILE_SPECIFIER - match AppStore <YOUR_APP_BUNDLE_IDENTIFIER>예를 들어 match AppStore com.domain.blabla.demo.
9. TEMP_KEYCHAIN_USER 및 TEMP_KEYCHAIN_PASSWORD - 워크플로우에 임시 키체인 사용자와 비밀번호를 assign하세요.
10. APPLE_KEY_ID — App Store Connect API Key 🔺Key ID.
11. APPLE_ISSUER_ID — App Store Connect API Key 🔺Issuer ID.
12. APPLE_KEY_CONTENT — App Store Connect API Key 🔺 Key file or Key content of .p8, 체크 하세요
13. CERTIFICATE_STORE_URL — Match 키의 리포지토리 URL (예: https://github.com/***/fastlane_match.git)
4. GitHub 워크플로우 파일을 구성하십시오.
Create a GitHub workflow directory.
cd .github/workflows
디렉토리 내부에서 workflow 파일을 생성하고 다음을 추가하십시오. build-upload-ios.yml파일 이름
name: Build source code on ios
on:
push:
tags:
- '*'
jobs:
build_ios:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v6
- name: set Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: npm
- name: Install dependencies
id: install_code
run: npm ci
- name: Build
id: build_code
run: npm run build
- uses: actions/cache@v5
with:
path: ios/App/Pods
key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-
- name: Sync
id: sync_code
run: npx cap sync
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7.2
- uses: maierj/fastlane-action@v2.3.0
env:
DEVELOPER_APP_IDENTIFIER: ${{ secrets.DEVELOPER_APP_IDENTIFIER }}
DEVELOPER_APP_ID: ${{ secrets.DEVELOPER_APP_ID }}
PROVISIONING_PROFILE_SPECIFIER: match AppStore ${{ secrets.DEVELOPER_APP_IDENTIFIER }}
TEMP_KEYCHAIN_USER: ${{ secrets.TEMP_KEYCHAIN_USER }}
TEMP_KEYCHAIN_PASSWORD: ${{ secrets.TEMP_KEYCHAIN_PASSWORD }}
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
CERTIFICATE_STORE_URL: https://github.com/${{ secrets.CERTIFICATE_STORE_REPO }}.git
GIT_USERNAME: ${{ secrets.GIT_USERNAME }}
GIT_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
FASTLANE_APPLE_ID: ${{ secrets.FASTLANE_APPLE_ID }}
MATCH_USERNAME: ${{ secrets.FASTLANE_APPLE_ID }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
DEVELOPER_PORTAL_TEAM_ID: ${{ secrets.DEVELOPER_PORTAL_TEAM_ID }}
with:
lane: closed_beta
- name: Upload release bundle
uses: actions/upload-artifact@v2
with:
name: ios-release
path: ./App.ipa
retention-days: 60
이 워크플로우는 각 GitHub 후에 트리거되어야 합니다. 태그자동화 태그를 필요로 하시면, 참조하세요. 자동화 빌드 및 릴리즈와 GitHub 액션 첫째.
그런 다음 워크플로우는 NodeJS 의존성을 가져와, 설치하고 JavaScript 앱을 빌드합니다.
각각의 새로운 커밋을 전송할 때마다, 테스트 플라이트에서 릴리즈가 빌드됩니다.
앱은 Ionic을 사용하지 않아도 됩니다. Capacitor 베이스만 필수입니다. 오래된 Cordova 모듈을 포함할 수 있지만 Capacitor JS 플러그인은 선호됩니다.
5. 워크플로우 트리거
커밋 생성
커밋 커밋, __CAPGO_KEEP_0__을 확인할 수 있습니다.
워크플로우를 트리거하세요
새로운 커밋을 branch로 푸시하세요 main 또는 development 워크플로우를 트리거하기 위해

몇 분 후에 App Store Connect 대시보드에 빌드가 사용할 수 있습니다.

로컬 머신에서 배포할 수 있나요?
네, 가능합니다. 그리고 그것은 매우 쉬워요.
개인 저장소가 있다고 가정해 보세요. 그리고 무료 플랜의 분량을 모두 사용했으며 새로운 릴리즈에 대해 돈을 지불하지 않으려는 경우, 또는 애플리케이션을 수동으로 제출하는 것을 선호하는 경우.
그럼 가보죠
확인, 우선은 my_project_path/fastlane 경로에 .env Fastfile과 동일한 경로에 .env 로컬 머신에서 배포하기 위해 생성할 .env 파일의 비밀 속성을 _GitHub
a_s 아래에 나열된 것과 동일하게 생성할 수 있도록 하기 위해
.env 파일을 생성하세요. 그것을 시작하고 Fastlane 컴퓨터에서 다음 명령어를 실행하세요:
fastlane closed_beta
❌ Fastlane에 대한 중요한 정보 .env 파일을 노출시키지 않으려면, 우리는 .gitignore에 추가해야 합니다. , 다음과 같은 형식으로:❌
fastlane/*.env
It should work the same as it happens from GitHub Actions on the remote machine but in our local machine. 🍻

터미널 실행: $ Fastlane closed_beta
Fastlane과 GitHub Actions을 사용하여 iOS 앱에 대한 완전히 자동화된 프로세스를 구축했습니다. 축하합니다!
Google Play 콘솔에서 새로운 커밋을 보내면, 베타 채널에서 릴리즈가 자동으로 빌드됩니다. 이 블로그를 개선하기 위해, 피드백이나 질문, 제안이 있으시면 martin@capgo.app로 이메일을 보내주세요.
장치에서 빌드하기
장치에서 빌드해야 하는 경우, 프로비전닝에 수동으로 추가해야 합니다.
장치를 맥에 연결하고 장치 메뉴를 열어보세요.
그리고 identifier를 복사하세요.
그 다음에 다음 명령어를 실행하세요:
fastlane register_new_device
장치 이름과 identifier를 설정하라고 물어볼 것입니다.

문제가 발생하면
개발 장치 테스트 등 문제가 발생하면, 일반적으로 이 방법으로 해결할 수 있습니다.
어떤 마법의 명령어가 당신을 구할 수 있습니다:
fastlane match nuke development
fastlane match development
그 다음으로: 프로젝트를 정리하기 위해 Shift(⇧)+Command(⌘)+K를 눌러거나 Product > Clean(이름이 Clean Build Folder라고 표시될 수 있음)를 선택하십시오.
그 다음으로 다시 앱을 기기에서 실행하십시오.
감사합니다.
이 블로그는 다음 글을 기반으로 합니다: