Xcode Cloud – 3. ci 스크립트 작성

들어가며…

1편에서는 Xcode Cloud 구성 방법, 2편에서는 조금 더 디테일한 구성 방법을 알아봤습니다. 이번 3편에서는 스크립트 작성 (ci_script) 에 대해 알아보겠습니다. 제가 담당하고 있는 프로젝트에서는 DangerSwiftlint 를 설정해놓고 있는데 이를 기준으로 설명합니다.

Danger? SwiftLint?

우선 예시로 설명할 두 툴에 대해서 간단히 설명하겠습니다.

Danger (https://danger.systems)는 2016년 Ruby 언어 베이스로 출시된 CI 툴입니다. 새로 작성된 코드(branch)에서 추가된 파일, 수정된 파일, 삭제된 파일 등의 정보를 추출하여 메시지, 경고, 오류로 등급을 나누어 리포트를 작성하게 해주는 툴입니다. 아래는 Danger 홈페이지에 있는 리포트 예시입니다.

Dangerfile 로 명세를 작성한 후 이 파일을 SCM에 등록해두고 빌드 전 혹은 후에 명령어로 수행하게 자동화하는 방법으로 사용하며, 저자에게 명세 기준으로 문제점을 쉽게 파악하고 수정할 수 있게 하는 목적의 툴입니다.

Example of Danger Report

SwiftLint (https://github.com/realm/SwiftLint) 는 2015년 Relam 사에서 발표하였으며 Swift 언어로 작성된 코드의 작성 규칙(convention)을 검사하고 원한다면 수정할 수 있도록 만든 툴입니다. iOS 개발자에게는 거의 필수적인 lint 툴입니다.

.swiftlint.yml 파일로 사용할 규칙을 지정할 수 있으며, 규칙은 https://realm.github.io/SwiftLint/rule-directory.html 에서 확인하실 수 있습니다.

SwiftLint 는 여러 가지 방법으로 설치할 수 있고 CI 시스템에 따라 플러그인도 다양하게 지원되고 있습니다.

당연하게도 Danger를 통해서도 SwiftLint 를 사용할 수 있으며, SwiftLint의 결과를 Danger 리포트에서 함께 볼 수 있습니다.

Danger-swift

관리 중인 프로젝트의 CI/CD 시스템을 Bitrise 에서 Xcode Cloud 로 옮기면서 알게된 DangerSwift 버전이 Danger-swift (https://github.com/danger/swift) 입니다. (사실 껍데기와 인터페이스만 Swift 고 Core는 javascript 군요)

처음 발표 때는 하나의 실험으로 끝나지 않을까란 의구심을 가지고 있었는데 한참 업데이트가 없다가 어느새 버전이 세 개나 올라가 있었고 Danger의 플러그인으로 별도로 관리되던 SwiftLint 플러그인이 통합되어 있었습니다.

설치하는 방법도 npm, brew 등 다른 CLI 툴과 비슷한데 Swift란 이름이 들어간만큼 SPM (Swift Package Manager) 를 통한 설치법도 제공하고 있습니다.

ci_scripts

2편에서 설명한대로 ci_scripts는 소스코드 가져온 후, 빌드 전, 빌드 후 이렇게 3 단계에 대해 작성할 수 있습니다.

danger-swift 를 설치하기 위해서는 환경부터 정비가 되어야겠죠? 소스코드 가져온 직후 환경을 설정해보겠습니다. 앞서 설명한대로 Xcode Cloud 환경에는 brew 가 기본적으로 설치가 되어 있습니다.

아래는 ci_post_clone.sh 예시입니다.

#!/bin/zsh

# Update brew and install SwiftLint and Danger-swift
brew update
brew install swiftlint
brew install danger/tap/danger-swift

각 패키지에 대한 의존성을 체크하고 필요한 추가 패키지를 추가하는 것을 로그를 통해 볼 수 있습니다.

Build log related to Brew

스크립트에는 필요한 툴이나 명령어를 추가로 입력하면 됩니다.

키 값 등 중요한 값은 절대 스크립트에 포함하지 않습니다.

Access Token 처럼 중요한 환경 값은 스크립트에 넣을 수는 있지만 스크립트 파일이 SCM 에 포함되기 때문에 자칫하면 이 환경 값이 외부로 유출될 수 있습니다. 이런 환경 값은 워크플로 환경 설정에서 등록하고 꼭 Secret 을 활성화하여 저장하도록 합니다.

Danger 에서 Github 에 접근하기 위해서는 DANGER_GITHUB_API_TOKEN 값이 필요합니다. 이는 Github 설정에서 발급할 수 있으며 빌드 전 등록하셔야 오류가 나지 않습니다.

위 내용을 기준으로 적절하게 ci_pre_xcodebuild.sh, ci_post_xcodebuild.sh 에 내용을 작성하고 이를 SCM 에 push 하면 Xcode Cloud 는 각 단계에서 이를 확인하고 해당 스크립트를 수행합니다.

Danger-swift 구성하기

로컬에 환경을 구축해보도록 하겠습니다. 위에서 언급한 차례대로 수행합니다.

brew update
brew install swiftlint
brew install danger/tap/danger-swift

설치가 끝났다면 Danger의 명세 파일(Dangerfile.swift)을 만들어야 합니다.

아래 명령어를 실행하면 Xcode 가 실행되며 프로젝트가 하나 열립니다.

cd {project_root_directory} // (i.e. cd ~/Source/ExampleApp)
danger-swift edit
Dangerswift.swift

main.swift 파일로 이동하여 명세를 작성합니다. 명세 작성법은 http://danger.systems/js/reference.html 에서 확인하실 수 있습니다.

명세를 작성 후 터미널로 돌아가면 아래와 같은 상태이며 리턴키를 입력하면 프로젝트 root 디렉토리에 Dangerfile.swift 파일이 생성되어 있는 것을 확인하실 수 있습니다.

에러가 발생한다면!

앞서 설명했다시피 현재 디렉토리 위치를 확인해보세요. 프로젝트 루트가 아니면 오류가 발생할 수 있습니다.

쉽게 확인을 위해서 아래처럼 명세를 작성한다고 가정하겠습니다.

아래 코드는 현재 브랜치에서 생성된 파일, 수정된 파일, 삭제된 파일, 커밋 수를 알려주는 코드입니다.

import Danger

func summarize(from git: Git) {
    let numberOfCreatedFiles = git.createdFiles.count
    let numberOfModifiedFiles = git.modifiedFiles.count
    let numberOfDeletedFiles = git.deletedFiles.count
    let numberOfCommits = git.commits.count

    message("Deleted \(numberOfDeletedFiles) files")
    message("Modified \(numberOfModifiedFiles) files")
    message("Created \(numberOfCreatedFiles) files")
    message("Commits \(numberOfCommits)")
    
    markdown("**Good job!** Summarized this PR above... 🥳")
}

let danger = Danger()
summarize(from: danger.git)

이제 마지막으로 명세가 잘 돌아가는지 확인이 필요하겠죠?

danger-swift local
Example of Dangerfile.swift results

CI 에 적용 후 github 에서는 아래처럼 나타납니다.

Danger result in Github

어렵지 않죠?

Danger-swift 에서 swiftlint 사용하기

현재 Danger-swift 에는 SwiftLint 가 내장되어 있습니다. 플러그인으로 유지되다가 danger-swift 로 넘어오면서 swiftlint 가 내장이 되었다고 합니다. 기존의 danger-swiftlint 플러그인 코드 저장소는 https://github.com/ashfurrow/danger-swiftlint 이며, README.md 파일에 해당 내용이 언급되어 있습니다.

위에서 작성한 명세에서 swiftlint 는 함수로 바로 사용할 수 있는데 아래처럼 사용하시면 됩니다.

func lint(from git: Git) {
    let createdFiles = git.createdFiles
    let modifiedFiles = git.modifiedFiles
    
    SwiftLint.lint(.files(createdFiles + modifiedFiles), inline: true)
}

let danger = Danger()

lint(from: danger.git)

ci_script 로 다시 돌아가서…

앞서 Xcode Cloud 에서 작성한 ci_post_clone.sh 파일 이후에 아래와 같이 추가하고 앞서 작성한 Danger 명세와 스크립트 파일들을 커밋 & 푸쉬하고 Xcode Cloud 에서 잘 돌아가는지 확인을 하겠습니다.

#!/bin/zsh

# Update brew and install SwiftLint and Danger-swift
brew update
brew install swiftlint
brew install danger/tap/danger-swift

# Execute danger-swift for ci
cd $CI_WORKSPACE
danger-swift ci

여기까지 이해하고 잘 따라오셨다면 잘 작동할 것입니다.

마치며..

3편에 거쳐서 글을 작성해봤는데 오랜만에 쓰는 글이고 틈틈히 작성하다보니 흐름이 매끄럽진 않네요.. 결론은 Xcode Cloud 사용하기는 참 쉽습니다. brew 를 기본으로 제공해주다보니 더 쉬운 것 같습니다. jenkins 도입하면서 쉘 스크립트 짜던 고생이 떠오르는군요…

현재 Xcode Cloud 는 베타입니다. 애플 서버들 중 인증관련 서버와 Xcode Cloud 관련 서버가 종종 문제가 발생하는 것으로 보이는데 이 때문에 베타를 길게 잡고 계속 정리하고 있는 것으로 보입니다. 다소 사이즈가 작은 프로젝트이고 코드 변경이 잦지 않다면 당장 사용해도 큰 무리는 없어보입니다만, 대형 프로젝트이거나 잦은 코드 변경이 있다면 아직은 조금 더 지켜보는게 좋을 듯 합니다.

1편에서 요금 플랜에 대해서 잠깐 언급했는데 기본으로 주어지는 25시간이 넘으면 어떻게될지 지켜보고 있는 상황입니다. 요금 플랜이 나오기 전에는 그냥 허용을 해줄지 아니면 리셋될 때까지 사용을 못할지가 관건이네요. 이 부분도 확인이 되면 별도로 포스팅하도록 하겠습니다.

끝.

9 Shares:
답글 남기기

이메일 주소는 공개되지 않습니다.

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.

You May Also Like