Tuist에서 CI/CD 구축하기

annapo
14 min readFeb 18, 2023

--

Tuist + Fastlane (feat. Github Action)

왜 시작했나요 ?

최근에 저는 회사에서 fastlane을 도입했어요.
하다보니 느낀점은 CI/CD는 선택이 아닌 필수이고, 잘 구축한다면 시간을 많이 아낄수 있다는 점이었어요.

그래서 Tuist에서도 fastlane을 도입해보고 싶었답니다.

모든 파일은 여기서 확인 가능합니다. (star 눌러주세요 😇)

fastlane

이 글에선, fastlane에 대한 자세한 설명을 다루진 않을거에요. fastlane이 처음이시라면 얼마전 제가 작성한 글인 두려움 없이 fastlane, CircleCI 도입하기를 읽고 오시는것을 추천드려요 😇

그래도 간략히 소개드리면, fastlane은 클라이언트의 인증서 관리, 스크린샷 생성, 테스트, 배포 등을 자동화시켜주는 도구입니다.

Tuist

이 글에선, Tuist에 대한 자세한 설명을 다루진 않을거에요. Tuist가 처음이시라면, 얼마전 올라온 if(kakao)의 영상을 보시는 것을 추천드려요 🤣

그래도 간략히 소개드리면, Tuist란 프로젝트 파일의 생성 및 관리할 수 있는 도구입니다.

두려움 없이 시작하기.

시작도 전에 겁먹을 필요가 전혀 없답니다. 두려움 없이 시작합시다.

tuist generate

tuist generate

우리는 프로젝트를 시작하기 위해 해당 명령어를 사용합니다.

명령을 실행해서 프로젝트를 열면, 믹서기 👿 마냥 Signing부분을 갈아치워서 저희를 힘들게 했죠.

사실 Tuist 입장에서 생각해보면 Signing 설정을 안해준 우리의 잘못이 아닐까요 ? (사실 제 잘못 ⭕️)

Tuist의 Signing 기능 사용하기

이 기능을 사용하게 되면 fastlane의 match, cert, sigh 등 코드 사이닝 관련을 명령을 해줄 필요가 없게 됩니다.

fastlane만을 사용한다면 fastlane으로 코드 사이닝을 해주어야겠지만, tuist를 같이 사용하는 상황이라면 Tuist Signing을 사용하는게 더 편해보였습니다.

왜냐면, tuist generate 만 해주면 알아서 코드 사이닝을 해주기 때문입니다.

.cer, .p12 파일 준비하기

애플 디벨로퍼 사이트에서 Certificates을 생성해주어야 합니다.

Certificates를 생성하는 방법은 공식 문서에 자세히 설명 되어있고, 구글링 하면 어렵지 않게 생성할 수 있어요 !

이런식으로 두개를 만들어줍니다. 하나는 개발용이고, 하나는 배포용입니다.

다운받았다면, 키체인 접근으로 cer 파일을 열어주세요.

그럼 이렇게 인증서 두개를 확인할 수 있고, 이를 우클릭하여 하나씩 .p12 파일로 내보내기를 해줄겁니다. 암호는 빈칸으로 해두고 확인 버튼을 눌러주세요.

이런식으로 준비해주시면 .cer파일과 .p12 파일은 준비 완료입니다. 이름이 동일해야합니다. A.cer, A.p12 어떤 이름인지는 중요하지 않은데, .cer파일과 .p12파일 한 쌍의 이름은 동일해야합니다. 😇

mobileprovision 파일 준비하기

애플 디벨로퍼 사이트에서 Profile을 생성해주어야 합니다. 프로필을 만드는 법은 어렵지 않아요. + 버튼을 눌러서 차근차근 만들어주고, 이름은 아무상관이 없습니다. 어차피 이름을 바꿔줘야 해요 😅

프로필도 개발용, 앱 스토어용으로 두개를 만들어줍니다.

Target.Configuration.mobileprovision

이름은 이렇게 변경해주고 다운받아줍니다.

.cer, .p12, mobileprovision 파일을 Tuist/Signing 폴더에 넣기

이런식으로 들어가면 끝입니다.

