Info

iOS, Android 프로젝트에서 배포를 위해 아카이브 파일 (.ipa), 패키지파일 (.apk) 을 추출하고 업로드 및 공유하는데 귀찮은 작업들이 상당히 많다.
(iOS는 scheme 바꾸고, 아카이빙하고 파일 추출하고 업로드하고 공유하고.., 안드로이드도 뭐 작업중에는 뭘 할 수가 없으니까 )

그래서 이런 귀찮은 작업을 좀 편하게 할 수 있는 방법을 찾다가 Fastlane 이라는 것을 찾다.
Fastlane 은 인증서 자동갱신, 스냅샷 추출, 아카이빙, crashlytics 배포, slack 공유, s3업로드, 테스트 플라이트, 앱스토어 등록 등등 다양한 동작을 하도록 구성할 수 있다.

자세한 내용은 아래 fastlane 사이트를 참고하는걸로 하고..
일단은 내부 배포를 위하여 프로젝트 내에서 provisioning 파일을 생성/갱신, 테스트용 뱃지 추가, 아카이브 후 추출, aws-s3에 파일 업로드, Crashlytics 에 배포 파일 업로드, slack에 결과 공유 까지만 작업을 하는 방법을 소개하려고 한다.

1. Fastlane 설치

기능을 사용하기 위해서 mac에 fastlane을 설치 해야한다. 설치 방법은 세가지가 있으니 자신의 mac 에서 주로 사용하는 방법을 선택하여 설치를 진행하면 된다.

(Homebrew 로 설치하면 fastlane 내부 ruby가 동작해서 작업이 귀찮아짐. ruby 모듈들을 fastlane 안에 ruby에 설치해줘야함. 맘편하게 Script로 설치하세요)


Homebrew

$ brew cask install fastlane

Installer Script

아래 링크에서 zip 파일을 다운로드 받고, install 스크립트를 실행

https://download.fastlane.tools/fastlane.zip

Rubygems

$ sudo gem install fastlane -NV



2. Fastlane 초기화

Fastfile, Appfile, .env 파일을 직접 생성해서 작업을 해도 되지만 귀찮으니까 iOS혹은 Android 프로젝트 루트에서 아래 명령어로 관련 파일들을 생성해주자
# fastlane
$ fastlane init
초기화 작업이 완료되면 ./fastlane 폴더 내부에 Fastfile, Appfile, .env 파일이 생성되어 기본적인 준비가 된다.


3. Plugin 설치 (aws_s3, badge) - optional

Badge

앱을 내부배포할 때 dev, stage등 여러가지 앱 스킴을 사용하는 경우들이 있는데 이럴경우 어떤 버전이 설치되어 있는지 직관적으로 알 수 있는 방법이 없다. 
그래서 badge 라는 plugin을 사용하여 앱 아이콘에 뱃지를 넣어주어 직관적인 버전 확인이 가능하도록 해줄 수 있다. 
아래를 참고하여 플러그인을 설치하고, 관련된 모듈들을 설치하도록 하자.
더 자세한 내용은 아래 사이트를 참고하자
# badge
$ fastlane add_plugin badge
#
$ brew install imagemagick
$ brew install librsvh

aws_s3

내부 배포를 위한 파일 서버가 S3로 설정되어 있는 경우 아카이브 파일 (.ipa), 패키지파일 (.apk) 을 생성한 후 Amazon S3 Bucket 으로 바로 전송을 할 수가 있다.
이 기능을 사용하려면 아래를 참고하여 S3 플러그인을 설치하면 되고, 조금 더 자세한 정보 및 사용법은 아래 링크에서 확인하면 된다.
# aws-s3
$ fastlane add_plugin aws_s3


4. Fastlane 파일 설정

이제 fastlane 초기 세팅 작업들은 마무리가 되었고, 실제로 동작시킬 내용들을 추가하는 방법을 알아보도록 하자.

Fastfile

