예~~전에 Jenkins와 docker를 이용해 프론트엔드의 CI/CD는 직접 구축해본 경험이 있지만
이와는 완전히 다르게 돌아가는 Android에서 CI/CD를 어떻게 적용하였는지에 대해 정리해보고자 합니다. 🤗
Github에서는 CI/CD를 위한 Action이라는 기능을 제공해주고 있습니다. :-)
1. Github Action 시작하기
Github에서 Repository를 만들게 되면 Actions tab을 확인하실 수 있습니다.
해당 tab에서 android를 검색하게 된다면 👇🏻아래와 같이 Android를 위한 CI를 볼 수 있어요.
여기서 Configure를 클릭하면, CI/CD를 설정할 수 있는 yaml 파일이 생성되고,
요구사항에 맞게 코드를 수정하여 commit하시면 Action이 적용되게 됩니다.
2. Configure 설정하기
제가 생각한 전체적인 프로세스는 👇🏻아래 이미지와 같습니다.
2.1 dev branch인 경우
Build까지만 수행
2.2 main branch인 경우
- Release로 build
- Firebase App Distribution에 배포
이를 위해 먼저 workflow의 job을 build와 deploy 2개로 나누고, main branch인 경우에만 deploy를 수행하도록 하였습니다.
2.1 공통 step
먼저 우선적으로 dev와 main branch에 상관없이 공통적으로 수행하는 action들에 대해 알아보겠습니다.
build:
runs-on: ubuntu-latest
steps:
- name: check out repository
uses: actions/checkout@v3
- name: set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: gradle
- name: Cache Gradle Packages
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{runner.os}}-gradle-${{hashFiles('**/*.gradle*')}}
restore-keys: ${{runner.os}}-gradle-
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Access Google-Service file
run: echo '${{ secrets.GOOGLE_SERVICE }}' > ./app/google-services.json
# - name: Run klint
# run: ./gradlew klintCheck
- name: Run unit tests
run: ./gradlew testDebugUnitTest
여기서 주의해야 하는 점은 Firebase에 App을 배포할 것이기 때문에 google-service.json이 필요하다는 것입니다.
🌹 google-service.json은 보안적으로 중요한 문서이기 때문에 Repository에 직접적으로 업로드하는 것을 피해야 합니다.
하지만 build를 하는데 google-service.json이 필요하죠.😭
저는 이 문제를 해결하기 위해 Github의 Secret에 google-service.json의 내용을 저장해두고 build 시에 가져와 사용할 수 있도록 해주었습니다. 🤗
2.2 Github Secret
Secret은 Repository의 Settings > Security 에서 추가할 수 있습니다.
🌹 주로 외부에 노출되어서는 안되는 환경변수를 저장하는데 사용할 수 있습니다.
저는 아래 이미지와 같이 3개의 Secret value를 추가하여 CI Configuration에서 사용하고 있죠.
2.3 dev branch
위에서 보았던 공통 action을 수행한 이후에 dev branch인 경우, 아래와 같이 build를 수행하게 됩니다.
이렇게 Configure를 지정하고 나서, dev branch에 push event를 발생시키게 되면 해당 workflow가 trigger되 정의되어 있는 job들을 실행하게 됩니다.
이전에 말씀드렸듯이 dev branch에 경우에는 build만 수행하도록 하는것이 목표였기 때문에 deploy까지 수행하지 않고 build에서 성공적으로 완료한 것을 볼 수 있습니다. 🔥👍🏻
만약 build 도중 문제가 생긴다면 해당 job의 세부사항을 통해 원인을 분석할 수 있습니다.(크으..)
🌹 확실히 예전 Jenkins를 사용할 때보다 UI가 좋아서 좋습니다.
2.4 추가
위와 같이 Action이 수행중인 것은 Action tab에서 확인할 수 있지만, code tab에서도 마찬가지로 확인할 수 있습니다.
👇🏻 요렇게
저 동그라미를 누르면 현재 진행중인 Action의 정보도 볼 수 있습니다.
3. main branch
main branch에 경우에는 두 개의 job을 수행해야 할 뿐만 아니라,
Firebase App Distribution에 테스트 앱을 배포해야 하는 작업이 필요하므로 dev branch보다 조금 더 복잡합니다. 😭
3.1 build
main branch에서는 dev와 달리 Release로 build를 수행하게 됩니다.
이 때의 결과물로 apk 파일이 생성되게 됩니다. 저는 이 apk 파일을 실제 배포를 담당하는 deploy job에서도 사용하고자 합니다.
이를 위해 Artifact를 이용해 파일을 업로드해둘 필요가 있습니다. 👍🏻
이렇게 build 작업을 마무리 하게 되면 다음으로 deploy job으로 넘어가게 됩니다.
3.2 deploy
🌹 deploy는 반드시 build 작업 이후에 수행될 수 있도록 needs를 지정해줍니다.
가장 먼저 해주어햐 하는 작업은 build에서 upload한 apk 파일을 다시 가져오는 것입니다.
🏠 이 때! name은 build에서 지정한 name, path와 동일해야 함에 주의해야 합니다. :-)
그 다음으로는 deploy의 최종 목적인 firebase에 앱을 배포하는 action입니다. 🔥🔥
다행이도 이는 외부 Actoin을 통해 수행할 수 있지요.ㅎㅎ
이전에 Secret에 대해 설명할 때 FIREBASE_APP_ID와 CREDENTIAL_FILE_CONTENT를 생성하였다고 말씀드렸습니다.
여기서는 이 두 값을 어떻게 만드는지에 대해 알아보도록 하겠습니다.
👇🏻 전달되는 값들은 아래 github에서 확인할 수 있습니다.
GitHub - wzieba/Firebase-Distribution-Github-Action: This action uploads artifacts (.apk or .ipa) to Firebase App Distribution.
This action uploads artifacts (.apk or .ipa) to Firebase App Distribution. - GitHub - wzieba/Firebase-Distribution-Github-Action: This action uploads artifacts (.apk or .ipa) to Firebase App Distri...
github.com
🔥 해당 Action의 예전 버전에서는 token 값을 전달하지만, token이 deprecated되어 대신 serviceCredentialsFileContent 값을 전달해주어야 합니다.
그럼 먼저 CREDENTIAL_FILE_CONTENT에 대해 알아보겠습니다.
3.1 CREDENTIAL_FILE_CONTENT
👇🏻 참고
FIREBASE_TOKEN migration
This action uploads artifacts (.apk or .ipa) to Firebase App Distribution. - wzieba/Firebase-Distribution-Github-Action
github.com
3.2 FIREBASE_APP_ID
해당 값은 Firebase console에서 생성할 수 있으며,
Project와 앱을 생성하게 되면 아래와 같이 APP ID를 확인할 수 있습니다.
이제 모든 설정이 완료되었습니다. 👏🏻👏🏻👏🏻👏🏻
main branch에 코드를 push하게 된다면
🌹 성공적으로 deploy까지 완료한 것을 알 수 있습니다.
Firebase Console에도
테스트 앱이 성공적으로 배포되었습니다.
👏🏻👏🏻👏🏻👏🏻
4. 최종 Configuration
name: Android CI
on:
push:
branches: [ "dev", "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: check out repository
uses: actions/checkout@v3
- name: set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: gradle
- name: Cache Gradle Packages
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{runner.os}}-gradle-${{hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/buildSrc/**.*.kt')}}
restore-keys: ${{runner.os}}-gradle-
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Access Google-Service file
run: echo '${{ secrets.GOOGLE_SERVICE }}' > ./app/google-services.json
# - name: Run klint
# run: ./gradlew klintCheck
- name: Run unit tests
run: ./gradlew testDebugUnitTest
- name: Build with Gradle
run: ./gradlew assembleDebug
if: "contains(github.ref, 'dev')"
- name: Build with Gradle
run: ./gradlew assembleRelease
if: "contains(github.ref, 'main')"
- name: Upload apk-files Directory
uses: actions/upload-artifact@v3
with:
name: app-release-unsigned
path: ${{ github.workspace }}/app/build/outputs/apk/release/
if-no-files-found: ignore
if: "contains(github.ref, 'main')"
deploy:
runs-on: ubuntu-latest
needs: build
if: "contains(github.ref, 'main')"
steps:
- name: Check out Git Repository
uses: actions/checkout@v2
- name: Download apk-files Artifactory
uses: actions/download-artifact@v3
with:
name: app-release-unsigned
path: ${{ github.workspace }}/app/build/outputs/apk/release/
- name: upload artifact to Firebase App Distribution
uses: wzieba/Firebase-Distribution-Github-Action@v1
with:
appId: ${{secrets.FIREBASE_APP_ID}}
serviceCredentialsFileContent: ${{secrets.CREDENTIAL_FILE_CONTENT}}
groups: base
file: app/build/outputs/apk/release/app-release-unsigned.apk
5. 참고자료
- [GitHub Actions Documentation]
- [Github Actions 으로 안드로이드 CI/CD 구축하기]
- [End to End CI/CD pipeline using GitHub Actions for Android Application]
처음해보는 Github의 CI/CD 작업이었지만
기존에 해보았던 Jenkins를 이용한 배포와 비슷한 점이 많아 빠르게 진행할 수 있었던 듯 합니다.
다만 역시 혼자 공부해 혼자 해보는거라 혹시 틀린 점이 있다면
꼭 댓글로 말씀 부탁드립니다. 🙏🙏
다음에는 lint까지 적용하여 CI할 수 있도록 개선해보고자 해요.
( * 벌써부터 노란줄이 엄청나게 그어지는게 보이네요. ㅎㅎ 😭 )
감사합니다. 😌
'기록 > 회고록' 카테고리의 다른 글
[2024] 부터 시작하는 올해 뭘 했을까? 😆 (0) | 2024.11.08 |
---|---|
[하루네컷] Designsystem에 맞게 Core Component 만들어보기 (0) | 2023.04.04 |
[Android] 첫 멀티모듈 적용기 (0) | 2023.03.06 |
[우테캠] 2차 팀별 과제 회고록 (2) | 2022.07.22 |
처음 써보는 회고록👍🏻 가봤슈 프로젝트 (2) | 2022.06.30 |