애플사이다의 iOS 개발 일지

[iOS] GitHub Action으로 Xcode 테스트 자동화하기 - CI/CD 본문

비전공자용 노력/개발 툴

[iOS] GitHub Action으로 Xcode 테스트 자동화하기 - CI/CD

Applecider 2022. 8. 29. 07:00

개발자가 설정한 특정 상황이 발생하면 자동으로 실행할 동작을 미리 정해두는 기능이 있다.

예를 들어 특정 branch에 merge 또는 PR을 요청했을 때 테스트가 실행되도록 하는 것이다.

Git Flow 기능과 함께 사용하면 더욱 유용하다.

*Git Flow 설명은 지난 포스트 참고 - [Git] GitFlow로 브랜치 관리하기

 

이렇게 테스트 코드를 실행하거나, 새로운 버전을 release 하기 전에 TestFlight를 실행할 수 있는데

이러한 자동화 기능을 CI/CD라고 한다.

 

CI/CD를 위한 툴은 Jenkins, fastlane, Buildkite 등 다양한데,

오늘은 GitHub Action을 알아보자.

 


CI/CD란?

  • CI : Continuous Integration (지속적 통합)
  • CD : Continuous Deployment (지속적 배포) 또는 지속적인 서비스 제공(Continuous Delivery)

CI빌드/테스트 자동화 과정이다.

코드 변경사항에 대해 정기적인 테스트가 실행되므로 코드 충돌을 방지할 수 있다.

 

CD배포 자동화 과정이다.

GitHub Action이란?

Git에 코드를 올리는 것만으로 빌드/테스트, 배포를 자동으로 실행하는 기능이다.

GitHub의 Understanding GitHub Actions 페이지에 친절히 설명되어 있다.

The components of GitHub Actions

  1. Events : 특정 상황이 발생한다면 (ex. 특정 branch에 Push 했을 때)
  2. WorkFlows : 이 작업들을 실행시키겠다.
  3. Jobs : WorkFlows에 명시된 1개의 작업이다.  (ex. Unit Test를 실행)
    - 여러 개의 Jobs는 병렬적 (동시 다발적)으로 실행된다.
    - Job 내부에서 순차적으로 실행할 내용을 Step으로 구분한다.
  4. Actions : 자주 사용하는 명령을 Action에 정의해뒀다가 Step에서 사용할 수 있다. (재사용성 높음)
  5. Runners : Job을 실행하는 주체이다. (VM 개념)
    - 각각의 Job은 독립적인 Runner를 가진다.

*전반적인 설명은 GitHub의 소개 영상을 참고
*Event, Workflows, Jobs, Actions, Runners에 대한 자세한 설명은 GitHub 링크를 참고 

GitHub Action 사용하기 - 테스트 코드 자동 실행

이제 yml 파일을 추가해서 CI를 구현해보자.

 

먼저 GitHub의 Actions 탭에서 Swift 항목의 Actions를 생성한다. 아래 캡쳐 참고

(Swift Package를 빌드/테스트해야 하기 때문)

이제 아래처럼 yml 파일에 원하는 자동화 조건을 작성할 수 있다.

*우측의 MarketPlace에 xcodebuild 등 명령어를 검색하면 자세한 사용 방법이 나온다.

 

yml에 작성한 코드를 살펴보자.

yml 예시

// Workflow 이름
name: Run Test

// ✅ 트리거 조건 (push 하거나 PR 하면 하단의 jobs를 실행하겠다는 뜻)
on:
  push:
    branches: [ develop ]  // develop branch로 push한 상황
  pull_request:
    branches: [ develop ]  // develop branch로 PR 생성한 상황