우선 가장 핵심이 되는 Fastfile 에 대해서 살펴보자. 
이 파일은 platform과 lane 을 설정하고 lane 별로 어떤 동작을 시킬지, lane 실행전/후에 어떤 동작을 수행할 지 설정하는 곳이다.
아래 예제 파일을 보면서 동작들을 확인해보자.
# More documentation about how to customize your build
# can be found here:
# https://docs.fastlane.tools
fastlane_version "2.18.3"
# This value helps us track success metrics for Fastfiles
# we automatically generate. Feel free to remove this line
# once you get things running smoothly!
generated_fastfile_id "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # fastfile
default_platform :ios
#######
# iOS #
#######
platform :ios do
before_all do
# lane
end
##################################################
# First iOS App Lanes - A
##################################################
lane :firstapp_debug do
# provisioning profile , ,
sigh
# (Optional) badge
# badge(dark: true) #
badge(shield: "Version-dev-red", no_badge: true) # shield
# iOS app build, archive, export
gym(
# workspace
workspace: "[Project path].xcworkspace", # [Project path]: xcworkspace (xcodeproj secondapp )
configuration: "Debug", # configuration
scheme: "[Project scheme]", # [Project scheme]: scheme
silent: true,
clean: true,
output_name: "[file name]", # [file name]:
include_symbols: false, # (optional)
include_bitcode: true, # (optional) - default:false
export_method: "development", # export method
)
# (Optional) S3
aws_s3(
access_key: ENV['S3_ACCESS_KEY'], # .env
secret_access_key: ENV['S3_SECRET_ACCESS_KEY'], # .env
bucket: ENV['S3_BUCKET'], # .env
region: ENV['S3_REGION'], # .env
path: '[bucket path]', # [bucket path]:
ipa: '[file name].ipa', # export
upload_metadata: true,
)
# (Optional) Crashlytics
crashlytics(
api_token: ENV['CRASHLYTICS_API_TOKEN'], # .env
build_secret: ENV['CRASHLYTICS_SECRET'] # .env
)
end
lane :firstapp_stage do
sigh
badge(shield: "Version-stage-blue", no_badge: true)
gym(
workspace: "[Project path]",
configuration: "Stage",
scheme: "[Project scheme]",
silent: true,
clean: true,
output_name: "[file name]",
include_symbols: false,
include_bitcode: true,
export_method: "ad-hoc",
)
aws_s3(
access_key: ENV['S3_ACCESS_KEY'],
secret_access_key: ENV['S3_SECRET_ACCESS_KEY'],
bucket: ENV['S3_BUCKET'],
region: ENV['S3_REGION'],
path: '[bucket path]',
ipa: '[file name].ipa',
upload_metadata: true,
)
crashlytics(
api_token: ENV['CRASHLYTICS_API_TOKEN'],
build_secret: ENV['CRASHLYTICS_SECRET']
)
end
###########################################################
# Second iOS App Lane - B iOS
###########################################################
lane :secondapp_debug do
sigh
badge(shield: "Version-dev-red", no_badge: true)
gym(
# project
project: "[Project path].xcodeproj", # [Project path]: xcodeproj
configuration: "Debug",
scheme: "[Project scheme]",
silent: true,
clean: true,
output_name: "[file name]",
include_symbols: false,
include_bitcode: true,
export_method: "development",
)
aws_s3(
access_key: ENV['S3_ACCESS_KEY'],
secret_access_key: ENV['S3_SECRET_ACCESS_KEY'],
bucket: ENV['S3_BUCKET'],
region: ENV['S3_REGION'],
path: '[bucket path]',
ipa: '[file name].ipa',
upload_metadata: true,
)
crashlytics(
api_token: ENV['CRASHLYTICS_API_TOKEN'],
build_secret: ENV['CRASHLYTICS_SECRET']
)
end
# lane
after_all do |lane|
# slack .
slack(
message: "FastLane success",
slack_url: ENV['SLACK_URL']
)
end
# lane
error do |lane, exception|
slack(
message: exception.message,
success: false,
slack_url: ENV['SLACK_URL']
)
end
end
##############################################################################################
##############################################################################################
###########
# Android #
###########
platform :android do
before_all do
end
##################################################
# First iOS App Lanes - A
##################################################
lane :firstapp_debug do
gradle(task: 'clean', project_dir: "[Project path]") # [Project path]: root
gradle(task: "assemble",
build_type: "Debug",
project_dir: "[Project path]") # [Project path]: root
# (Optional) shell script
# shell script Fastfile fastlane/
sh "mv ../firstapp_android/app/build/outputs/apk/firstapp-debug.apk ../firstapp_android/app/build/outputs/apk/[File name].apk" # [File name]:
# (Optional) S3
# plugin fastlane
aws_s3(
access_key: ENV['S3_ACCESS_KEY'], # .env
secret_access_key: ENV['S3_SECRET_ACCESS_KEY'], # .env
bucket: ENV['S3_BUCKET'], # .env
region: ENV['S3_REGION'], # .env
path: '[bucket path]', # [bucket path]:
ipa: './firstapp_android/app/build/outputs/apk/[File name].apk', # export
upload_metadata: true,
)
end
lane :firstapp_stage do
gradle(task: 'clean', project_dir: "[Project path]")
gradle(
task: "assemble",
build_type: "Release",
# signing
properties: {
# 'versionCode' => [version code], # (Optional) [version code]:
# 'versionName' => [version name], # (Optional) [version name]:
'android.injected.signing.store.file'=> ENV['ANDROID_APP_SIGNING_KEY'], # .env
'android.injected.signing.store.password'=> ENV['ANDROID_APP_SIGNING_PASSWORD'], # .env
'android.injected.signing.key.alias'=> ENV['ANDROID_APP_SIGNING_ALIAS'], # .env
'android.injected.signing.key.password'=> ENV['ANDROID_APP_SIGNING_PASSWORD'] # .env
},
project_dir: "[Project path]")
sh "mv ../firstapp_android/app/build/outputs/apk/firstapp-release.apk ../firstapp_android/app/build/outputs/apk/[File name].apk"
aws_s3(
access_key: ENV['S3_ACCESS_KEY'],
secret_access_key: ENV['S3_SECRET_ACCESS_KEY'],
bucket: ENV['S3_BUCKET'],
region: ENV['S3_REGION'],
path: '[bucket path]',
ipa: './firstapp_android/app/build/outputs/apk/[File name].apk',
upload_metadata: true,
)
end
after_all do |lane|
slack(
message: "FastLane success",
slack_url: ENV['SLACK_URL']
)
end
error do |lane, exception|
slack(
message: exception.message,
success: false,
slack_url: ENV['SLACK_URL']
)
end
end