master.key 파일 생성

Tuist는 인증서들을 깃허브에 그대로 올리면 안되기 때문에 master.key를 생성해서 generate시에 암호화를 시켜줍니다.

openssl rand -base64 32

명령어를 사용하여 랜덤값을 얻어내고 이를 master.key파일에 넣어줍니다. 위치는 Tuist 폴더 바로 아래에 넣어줍니다. master.key의 값으로 암호화와 복호화를 하기 때문에 master.key의 값은 팀원들끼리 공유가 되어야하고 깃허브에는 올리면 안됩니다.

자 이제 끝났네요. tuist generate 명령어로 프로젝트를 생성해보세요 😇

😇 : 이렇게 인증 설정이 잘되었다면 성공이에요. 🎉 고생했어요. 하하.
👿 : 하지만 아직 한참 남았죠 ?

CI/CD 구축을 위한 fastlane의 Fastfile 설정

우리는 로컬에서 fastlane을 사용하는 것을 넘어서 CI/CD 서버에서 fastlane을 동작시킬 것이기 때문에 조금 더 설정이 필요합니다.

키체인을 생성하고 .cer, .p12, mobileprovision을 키체인에 집어넣기

로컬에서는 좀 전에 키체인 열기를 통해서 .cer파일을 열었기 때문에 키체인에 자연스럽게 들어갔을테지만, CI/CD 서버에서의 키체인에는 아무것도 없을거에요.

그래서 키체인을 생성해주고 인증파일들을 집어넣어 줄거에요. 🤣

create_keychain(
name: "#{KEYCHAIN_NAME}",
password: "#{KEYCHAIN_PASSWORD}",
default_keychain: true,
unlock: true,
timeout: 3600,
lock_when_sleeps: true
)

키체인을 생성하고 default_keychain 옵션을 켜줍니다. (참고 — create_keychain문서)

import_certificate(
certificate_path: "Tuist/Signing/release.cer",
keychain_name: "#{KEYCHAIN_NAME}",
keychain_password: "#{KEYCHAIN_PASSWORD}"
)

import_certificate(
certificate_path: "Tuist/Signing/release.p12",
keychain_name: "#{KEYCHAIN_NAME}",
keychain_password: "#{KEYCHAIN_PASSWORD}"
)

install_provisioning_profile(path: "Tuist/Signing/#{APP_NAME}.Release.mobileprovision")

.cer파일과, .p12 파일, mobileprovision 파일을 키체인에 저장해주어야 합니다. (참고 — import_certificate문서, install_provisioning_profile문서)

# ✅ 인증서를 키체인에 저장
desc "Save To Keychain"
lane :set_keychain do |options|
create_keychain(
name: "#{KEYCHAIN_NAME}",
password: "#{KEYCHAIN_PASSWORD}",
default_keychain: true,
unlock: true,
timeout: 3600,
lock_when_sleeps: true
)

import_certificate(
certificate_path: "Tuist/Signing/release.cer",
keychain_name: "#{KEYCHAIN_NAME}",
keychain_password: "#{KEYCHAIN_PASSWORD}"
)

import_certificate(
certificate_path: "Tuist/Signing/release.p12",
keychain_name: "#{KEYCHAIN_NAME}",
keychain_password: "#{KEYCHAIN_PASSWORD}"
)

install_provisioning_profile(path: "Tuist/Signing/#{APP_NAME}.Release.mobileprovision")
end

set_keychain lane을 추가했습니다. 로컬에서는 이 lane을 돌리지 마세요. 왜냐면 디폴트 키체인 옵션이 켜져있어서 귀찮아진답니다 !

# ✅ 테스트 플라이트 업로드
desc "Push to TestFlight"
lane :tf do |options|
# ✅ 앱스토어 커넥트 키 연결
app_store_connect_api_key(is_key_content_base64: true, in_house: false)

# ✅ 빌드 넘버 증가
increment_build_number({ build_number: latest_testflight_build_number() + 1 })

# ✅ 빌드
build_app(
workspace: "#{APP_NAME}.xcworkspace",
scheme: "#{SCHEME}",
export_method: "app-store"
)

