
지난 게시글에 이어 이번엔 Android 에 대해 적용을 할 차례이다. Android 는 비교적 iOS 보다 적용하는 과정이 수월한데,
Android 배포 자동화 시스템 구축에서 사용하는 툴이나 서비스들은 다음과 같다.
- Android
- Gradle or Terminal Command (APK/AAB 빌드)
- Play Console API (빌드 업로드 및 배포)
- Fastlane Supply (Google Play 메타데이터 관리)
cd android/
fastlane init
init 명령어를 입력한 뒤 json secret file 의 경로를 입력해야 하는데, 이는 Google credentials 를 의미한다.
GCP 에서 서비스 계정을 생성한 뒤 여기서 발급받은 json 을 프로젝트에 넣어서 그 경로를 입력해줘야 하는데 이에 대한 방법은
Fastlane-Android 공식문서의 Collect your Google credentials 섹션에 자세하게 나와있다.
▲ 그림 1. 추가한 Fastlane 용 서비스 계정
이 밖에도 꼭 해줘야 하는 작업으로는 Google Play Developer API 가 활성화 되어 있는지 확인해야 한다. 서비스 계정을 넣어줘도 해당 API 가 활성화되어 있지 않으면 Play Console 에 앱 번들 자동 업로드가 안되기 때문에 활성화가 필요하다.
▲ 그림 2. Google Play Android Developer API 활성화
우선 build_app(flavor) 이라는 함수를 정의하였다.
Fastfile에서 Android 앱을 빌드하는 방법은 크게 두 가지가 있다:
- Gradle 명령어를 직접 사용하는 방식
- flutter 명령어를 사용하는 방식
이 프로젝트에서는 두 번째 방법인 flutter 명령어를 통한 빌드 방식을 선택하였다. 그 이유는, --dart-define=FLAVOR=dev 또는 --dart-define=FLAVOR=prod와 같은 환경 변수를 명시적으로 main.dart에 전달할 수 있기 때문이다.
반면, Gradle 명령어만을 사용할 경우에는 Flutter 엔트리포인트(main.dart)에 전달할 --dart-define 인자를 자연스럽게 포함시키기 어렵다. 따라서 각 빌드 flavor 환경에 맞는 정의를 명확히 전달하고자, flutter build 명령어를 직접 사용하여 빌드를 수행하도록 구성하였다.
platform :android do
def build_app(flavor)
if flavor == "dev"
sh "flutter build appbundle --flavor dev --release --dart-define=FLAVOR=dev"
return "../build/app/outputs/bundle/devRelease/app-dev-release.aab"
elsif flavor == "prod"
sh "flutter build appbundle --flavor prod --release --dart-define=FLAVOR=prod"
return "../build/app/outputs/bundle/prodRelease/app-prod-release.aab"
else
UI.user_error!("Unknown flavor: #{flavor}")
end
end
Android 의 경우 이전 게시글(iOS)과 다르게, Android 에서는 내부 테스트용 앱 번들을 올릴 때, 개발 환경의 앱 번들은 올릴 수가 없어서, 우선 운영 환경에서의 내부 테스트 배포(Lane 1), 운영 환경에서의 Play Console 배포(Lane 2) 로 2개의 Lane 을 구성하였다.
이러한 문제점에 착안하여 Firebase App Distribution을 적용하였는데, 추후에 Firebase App Distribution 까지 CI 단계에 적용한 내용에 대해 포스트할 예정이다.
[build_app(flavor) 생략]
...
# Lane 1: [prod] 환경 - Play Console 내부 테스트 배포
desc "Deploy production .aab to Play Console Internal Test"
lane :deploy_prod_to_internal_test do
aab_path = build_app("prod")
upload_to_play_store(
track: "internal",
aab: aab_path
)
end
# Lane 2: [prod] 환경 - 프로덕션 배포
desc "Deploy production .aab to Play Console Production"
lane :deploy_prod_to_production do
aab_path = build_app("prod")
upload_to_play_store(
track: "production",
aab: aab_path,
skip_upload_metadata: true,
skip_upload_images: true,
skip_upload_screenshots: true,
skip_upload_changelogs: true,
)
end
end
iOS 와 마찬가지로 Play Console 에 자동으로 앱 번들을 올리고, 이 후 출시 노트나 메타 데이터 변경, 버전 업데이트 작업은 수동으로 하기 위해 다음과 같이 설정하였다.
이전 게시글 에서 공통적인 setup_environment 을 수행한 뒤에 deploy_android 작업을 수행한다. 우선 앱을 빌드 하기 전에 환경 설정해줘야 하는 작업으로는 다음과 같다.
- Google-services.json 생성
- local.properties 생성
- 앱 서명 관련 key.properties 생성
위의 3가지들은 모두 .gitignore 에 명시되어 있는 파일들로, Runner 에서 실행했을 때 해당 파일이 물리적으로 없기 때문에 이를 Actions secrets 에 값을 저장하고 불러와 생성하는 식으로 진행해야 한다.
▲ 그림 3. Android 관련 Github Actions secrets 저장된 변수들
deploy_android:
name: Android Deployment
runs-on: self-hosted
needs: setup_environment
steps:
- name: Google-services.json 생성
working-directory: android
run: |
# 운영 환경(prod)만 고려하여 배포(내부테스트, 프로덕션)
mkdir -p app/src/prod
cat <<EOF > app/src/prod/google-services.json
${{ secrets.ANDROID_GOOGLE_SERVICE_PROD_JSON }}
EOF
- name: local.properties 생성
working-directory: android
run: |
cat <<EOF > local.properties
${{ secrets.ANDROID_LOCAL_PROPERTIES }}
EOF
- name: Signing Key 복호화 및 key.properties 생성
working-directory: android
run: |
# keystore 전용 디렉토리 생성
mkdir -p keystore/release/prod
# .jks 파일 복호화
echo "${{ secrets.ANDROID_PRODUCTION_KEY_BASE_64 }}" | base64 -d > keystore/release/prod/production-key.jks
# 권한 설정
chmod 600 keystore/release/prod/production-key.jks
# production-key.properties 생성
cat <<EOF > keystore/release/prod/production-key.properties
${{ secrets.ANDROID_PRODUCTION_KEY_PROPERTIES }}
EOF
환경설정이 모두 완료되었다면 그 다음으로 커밋 메시지에서 배포 옵션에 따라 배포를 실행해야 한다. 마찬가지로 [커밋 메시지] - deploy:1 이런 식으로 배포 옵션을 메시지를 통해 입력을 받아 실행하였고, 위에서 2개의 Lane(내부 테스트 업로드, 프로덕션 업로드)를 설정했기 때문에 이에 따라 분기 처리하여 실행하도록 설정했다.
- name: 옵션에 따른 배포 실행
working-directory: android
run: |
COMMIT_MSG=$(git log -1 --pretty=%B)
if [[ "$COMMIT_MSG" =~ deploy:[1-9] ]]; then
DEPLOY_OPTION=$(echo "$COMMIT_MSG" | grep -o 'deploy:[1-9]' | cut -d':' -f2)
else
echo "배포 옵션이 지정되지 않았습니다. 기본 옵션(1) 사용"
DEPLOY_OPTION="1"
fi
echo "선택된 배포 옵션: $DEPLOY_OPTION"
case "$DEPLOY_OPTION" in
"1")
echo "[production] Play Console Internal Test 배포 시작"
fastlane deploy_prod_to_internal_test
;;
"2")
echo "[production] Play Console Production 배포 시작"
fastlane deploy_prod_to_production
;;
*)
echo "Invalid deployment option selected: $DEPLOY_OPTION"
exit 1
;;
esac
지금까지 Android 와 iOS 에서 배포 옵션에 따라 배포 자동화 시스템을 구축하는 방식에 대해서 알아보았다. iOS 에서는 각 환경마다 TestFlight 에 올릴 수 있지만 Android 에서는 개발 환경 전용 GoogleService.json 과 업로드 키 문제로 인해 "운영" 환경만 내부테스트에 업로드가 가능한 한계점이 있는데, 이를 보완하기 위해서 Firebase 의 App Distribution 서비스를 도입했었고 이를 가지고 배포 자동화 워크 플로우에 추가할 계획이다.
또한 지금은 Build Configuration 이 환경에 따라서 dev 와 prod 로 구성되어 있는데, 추후에 포스트할 게시글의 내용으로는 환경을 더 나눠 이에 따른 배포 파이프라인 개선을 할 예정이다.