ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Flutter Fastlane CI/CD(2) - Android
    Flutter ํ…Œํฌ 2023. 3. 7. 01:08

    1. Fastlane init

     
    cd android/
    fastlane initโ€‹

    Android ํด๋”๋กœ ์ด๋™ํ•˜์…”์„œ, init ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.

    ์ดˆ๊ธฐ์— ํŒจํ‚ค์ง€ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜๋ผ๊ณ  ๋‚˜์˜ต๋‹ˆ๋‹ค. ํŒจํ‚ค์ง€ ์ด๋ฆ„(com.example.example)์ด ๊ธฐ์–ต๋‚˜์ง€ ์•Š์œผ์‹ ๋‹ค๋ฉด, app/build.gradle ํŒŒ์ผ ๊ฐ€์…”์„œ applicationId ํ•ญ๋ชฉ์— ์žˆ๋Š” ํŒจํ‚ค์ง€ ์ด๋ฆ„์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

     

    2. Json Secret File

     

    ํŒจํ‚ค์ง€ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜๋ฉด, json secret file ๊ฒฝ๋กœ๋ฅผ ๋„ฃ์œผ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. (json_secret_file? Fastlane Android ๊ณต์‹๋ฌธ์„œ Collect your Google credentials ์„น์…˜)

     

    2-1. GCP ์ ‘์†

     

    Google Cloud Platform ์‚ฌ์ดํŠธ์˜ IAM ๋ฐ ๊ด€๋ฆฌ์ž > ์„œ๋น„์Šค ๊ณ„์ •, ํŽ˜์ด์ง€ ์™ผ์ชฝ ์ƒ๋‹จ ์œ„์˜ ์„œ๋น„์Šค ๊ณ„์ • ๋งŒ๋“ค๊ธฐ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.

    2-2. ์„œ๋น„์Šค ๊ณ„์ • ์„ธ๋ถ€์ •๋ณด ์ž…๋ ฅ

     

    Fastlane ์„ ์œ„ํ•ด ๋งŒ๋“œ๋Š” ์„œ๋น„์Šค ๊ณ„์ •์ด๋ผ, ์ด๋ฆ„์€ fastlane ์œผ๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

     

    2-3. ์•ก์„ธ์Šค ๊ถŒํ•œ ๋ถ€์—ฌ

     

    ์—ญํ• ์€ ํ•„์ˆ˜๋กœ ์„œ๋น„์Šค ๊ณ„์ • ์‚ฌ์šฉ์ž๋กœ ํ•ด์ฃผ์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค.

     

    3-3. ์‚ฌ์šฉ์ž ์•ก์„ธ์Šค ๊ถŒํ•œ ๋ถ€์—ฌ

     

    ํ•ด๋‹น ํ•„๋“œ๋“ค์€ ๋น„์›Œ๋‘๊ณ  ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค

     

    3-4. ํ‚ค ์ƒ์„ฑ

     

    ์„œ๋น„์Šค ๊ณ„์ •์ชฝ์„ ํ™•์ธํ•ด๋ณด์‹œ๋ฉด, ๋ฐฉ๊ธˆ ๋งŒ๋“  ๊ณ„์ •์ด ์ƒ์„ฑ๋˜์–ด ์žˆ์„๊ฒ๋‹ˆ๋‹ค. ๊ณ„์ •์„ ๋ˆ„๋ฅด๊ณ  ๋“ค์–ด๊ฐ„ ๋’ค, ํ‚ค ์ถ”๊ฐ€๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ํ‚ค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. JSON ์œผ๋กœ ์„ ํƒํ•˜๊ณ  ๋งŒ๋“ญ๋‹ˆ๋‹ค.

     

    3-5. ํ‚ค ์ €์žฅ ๋ฐ json secret file ์ž…๋ ฅ

    ์ด์ œ ํ‚ค๋ฅผ ์ €์žฅํ•˜๊ณ , Google Play Console ๋กœ ๋Œ์•„๊ฐ€์…”์„œ, ์‚ฌ์šฉ์ž ๋ฐ ๊ถŒํ•œ ์„ ๋ณด์‹œ๋ฉด ์ƒ์„ฑํ•œ ์„œ๋น„์Šค ๊ณ„์ •์ด ์ถ”๊ฐ€ ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•Œ๋งž์€ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•ด์ค๋‹ˆ๋‹ค. 

    ๋ถ€์—ฌํ•œ ๋’ค, ๋ฐ›์€ JSON ํ‚ค๋ฅผ ์•ˆ์ „ํ•œ ๊ณณ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ๋Š” json ํ‚ค๋ฅผ serviceAccount.json ์œผ๋กœ ์ €์žฅํ•˜์˜€๊ณ , ๊ฒฝ๋กœ๋Š” android/app/ ์— ์ €์žฅํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    ์•ฑ ์ฝ”๋“œ ๋‚ด์— ํ‚ค ํŒŒ์ผ์„ ์ €์žฅํ•œ๊ฒŒ git ์— ์˜ฌ๋ผ๊ฐ€๋ฉด ์•ˆ๋˜๋‹ˆ, gitignore ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

    metadata setup ํ• ๊ฑฐ๋ƒ๊ณ  ๋ฌผ์–ด๋ณด๋Š”๋ฐ, ์Šคํ† ์–ด์— ์˜ฌ๋ฆด ๋•Œ ํ•„์š”ํ•œ data ์ด๋ฏ€๋กœ y ํ•ด์ฃผ๊ณ  ๋‹ค์šด๋ฐ›์Šต๋‹ˆ๋‹ค.

    4. Fastlane Firebase Distribution ์„ค์ •

    Fastlane ์„ ์ด์šฉํ•˜์—ฌ Firebase App Distribution ์— ๋ฐฐํฌํ•˜๋ ค๋ฉด ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜, ์ธ์ฆ ๋“ฑ์„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    4-1. Firebase Plugin ์„ค์น˜

    fastlane add_plugin firebase_app_distribution

    Firebase ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•  ๋•Œ Your user account isn't allowed... ๋ผ๋Š” ๋ฉ”์„ธ์ง€๊ฐ€ ๋‚˜์˜ค๋ฉด์„œ Password ๋ฅผ ์ž…๋ ฅํ•˜๋ผ๊ณ  ํ•  ์ˆ˜๋„ ์žˆ๋Š”๋ฐ, Mac password ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.

    4-2. Firebase ๋กœ๊ทธ์ธ

    fastlane run firebase_app_distribution_login

    CLI๋กœ Firebase ์— ๋กœ๊ทธ์ธ ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ปค๋งจ๋“œ ์ž…๋ ฅ ์‹œ ์ ‘์†ํ•  ์ˆ˜ ์žˆ๋Š” ๋งํฌ URL ์ด ํ„ฐ๋ฏธ๋„์— ๋ณด์ผํ…๋ฐ, ์ ‘์†ํ•ด์„œ Firebase ์ธ์ฆ ํ•ด์ฃผ์‹œ๋ฉด Refresh Token ์ด ๋‚ ์•„์˜ต๋‹ˆ๋‹ค. ํ•ด๋‹น ํ† ํฐ์€ ์ž˜ ๋ณด๊ด€ํ•ด์ฃผ์„ธ์š”.

    5. ํ™˜๊ฒฝ๋ณ€์ˆ˜[ENV] ์„ค์ •

    ๊ฐ ๊ฐœ๋ฐœ์ž ๋งˆ๋‹ค Firebase Refresh Token ์ด ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋Š”๋ฐ, Fastlane ์—์„œ๋Š” .envํŒŒ์ผ๋กœ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๊ด€๋ฆฌํ•˜์—ฌ ์„œ๋กœ ๋‹ค๋ฅธ ํ™˜๊ฒฝ์—์„œ๋„ ๋™์ผํ•œ Lane ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    # Appfile
    json_key_file(ENV["JSON_KEY_FILE_PATH"]) # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
    package_name("com.test.test") # e.g. com.krausefx.app
    
    # .env
    JSON_KEY_FILE_PATH="/Users/jangseung-gyun/Documents/GitHub/frontend/android/app/serviceAccount.json"

    Fastlane ํด๋” ์•ˆ์˜ Appfile ์ž…๋‹ˆ๋‹ค. package_name ์€ ์–ด๋А ํ™˜๊ฒฝ์—์„œ๋‚˜ ๋™์ผํ•˜์ง€๋งŒ, json_key_file ๋Š” ๊ฐœ๋ฐœ์ž ๋งˆ๋‹ค ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์น˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

     

    6. Firebase Distribution Lane ์ƒ์„ฑ

    default_platform(:android)
    
    platform :android do  desc "Upload Debug APK to Firebase Distribution"  lane :firebase_debug do |options|
        gradle_build
        firebase_upload(release_note: options[:release_note])
      end  desc "Upload Release APK to Firebase Distribution"  lane :firebase_release do    gradle_build(release: true)
        firebase_upload(release: true)
      end  desc "Get Flutter version from pubspec.yaml"  private_lane :get_version_info do    yaml_file_path = "yaml_file_path์ž…๋ ฅ"    data = YAML.load_file(yaml_file_path)
        version = data["version"]
    
        version_name = version.split("+")[0]
        version_code = version.split("+")[1].to_i
    
        {version_name: version_name, version_code: version_code}
      end  private_lane :gradle_build do |options|
        version = get_version_info
        version_name = version[:version_name]
        version_code = version[:version_code]
    
        gradle(
          task: "assemble",
          build_type: options[:release] ? "Release" : "Debug",
          properties: {
            "android.injected.version.name" => version_name,
            "android.injected.version.code" => version_code
          }
        )
      end  private_lane :firebase_upload do |options|
        firebase_app_distribution(
          app: "์•ฑ id์ด๋ฉฐ Firebase์˜ ํ”„๋กœ์ ํŠธ ์„ค์ • ์ผ๋ฐ˜ ํƒญ์— ์žˆ์Šต๋‹ˆ๋‹ค",
          firebase_cli_token: '4-2์—์„œ ๋ฐ›์€ Refresh Token',
          groups: "test",
          debug: options[:release],
          android_artifact_type: "APK",
          release_notes: options[:release] ? "firebase_upload release" : "firebase_upload debug",
          apk_path: options[:release] ? "app-release.apk๋ฅผ ์ €์žฅํ•  ํŒŒ์ผ ๊ฒฝ๋กœ /Users/jangseung-gyun/Documents/GitHub/frontend/build/app/outputs/flutter-apk/app-release.apk" : "app-debug.apk๋ฅผ ์ €์žฅํ•  ํŒŒ์ผ ๊ฒฝ๋กœ /Users/jangseung-gyun/Documents/GitHub/frontend/build/app/outputs/flutter-apk/app-debug.apk"    )
      end  desc "Lane for distribution"  lane :firebase_aos do      gradle(
              task: "assemble",
              build_type: "Debug",
          )
    
          firebase_app_distribution(
              app: "์•ฑ id์ด๋ฉฐ Firebase์˜ ํ”„๋กœ์ ํŠธ ์„ค์ • ์ผ๋ฐ˜ ํƒญ์— ์žˆ์Šต๋‹ˆ๋‹ค",
              firebase_cli_token: '4-2์—์„œ ๋ฐ›์€ Refresh Token',
              groups: "test",
              debug: true,
              android_artifact_type: "APK",
              release_notes: "fastlane test",
              apk_path: "app-debug.apk๋ฅผ ์ €์žฅํ•  ํŒŒ์ผ ๊ฒฝ๋กœ /Users/jangseung-gyun/Documents/GitHub/frontend/build/app/outputs/flutter-apk/app-debug.apk"      )
      endend

    [lane ์„ค๋ช…]

    firebase_debug ๋ ˆ์ธ์€ debug๋ชจ๋“œ๋กœ Firebase์— ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค(Firebase์— firebase_upload debug๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค)

    firebase_release๋ ˆ์ธ์€ release๋ชจ๋“œ๋กœ Firebase์— ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค (Firebase์— firebase_upload release๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค)

    ๋Œ“๊ธ€

Designed by Tistory.