# ✅ 테스트 플라이트 업로드
upload_to_testflight(skip_waiting_for_build_processing: true)
end

나머지 Fastfile의 lane은 정말 별게 없습니다. 왜냐면 tuist가 generate하면서 이미 라이브러리를 세팅해주고 코드 사이닝까지 해줄거기 때문이에요. 앱스토어 커넥트 키 연결이나 빌드 넘버 증가 등은 저의 블로그를 참고해주세요.

tuist clean
tuist fetch
tuist generate
fastlane tf

로컬에서는 이렇게 진행하면 됩니다. 우선 로컬에서 fastalne이 동작하는지부터 확인해보세요. 😇

깃허브 workflow 설정하기

우선 develop 브랜치에 푸시가 되면 동작하는 workflow를 만들어볼거에요.

Tuist과 fastlane을 Github Action에서 사용할 때 다음 세가지에 집중해야합니다.

  1. ignore된 xxconfig파일을 worker에 끌어오기
  2. tuist singing에서 사용한 master.key 생성하기
  3. keychain에 .p12, .cer, mobileprovision 저장하기

하나씩 차근차근 해결해보겠습니다.

ignore된 xcconfig파일을 worker에 끌어오기

저희는 여기 .xcconfig 파일을 두개 사용하고 있어요. 하지만, 깃에는 올라가지 않아서 이 파일들을 끌어와주어야 합니다. 그렇지 않으면 빌드 과정에서 터질거에요. 🌋

깃허브 private 레포를 하나 만들어주었습니다. 여기에 xcconfig 파일들을 추가해주세요. private 레포이기 때문에 이 레포에 접근할 토큰이 필요합니다.

내 계정(private 레포의 접근 권한을 가지고 있는 계정)의 settings -> Developer settings -> Personal access tokens 에서 Generate new token 을 클릭합니다.

이런식으로 repo에 접근 권한을 가진 토큰을 발급받고, 이를 원래 repo의 secrets에 추가해줍니다.

- name: Bring ignored files
uses: actions/checkout@v3
with:
repository: Nexters/JYP-iOS-ignored
path: JYP-iOS/JYP-iOS/Sources/Config
token: ${{ secrets.ACTION_TOKEN }}

workflow는 이런식으로 끌어올 레포명(repository)과 끌어올 위치(path)와 방금 얻은 private repo접근 토큰(token)을 설정해줍니다. (참고 — checkout@v3)

파일들을 끌어오면서 원래 있던 파일들이 무시되기 때문에 복사 위치인 Config 폴더를 비워주세요. 😇 : 1단계 끝이에요.

tuist singing에서 사용한 master.key 생성하기

- name: Setting Master Key
run: |
echo "$MASTER_KEY" > Tuist/master.key

미리 깃허브 secrets에 마스터 키 값을 넣어두고 이를 master.key파일에 넣어주는 명령어를 사용합니다. 😇 : 2단계는 쉬워요.

keychain에 .p12, .cer, mobileprovision 저장하기

깃허브에 올라간 .p12파일과 .cer 파일은 master.key로 암호화 되어있어서 .encrypted가 붙어있습니다. .encrypted를 떼주고 keychain에 저장할겁니다.

tuist signing decrypt

명령어를 사용하여 복호화를 해주고 나서 아까 Fastfile에 만들어둔 set_keychain lane을 동작시키면 worker의 keychain에 인증 파일들이 저장될 거에요.

- name: Tuist Signing Decrypt
run: tuist signing decrypt

- name: Set Keychain
run: fastlane set_keychain
env:
KEYCHAIN_NAME: ${{ secrets.KEYCHAIN_NAME }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}

이런식으로 decrypt 명령어를 하고 set_keychain lane을 실행되도록 workflow를 작성합니다.

😇 : 이제 진짜 끝났어요. 🎉 고생했어요. 하하.
👿 : 고생했어요. 하하. (happy ending . .)

하고싶은 말

사실, 블로그에서는 성공한거 1개를 정리해서 올리지만, 60번 동안은 빨간색 실패화면만 봤었네요. 🥺

저의 깃허브도 놀러오세요 😇

--

--