// ✅ 트리거 발생 시 실행할 작업들 
jobs:
  // ✅ 이번 예시에서는 "build"라는 1개 작업만 진행
  build:  

    runs-on: macos-latest  // iOS 플랫폼에서 실행한다는 뜻

    // ✅ job 내부에서 순차적으로 실행할 내용
    steps:
    - uses: actions/checkout@v3  // repository에 체크아웃해서 job이 접근 가능하게 한다는 뜻 (Github Actions에서 미리 정의한 명령)
    - name: Build Xcode  // 실행할 작업 이름
      run: |  // 여러가지 명령어를 사용할 때 "|" 입력
        xcodebuild clean test -project WhatWeEat.xcodeproj \
        -scheme WhatWeEat \
        -destination 'platform=iOS Simulator,name=iPhone 13,OS=latest'
        // Xcode로 특정 환경에서 clean 및 test를 진행하겠다는 뜻
        // ❗-project WhatWeEat/WhatWeEat.xcodeproj를 쓰면 경로를 못찾는 문제 발생 가능
        // ❗-destination '...,OS=14.1'를 쓰면 OS 버전이 안맞는 문제 발생 가능
  1. on에서 트리거 조건을 설정하고,
  2. jobs에서 트리거 발생 시 실행할 작업들을 설정하고,
  3. steps에서 job에서 실행할 일을 설정한다.

이 예시에서는 develop branch로 merge하거나 PR을 생성하는 시점에 테스트 코드를 실행하도록 자동화했다.
(이외에도 새로운 Issue가 생성됐을 때, 특정 Label이 추가됐을 때 등의 시점으로도 설정 가능하다.)

 

❗로 표시한 부분에 주의하자..

(에러를 해결한 삽질 과정은 아래에 기록했다.)

uses 키워드

steps의 uses 키워드를 통해

GitHub Action에서 미리 정의한 명령을 가져와서 편리하게 사용할 수 있다.

 

위 코드의 actions/checkout@v3 명령은 뭘 의미할까?

"repository에 체크아웃해서 job이 접근 가능하게 한다"는 뜻이다.

*체크아웃 개념이 궁금하다면 이 링크를 참고

run 키워드

코드 하단의 run 키워드에는 이런 내용이 들어있다.

  • -project 플래그 : test 할 프로젝트이다.
  • -scheme 플래그 : 빌드할 scheme 이다.
  • -destination 플래그 : 빌드 시 사용할 플랫폼, 기기, iOS 버전이다.

CocoaPods을 사용했다면

만약 SPM 대신 CocoaPods을 사용했다면 에러가 발생한다.

pod install, project 대신 workspace로 실행해야 하기 때문이다.

 

따라서 run 부분을 아래 코드로 바꿔야 한다.

// CocoaPods 사용 시
run: |
  pod install --repo-update --clean-install --project-directory=HappyChuseok/
  xcodebuild clean test -workspace HappyChuseok/HappyChuseok.xcworkspace -scheme HappyChuseok -destination 'platform=iOS Simulator,name=iPhone 11 Pro,OS=latest'
        
// 참고 - SPM 사용 시
run: | 
  xcodebuild clean test -project WhatWeEat/WhatWeEat.xcodeproj -scheme WhatWeEat -destination 'platform=iOS Simulator,name=iPhone 13 Pro,OS=14.1'

이외에도 원하는 swift-version을 명시하는 것도 가능하다.

*GitHub의 Building and testing Swift 페이지를 참고

Workflow 작동 확인하기

아래처럼 yml 파일을 추가하면서 commit하면 된다.

Actions 탭에 들어갔는데 아무 일도 일어나지 않았다.

아래처럼 Run Test라는 WorkFlow는 추가됐지만, 아직 작동 (Run)되지 않은 것을 볼 수 있다.

원인은 트리거 조건이 발생하지 않았기 때문이다.

(develop branch에 Push/PR하는 게 트리거 조건이었는데, 위에서 master branch에 커밋했음)

그리고 생각해보니 Unit Test를 추가하지 않았다;

 

다시 Unit Test를 추가하고,

새로운 branch를 생성한 뒤 develop branch로 PR을 생성했더니 아래처럼 실패 메시지가 생겼다.

