-
iOS 프로젝트에 Github Action으로 CI 구성하기카테고리 없음 2022. 12. 5. 03:00
현재 부스트캠프에서 진행하고 있는 프로젝트 burstcamp를 개발하며 작성하는 글입니다.
간단히 저희 프로젝트를 개발하는 방식을 소개해드리겠습니다.
일단, git flow 전략을 적용하여 개발하고 있습니다.
각자의 local에서 새로운 브랜치를 생성해 개발한 뒤, 완료되면 remote의 develop 브랜치에 pr을 보내는 방식입니다.
또한, build를 할 때 swiftlint를 이용해서 스타일을 검사한 뒤, 몇몇 항목에 대해서는 autocorrection을 적용해주고 있습니다.
pr이 생성되면 나머지 팀원 3명 중 2명 이상의 approve를 받아야 merge가 가능합니다.
이렇게 pr의 리뷰가 끝나면 merge를 하고, 각자 develop 브랜치를 pull 받아 개발하게 되는데, 여기에서 가끔 문제가 발생합니다.
문제 상황
1. 누군가 빌드가 실패하는 코드를 올린다.
2. 누군가 swiftlint가 되지 않거나, autocorrection이 적용되지 않은 코드를 올린다.
두 상황 모두 빠르게 수정을 하고, 다시 pr을 올려 force merge 하거나, 다시 팀원들의 승인을 받아야 합니다.
이는 불필요하게 꽤나 많은 시간을 소모하게 됩니다.
당연히 모두가 빌드가 되는지 확인하고, lint에 걸리는 코드를 수정하고 올리면 되지만, 실수는 어쩔 수 없는 법입니다.
이를 방지하기 위해서 생각해 낸 것이, "pr을 생성할 때마다 build와 lint를 검사해주자"입니다.
바로 github action을 만들어봅니다.
Github Action 시작하기
첫 목표는 github action에서 현재 개발환경인 xcode 14.0, 최소타겟인 iOS 14.0에서 빌드하자.
여러 가지 글을 보며 시도해보다가 macOS버전에 따라 설치된 xcode와 iOS 버전이 다르다는 것을 알아냈습니다.
이제부터 시작입니다. (말투가 바뀝니다)
github action에서 지원하는 OS목록에서 macOS버전을 확인한다.
각각의 macOS에서 어떤 버전들을 지원하는지 알아보자
각각 왼쪽부터 macOS 10.15, macOS 11, macOS 12
여기에서 문제를 맞닥뜨린다.
최소 타겟의 빌드가 되는지 확인하기 위해서 iOS 14.0를 따라서 xcode 12.0.1 & macOS 10.15를 선택해야 하는지,
개발환경에서 빌드가 되는지 확인하기 위해서 xcode 14.0을 따라서 iOS 16.0 & macOS 12를 선택해야 하는지.일단은 최소 타겟에서 빌드가 되는지 확인하기 위해서 iOS 14.0을 따라가기로 했다.
여러 번 시도를 해봤지만 계속해서 실패했다. 원인은 macOS버전에 있었다.
바로 iOS 14.0을 빌드하기 위해 선택해야 하는 macOS 10.15 버전의 지원이 12월 1일부터 끊겼다는 것이었다.
어쩔 수 없이 가장 가까운 버전인 iOS 14.4를 따라 빌드를 시도해봤다.
정말 여러 가지 오류가 발생했고, 대부분은 해결을 했지만 다음 오류는 해결하지 못했다.
Resolve Package Graph
xcodebuild: error: Could not resolve package dependencies:
Package.resolved file is corrupted or malformed; fix or delete the file to continue느낌상 현재 프로젝트는 xcode 14.0에서 작성된 것이고, 이전 버전과 호환 가능한 옵션을 켜놓지 않았다.
뇌피셜이지만 우리 프로젝트는 xcode 14.0에서만 빌드가 되니, 테스트도 xcode 14.0에서 해야 하겠구나! 하는 판단을 하게 됐다.
이제 우리도 CI 한다!
판단을 따라 xcode 14.0에 맞춰 iOS 16.0 & macOS 12를 선택했다.
yml(야믈이지만 야물이라고 부르는 게 좋다) 파일은 아래 링크를 참고해서 만들었다.
My Swift Package Manager Release Workflow
SPM Cache
이제 빌드는 잘 됐지만, SPM Cache가 동작하지 않았다.
SPM cache not working on github actions, any ideas?
위 답변을 보니 SPM dependency 파일들은
githubAction/cache
의 공식문서에 구현된 것과 다르게 .build폴더에 있지 않고, DerivedData폴더에 저장되기 때문이라고 한다.따라서 경로를 .build -> DerivedData로 수정해주었다.
완성된 야물 파일은 build.yml에서 볼 수 있다.
Secret File 추가
프로젝트에는 Google Firebase를 지원하기 위한 plist파일과, 여러 API KEY들을 저장해놓은 plist파일들을 포함되어있었다.
하지만 보안을 위해 git에는 올리지 않은 상태였기 때문에, github action에서 빌드를 하게 되면 해당 파일들을 포함하지 않아 오류가 발생한다.
이를 위해 github에서는 secret이라는 기능을 제공한다. 아래 공식 문서에서 자세한 내용을 볼 수 있다.
https://docs.github.com/en/actions/security-guides/encrypted-secrets
기본적으로는 key를 직접 string값으로 넣어주게 되는데, 현재 프로젝트에서는 해당 정보들을 파일로 갖고 있다.
따라서 다른 방식으로 접근이 필요하다.
파일을 base64로 인코딩 -> secret에 저장 -> github action에서 디코딩 후 파일 저장
1. 인코딩은 Base64 Encode and Decode - Online 이곳에서 하면 된다.
❗️❗️파일을 통째로 인코딩해야 오류가 발생하지 않습니다.❗️❗️
2. 파일을 통째로 인코딩하면 아래와 같이 txt 파일로 나오게 되는데, 해당 내용을 통째로 복사한다.
3. 그 값을 들고 프로젝트의 Secret값에 추가해준다.
4. github action에서 해당 secret들을 디코딩하고, 그 값을 파일로 저장해주면 된다.
아래 코드는 우리 프로젝트에서 사용하는 예시이다.
// build_test.yml - name: Create secret file env: FIREBASE_SECRET: ${{ secrets.FIREBASE_SECRET }} API_SECRET: ${{ secrets.API_SECRET }} run: | echo $FIREBASE_SECRET | base64 -D -o ${{ env.PROJECT }}/GoogleService-Info.plist echo $API_SECRET | base64 -D -o ${{ env.PROJECT }}/Service-Info.plist ls -al ${{ env.PROJECT }}
해치웠나?
다행히 성공적으로 빌드가 됐다. 대략 평균적으로 18분 정도 걸리는 것 같다.
SPM Cache가 동작하면, 16분 정도 걸린다.
DerivedData Cache까지 하게 되면, 시간이 비약적으로 단축된다.
그렇지만 과연 빌드 테스트에 Derived Data Cache를 적용해도 되는지는 아직 잘 판단이 안 선다.
번외
원래는 swiftlint도 같이 해주려고 생각했다.
하지만 build phase에 있는 swiftlint가 동작해서, 따로 설정해주지 않아도 됐다.
github action으로 swiftlint를 해줬을 때에는 제외한 경로까지 검사해줘서 애를 먹었는데,
build phase에서 실행되는 swiftlint는 설정한 옵션대로 잘 됐다.
번외 2
github action의 Cache는 branch별로 저장돼서 같은 브랜치에서 여러 번 푸쉬하는 것이 아니면, 별로 소용이 없는 것 같다.
개선 방법을 찾아봐야겠다.
추가 - fork한 레포에서는 upstream에 추가한 secret file이 없다
secret file을 추가하여 github action에서 활용하는 것은fork한 레포에서는 적용이 되지 않는 것을 확인할 수 있다.
해결 방법은 간단하게, fork한 레포에도 secret file을 추가해 주는것이다.