728x90
지난 프로젝트에서 Github Actions CI/CD를 사용한 적이 있었다.
오늘 포스팅에서는 사용했던 Github Actions CI/CD 코드를 분석하고, CI/CD가 무엇인지 또한 다음 프로젝트에서도 활용할 수 있도록 공부해보자.
CI/CD란 무엇인가
CI(Continuous Integration)와 CD(Continuous Delivery 또는 Continuous Deployment)는 소프트웨어 개발 프로세스에서 중요한 역할을 담당한다. CI는 코드 변경사항을 주기적으로 빌드하고 테스트하여 메인 저장소에 통합하는 것을 말한다.
CD는 CI 과정을 통과한 코드를 자동으로 배포하는 단계로, Delivery는 승인을 통해 배포되고, Deployment는 자동으로 배포된다.
CI/CD의 이점
- 개발 과정의 자동화를 통해 빌드와 테스트의 일관성을 유지할 수 있다.
- 코드 변경 사항을 신속하게 감지하고 피드백을 받을 수 있어 개발 사이클을 단축할 수 있다. 왜냐하면 지속적인 통합과 배포를 통해 개발자들이 보다 집중적으로 코딩에 몰두할 수 있으며, 결함을 빠르게 발견하고 수정할 수 있기 때문이다.
- 배포 과정의 자동화는 사용자에게 안정적인 애플리케이션 업데이트를 보다 신속하게 제공할 수 있게 한다.
프로젝트에서 사용했던 CI(Continuous Integration, 지속적 통합)
전체코드
name: Android CI
on:
push:
branches: [main]
paths-ignore:
- '**/README.md'
pull_request:
branches: [main]
jobs:
build:
name: Development build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Access SERVER_URL
env:
SERVER_URL: $
run: echo '${{ secrets.SERVER_URL }}' > ./apikey.properties
- name: Setup JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: gradle
- name: Build with Gradle
run: ./gradlew build
- name: Check Lint
run: ./gradlew ktlintCheck
ui-test:
name: UI tests on Android (API level ${{ matrix.api-level }})
runs-on: macos-latest
strategy:
matrix:
api-level: [ 23, 30, 31 ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Access SERVER_URL
env:
SERVER_URL: $
run: echo '${{ secrets.SERVER_URL }}' > ./apikey.properties
- name: Setup JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: gradle
- name: Setup Android SDK
uses: android-actions/setup-android@v2
- name: Run UI test
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
disable-animations: true
arch: x86_64
script: ./gradlew connectedCheck
코드 분석
워크플로우 정의
name: Android CI
on:
push:
branches: [main]
paths-ignore:
- '**/README.md'
pull_request:
branches: [main]
- name: Android CI: 워크플로우의 이름이다.
- on: 워크플로우를 트리거하는 이벤트를 정의한다.
- push: main 브랜치로의 푸시 이벤트를 트리거합니다. 단, README.md 파일에 대한 변경은 무시한다.
- pull_request: main 브랜치로의 풀 리퀘스트 이벤트를 트리거한다.
빌드 작업 정의
jobs:
build:
name: Development build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Access SERVER_URL
env:
SERVER_URL: $
run: echo '${{ secrets.SERVER_URL }}' > ./apikey.properties
- name: Setup JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: gradle
- name: Build with Gradle
run: ./gradlew build
- name: Check Lint
run: ./gradlew ktlintCheck
- jobs: 워크플로우 내에서 실행되는 작업들을 정의한다.
- build: 빌드 작업의 이름이다.
- runs-on: ubuntu-latest: 이 작업은 최신 우분투 환경에서 실행된다.
- steps: 작업 내에서 실행되는 단계를 정의한다.
- uses: actions/checkout@v3: 저장소를 체크아웃한다.
- name: Access SERVER_URL: 비밀 값 SERVER_URL을 환경 변수로 설정하고, 이를 apikey.properties 파일에 저장한다.
- uses: actions/setup-java@v3: JDK 11을 설정한다.
- java-version: '11': 사용될 자바 버전이다.
- distribution: 'temurin': JDK 배포판이다.
- cache: gradle: Gradle 캐시를 사용하여 빌드 시간을 단축한다.
- run: ./gradlew build: Gradle을 사용하여 프로젝트를 빌드한다.
- run: ./gradlew ktlintCheck: Gradle을 사용하여 Ktlint를 통해 린트 검사를 수행한다.
UI 테스트 작업 정의
ui-test:
name: UI tests on Android (API level ${{ matrix.api-level }})
runs-on: macos-latest
strategy:
matrix:
api-level: [ 23, 30, 31 ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Access SERVER_URL
env:
SERVER_URL: $
run: echo '${{ secrets.SERVER_URL }}' > ./apikey.properties
- name: Setup JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: gradle
- name: Setup Android SDK
uses: android-actions/setup-android@v2
- name: Run UI test
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
disable-animations: true
arch: x86_64
script: ./gradlew connectedCheck
- ui-test: UI 테스트 작업의 이름이다.
- runs-on: macos-latest: 이 작업은 최신 맥OS 환경에서 실행된다.
- strategy: 매트릭스 전략을 사용하여 여러 환경에서 테스트를 실행한다.
- matrix: 여러 API 레벨에서 테스트를 실행하기 위한 매트릭스를 정의한다.
- api-level: [ 23, 30, 31 ]: API 레벨 23, 30, 31에서 테스트를 실행한다.
- matrix: 여러 API 레벨에서 테스트를 실행하기 위한 매트릭스를 정의한다.
- steps: 작업 내에서 실행되는 단계를 정의한다.
- uses: actions/checkout@v3: 저장소를 체크아웃한다.
- name: Access SERVER_URL: 비밀 값 SERVER_URL을 환경 변수로 설정하고, 이를 apikey.properties 파일에 저장한다.
- uses: actions/setup-java@v3: JDK 11을 설정한다.
- uses: android-actions/setup-android@v2: Android SDK를 설정한다.
- uses: reactivecircus/android-emulator-runner@v2: Android 에뮬레이터를 설정하고 UI 테스트를 실행한다.
- api-level: ${{ matrix.api-level }}: 매트릭스에서 정의된 API 레벨을 사용한다.
- disable-animations: true: 에뮬레이터에서 애니메이션을 비활성화한다.
- arch: x86_64: 에뮬레이터 아키텍처를 설정한다.
- script: ./gradlew connectedCheck: Gradle을 사용하여 연결된 테스트를 실행한다.
프로젝트에서 사용했던 CD(Continuous Delivery, 지속적인 제공)
전체코드
name: Build and Publish to Play Store
on:
push:
branches:
- release/**
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2.4.0
- name: set up JDK 11
uses: actions/setup-java@v2.5.0
with:
distribution: 'zulu'
java-version: '11'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Access SERVER_URL
env:
SERVER_URL: $
run: echo '${{ secrets.SERVER_URL }}' > ./apikey.properties
- name: Build with Gradle
run: ./gradlew build
- name: Build Release AAB
id: buildRelease
run: ./gradlew bundleRelease
- name: Sign AAB
id: sign
uses: r0adkll/sign-android-release@v1
with:
releaseDirectory: app/build/outputs/bundle/release
signingKeyBase64: ${{ secrets.KEY_STORE_BASE_64 }}
alias: ${{ secrets.ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
- name: Upload AAB
id: uploadArtifact
uses: actions/upload-artifact@v2.3.1
with:
name: app
path: app/build/outputs/bundle/release/app-release.aab
- name: Create service_account.json
id: createServiceAccount
run: echo '${{ secrets.SERVICE_ACCOUNT_KEY }}' > service_account.json
- name: Deploy to Play Store (Internal)
id: deploy
uses: r0adkll/upload-google-play@v1.0.16
with:
track: internal
serviceAccountJson: service_account.json
packageName: com.pocs.blog
status: completed
releaseFiles: app/build/outputs/bundle/release/app-release.aab
코드분석
워크플로우 트리거
on:
push:
branches:
- release/**
- release/** 브랜치에 푸시가 발생할 때마다 이 워크플로우가 실행된다.
jobs:
build:
runs-on: ubuntu-latest
- build 작업은 최신 우분투 환경에서 실행된다.
단계별 작업
1. 코드 체크아웃
- name: Checkout
uses: actions/checkout@v2.4.0
- 저장소의 코드를 체크아웃한다.
2. JDK 설정
- name: set up JDK 11
uses: actions/setup-java@v2.5.0
with:
distribution: 'zulu'
java-version: '11'
- Zulu OpenJDK 11을 설정한다.
3. gradlew 실행 권한 부여
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- gradlew 스크립트에 실행 권한을 부여한다.
4. 환경변수 설정 및 파일 실행
- name: Access SERVER_URL
env:
SERVER_URL: $
run: echo '${{ secrets.SERVER_URL }}' > ./apikey.properties
- secrets.SERVER_URL 값을 apikey.properties 파일에 기록한다.
5. Gradle을 사용한 빌드
- name: Build with Gradle
run: ./gradlew build
- Gradle을 사용하여 프로젝트를 빌드한다.
6. 릴리즈 AAB 빌드
- name: Build Release AAB
id: buildRelease
run: ./gradlew bundleRelease
- 릴리즈용 Android App Bundle (AAB)을 빌드한다.
7. AAB 서명
- name: Sign AAB
id: sign
uses: r0adkll/sign-android-release@v1
with:
releaseDirectory: app/build/outputs/bundle/release
signingKeyBase64: ${{ secrets.KEY_STORE_BASE_64 }}
alias: ${{ secrets.ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
- AAB 파일을 서명합니다. 서명에 필요한 정보는 GitHub Secrets에서 가져온다.
8. AAB 업로드
- name: Upload AAB
id: uploadArtifact
uses: actions/upload-artifact@v2.3.1
with:
name: app
path: app/build/outputs/bundle/release/app-release.aab
- 빌드된 AAB 파일을 아티팩트로 업로드한다.
9. 서비스 계정 JSON 파일 생성
- name: Create service_account.json
id: createServiceAccount
run: echo '${{ secrets.SERVICE_ACCOUNT_KEY }}' > service_account.json
- secrets.SERVICE_ACCOUNT_KEY 값을 사용하여 service_account.json 파일을 생성한다.
10. Google Play Store에 배포(내부 트랙)
- name: Deploy to Play Store (Internal)
id: deploy
uses: r0adkll/upload-google-play@v1.0.16
with:
track: internal
serviceAccountJson: service_account.json
packageName: com.pocs.blog
status: completed
releaseFiles: app/build/outputs/bundle/release/app-release.aab
- 빌드된 AAB 파일을 내부 트랙으로 Google Play Store에 배포한다. 필요한 인증 정보는 service_account.json 파일에서 가져온다.
728x90
'Android > Study' 카테고리의 다른 글
[Android] WebView 사용기 (0) | 2024.06.24 |
---|---|
[Android] WebSocket를 통해서 채팅 기능 구현 feat. Clean Architecture, Hilt (0) | 2024.06.24 |
[Android] ConstraintLayout의 장점 (0) | 2024.06.11 |
[Android] RecyclerView LayoutManager에 대해서 공부하자 (0) | 2024.06.10 |
[Android] RecyclerView에서 onCreateViewHolder와 onBindViewHolder의 차이 (0) | 2024.06.09 |