.env

다음은 환경변수를 선언해 놓고 사용하는 dotenv 형태의 .env 파일이다.
이 파일에 선언된 값들을 Fastfile 에서 ENV[‘ ‘] 형태로 사용할 수 있다.
아래는 예제용으로 작성된 Fastfile의 .env 파일이다.
# for Slack
SLACK_URL="https://hooks.slack.com/services/~~~" # slack hook
# for S3
S3_ACCESS_KEY='XXXXXXX' # S3 Access key
S3_SECRET_ACCESS_KEY='xxxxxxxxxxxxxxxx' # S3 Sceret Access key
S3_BUCKET='example-buket' # S3 Bucket name
S3_REGION='ap-northeast-2' # S3 Region
# for Crashlytics
CRASHLYTICS_API_TOKEN='xxxxxxxxxxxxxxxx' # Crashlytics API token
CRASHLYTICS_SECRET='xxxxxxxxxxxxxxxx' # Crashlytics Secret
# for Android
ANDROID_APP_SIGNING_KEY='./keystore/firstapp_key.keystore' # Signing key
ANDROID_APP_SIGNING_PASSWORD='xxxx' # signing pw
ANDROID_APP_SIGNING_ALIAS='firstapp_android' # signing alias

Appfile

마지막으로 앱 관련 설정을 가지고 있는 Appfile 이다.
이 파을은 AppStore Id, Bundle Id, Team Id 등 식별자 값이 설정되어있는 파일이다.
Appfile 과 관련해서 더 자세한 내용은 링크를 참고하면 된다. (https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Appfile.md)
아래 파일도 예제용으로 작성된 FastfileAppfile 이다.
# Default
app_identifier "kr.tez.firstapp" # The bundle identifier of your app
apple_id "dev@tez.kr" # Your Apple email address
team_id "XXXXXXX" # Developer Portal Team ID
for_platform :ios do
for_lane :first_debug do
app_identifier "kr.tez.firstapp"
apple_id "dev@tez.kr"
team_id "XXXXXXX"
end
for_lane :first_stage do
app_identifier "kr.tez.firstapp"
apple_id "dev@tez.kr"
team_id "XXXXXXX"
end
for_lane :second_debug do
app_identifier "kr.tez.secondapp"
apple_id "tez_enterprise@tez.kr"
team_id "XYXYXYXY"
end
end
# you can even provide different app identifiers, Apple IDs and team names per lane:
# More information: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Appfile.md