PR 생성 후 하단에 뜬 메시지 (실패)

아래처럼 Actions 탭에서 상세내용을 확인 가능하다.

에러 메시지에 "xcodebuild: error: 'WhatWeEat/WhatWeEat.xcodeproj' does not exist." 내용이 있다.

*참고 - 아래처럼 현재 Unit Test 파일이 들어있는 디렉토리는 분리되어 있다.

검색해보니 디렉토리를 못찾을 수 있어서 working-directory를 추가하라는 내용이 있었다.

(검색 키워드 : GitHub Action, ios, exit code 66)

Process completed with exit code 66 during setup of Github Actions iOS project

working-directory에 WhatWeEatTests 디렉토리를 넣어봤지만 그래도 안됨..

 

그래서 경로 설정에 문제가 있다고 판단하여

-project WhatWeEat/WhatWeEat.xcodeproj -> WhatWeEat.xcodeproj로 바꿔봤는데 exit code 66 문제가 해결됐다!!!

근데 다른 에러가 생김.. 

157번 줄에 "xcodebuild: error: Unable to find a destination matching the provided destination specifier:"라는 내용이 있다.

162번 줄 아래로 available destination을 보여주는데 전부 OS: 15.2로 되어있다.

이유는 모르겠지만 최신 버전의 OS를 써야하는듯..?

그래서 -destination 'platform=iOS Simulator,name=iPhone 13 Pro,OS=14.1' -> OS=latest로 변경했다.

 

근데 그래도 실패

169번 줄에 "Reason: Cannot test target “WhatWeEatTests” on “iPhone 13 Pro”: 

iPhone 13 Pro’s iOS Simulator 15.2 doesn’t match WhatWeEatTests’s iOS Simulator 15.5 deployment target." 라는 내용이 있다.

 

WhatWeEat의 deployment target은 14.0으로 설정했고,

엥.. WhatWeEatTests의 deployment target은 따로 정한 적이 없는데..?

근데 WhatWeEatTests Target의 Build Settings를 확인해보니.. 15.5로 설정되어 있었다;; 에러 메시지가 맞았다.

Tests Target의 iOS deployment target 설정을 꼭 확인하자

그래서 WhatWeEatTests의 iOS deployment target을 14.0 (WhatWeEat Target과 동일하게)로 변경하고 재시도했다.

 

이번에는 성공!!!

테스트에 15분이나 걸렸다.

(중간중간 codesign 내용도 있던데.. 트리거 조건에 master branch가 있어서 그런가?)

PR 생성 후 하단에 뜬 메시지 (성공!!)

README에 Badge 추가하기

아래처럼 Workflow의 성공 여부를 나타내는 Badge를 만들 수 있다. (맨 오른쪽)

Badge 에시

Actions 탭에서 Workflow를 선택한 뒤

우상단의 ... 버튼을 탭하고, Create status badge를 탭한다.

그럼 Markdown 소스코드가 나오는데, 그걸 README에 복붙하면 된다.

Badge 생성하는 방법

Badge를 만드는 코드를 아래에 남겨본다.

![iOS 14.0](https://img.shields.io/badge/iOS-14.0-lightgrey?style=flat&color=181717)
[![Swift 5.6](https://img.shields.io/badge/Swift-5.6-F05138.svg?style=flat&color=F05138)](https://swift.org/download/) 
[![Xcode 13.4](https://img.shields.io/badge/Xcode-13.4-147EFB.svg?style=flat&color=147EFB)](https://apps.apple.com/kr/app/xcode/id497799835?mt=12) 
[![Run Test](https://github.com/just1103/WhatWeEat/actions/workflows/test_on_develop.yml/badge.svg)](https://github.com/just1103/WhatWeEat/actions/workflows/test_on_develop.yml)

 

- Reference

 

🍎 포스트가 도움이 되었다면, 공감🤍 / 구독🍹 / 공유🔗 / 댓글✏️ 로 응원해주세요. 감사합니다.

Comments