5. Fastlane 기본 명령어

lane 과 파일 작성이 완료되면 몇가지 기본적인 명령어를 확인하고 사용해보도록 하자.

$ fastlane lanes

우선 사용가능한 lane들을 조회하는 명령어 이다. 
이 명령어로 추가해 놓은 lane들이 정상적으로 로드되는지 확인하면 된다.
$ fastlane lanes
+------------------------+---------+--------+
| Used plugins |
+------------------------+---------+--------+
| Plugin | Version | Action |
+------------------------+---------+--------+
| fastlane-plugin-aws_s3 | 0.2.4 | aws_s3 |
+------------------------+---------+--------+
[18:47:48]: -------------------------------------------------
[18:47:48]: --- Step: Verifying required fastlane version ---
[18:47:48]: -------------------------------------------------
[18:47:48]: Your fastlane version 2.28.7 matches the minimum requirement of 2.18.3
[18:47:48]: ------------------------------
[18:47:48]: --- Step: default_platform ---
[18:47:48]: ------------------------------
--------- ios---------
----- fastlane ios firstapp_debug
----- fastlane ios firstapp_stage
----- fastlane ios secondapp_debug
--------- android---------
----- fastlane android firstapp_debug
----- fastlane android firstapp_stage
Execute using `fastlane [lane_name]`

$ fastlane env

이 명령어는 fastlane 환경 체크를 하는 명령이다. 
fasatlane이 정상적으로 실행되지 않거나 정보를 확인할 경우 이용하면 된다.
$ fastlane env
...
#
...


6. Fastlane 실행

이제 기나긴 작업이 끝나고 완성된 lane들을 사용하는 방법이다!

작업은 길지만 실제 사용은 별거 없다. 
platform과 lane 을 선택해주면 설정해준 작업들이 수행된다.
fastlane {platform} {lane}
$ fastlane ios firstapp_debug
...
# working...
...
[13:48:03]: ▸ Archive Succeeded
...
[13:48:06]: Successfully exported and compressed dSYM file
[13:48:06]: Successfully exported and signed the ipa file:
...
[13:48:06]: -------------------
[13:48:06]: --- Step: slack ---
[13:48:06]: -------------------
[13:48:07]: Successfully sent Slack notification
+------+-------------------------------------+-------------+
| fastlane summary |
+------+-------------------------------------+-------------+
| Step | Action | Time (in s) |
+------+-------------------------------------+-------------+
| 1 | Verifying required fastlane version | 0 |
| 2 | default_platform | 0 |
| 3 | gym | 23 |
| 4 | slack | 1 |
+------+-------------------------------------+-------------+
...
[13:48:07]: fastlane.tools finished successfully
위와 같이 진행과정이 표시되고 모든 작업이 완료되면 fastlane.tools finished successfully 이라는 메시지와 함께 작업이 완료된다.


반응형