본문 바로가기
Flutter CICD

GitHub Actions으로 Flutter CI/CD 구축하기 - workflow 설정 1

by 6cess 2024. 1. 6.

GiHub Actions으로 Flutter CI/CD 구축하기 - GitHub Secret 설정

 

전편에서 깃허브 액션 내에서 활용할 파일들이나 데이터들을 GitHub Secret에 등록하는 작업을 했다.

이제 깃허브 액션이 제공해주는 가상환경에서 이 secret을 활용하여 Workflow를 구성하는 작업을 진행한다.

 

우선 나의 프로젝트 상황에서는 두개의  workflow가 필요하다.

 

1. 풀리퀘스트 시 빌드 번호 업데이트, 빌드 그리고 배포 (develop, stage, release 브랜치 각각 작성)

2. develop, stage, release 브랜치에서 풀리퀘스트 시 develop, stage, release 브랜치 모두의 빌드 번호 업데이트

 

이번 글에서는 먼저 1번(빌드 및 배포 워크플로우)에 대해 기록한다

 

 

1, .github/workflows 디렉토리에 yml 파일 생성

해당 브랜치에서 파일을 직접 생성하거나 혹은 Actions탭의 안내 링크를 클릭하여 yml 파일을 생성한다.

직접 생성하거나

 

Actions탭 페이지에서 set up a workflow yourself 클릭

2, workflow의 이름과 Trigger 설정

# workflow의 이름 : develop 환경의 CICD
name: dev CICD

# workflow의 trigger 설정
on:
  # Trogger 조건 : develop 브랜치 pull request할 경우
  pull_request:
    branches: [ "develop" ]

2-1, 배포 환경 별 트리거

Develop 브랜치에 pull_request 할 경우 빌드 및 배포 진행

on:
  pull_request:
    branches: [ "develop" ]

 

3, job 이름과 runner(실행환경) 설정

...

on:
  # Trogger 조건 : develop 브랜치 pull request할 경우
  pull_request:
    branches: [ "develop" ]

jobs:
  # job 명명
  build and deploy:

    # job 실행환경 : github 제공 가상 macos
    runs-on: macos-latest

3-1, job 실행 환경 선택 기준

CI/CD를 구성할 때 본인이 소유한 실제 기기 환경에서 작업이 실행되게 할 수도 있고 깃허브에서 제공하는 환경에서 진행할 수도 있다.

  • 실제 소유한 기기 환경 : 현재 프로젝트 코딩 작업을 하는 기기에서 진행하게 되면 java, flutter sdk 등을 매번 설치할 필요가 없어 시간을 절약할 수 있으나 해당 기기가 일정 시간 이상 오프라인일 경우 실행 목록에서 제외될 수 있는 단점이 있다.
  • GitHub 제공 가상 환경 : 매번 java, flutter sdk 를 설치해야 하기 때문에 빌드 배포시간이 좀 더 소요된다.

시간이 좀 더 걸리더라도 특정 기기나 팀원에 한정되지 않는 CI/CD 환경을 위해 가상 환경으로 선택

 

4, steps 설정 - 1 : 깃허브 액션 브랜치로 체크아웃

...

    # job 실행환경 : github 제공 가상 macos
    runs-on: macos-latest

    steps:
    	# 프로젝트 브랜치를 github actions 브랜치로 체크아웃하여 올리는 작업
      - name: Checkout
        uses: actions/checkout@v3

5, steps 설정 - 2 : 현재 버전 빌드 번호 추출하여 +1 한 값 변수로 선언

      - name: 현재 빌드 번호 추출하여 +1을 한 후 $GITHUB_ENV 환경변수로 등록
        run: |
          version=$(grep 'version:' pubspec.yaml | awk '{print $2}')
          echo "Current version: $version"
          IFS='+' read -r base_version build_number <<< "$version"
          build_number=$((build_number + 1))
          new_version="${base_version}+${build_number}"
          echo "New version: $new_version"
          echo "NEW_VERSION=$new_version" >> $GITHUB_ENV

6, steps 설정 - 3 : ios 빌드 설정 파일 복호화 및 설치

...

    steps:
    	# 프로젝트 브랜치를 github actions 브랜치로 체크아웃하여 올리는 작업
      - name: Checkout
        uses: actions/checkout@v3

      - name: 🔐 ios 빌드 관련 파일 설치
        # 깃허브 시크릿에 등록한 값 불러오기
        env:
          P12_DISTRIBUTION_CERTIFICATE_BASE64: "${{ secrets.IOS_P12_DISTRIBUTION_CERTIFICATE_BASE64 }}"
          P12_DISTRIBUTION_CERTIFICATE_PASSWORD: "${{ secrets.IOS_P12_DISTRIBUTION_CERTIFICATE_PASSWORD }}"
          DISTRIBUTION_PROVISIONING_PROFILE_BASE64: "${{ secrets.IOS_DISTRIBUTION_PROVISIONING_PROFILE_BASE64 }}"
          LOCAL_NOTI_PROVISIONING_PROFILE_BASE64: "${{ secrets.IOS_LOCAL_NOTIFICATION_PROFILE_BASE64 }}"
          KEYCHAIN_PASSWORD: "${{ secrets.IOS_RUNNER_LOCAL_KEYCHAIN_PASSWORD }}"
          EXPORT_OPTIONS_BASE64: "${{ secrets.IOS_EXPORT_OPTIONS_BASE64 }}"
        run: |
          # 경로를 가리키는 환경 변수 선언
          CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
          PROVISIONING_PROFILE_PATH=$RUNNER_TEMP/ios_dist_pp.mobileprovision
          LOCAL_NOTIFICATION_PROFILE_PATH=$RUNNER_TEMP/ios_dist_noti_pp.mobileprovision
          KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
          EXPORT_OPTIONS_PATH="${{ github.workspace }}/ios/Runner/ExportOptions.plist"

          # base64 복호화 된 파일 생성
          echo -n "$P12_DISTRIBUTION_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
          echo -n "$DISTRIBUTION_PROVISIONING_PROFILE_BASE64" | base64 --decode -o $PROVISIONING_PROFILE_PATH
          echo -n "$LOCAL_NOTI_PROVISIONING_PROFILE_BASE64" | base64 --decode -o $LOCAL_NOTIFICATION_PROFILE_PATH
          echo -n "$EXPORT_OPTIONS_BASE64" | base64 --decode -o $EXPORT_OPTIONS_PATH

          # 임시 키체인 생성 Certificate import
          security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
          security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
          security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
          security import $CERTIFICATE_PATH -P "$P12_DISTRIBUTION_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security list-keychain -d user -s $KEYCHAIN_PATH

          # provisioning profile을 xcode가 인식할 수 있는 디렉토리에 복사
          mkdir -p ~/Library/MobileDevice/Provisioning\\ Profiles
          cp $PROVISIONING_PROFILE_PATH ~/Library/MobileDevice/Provisioning\\ Profiles
          cp $LOCAL_NOTIFICATION_PROFILE_PATH ~/Library/MobileDevice/Provisioning\\ Profiles

7, steps 설정 - 4 : android 빌드 설정 파일 복호화 및 설치

    steps:

        ...

        - name: 🔐 android 빌드 관련 파일 설치
          run: |
            #debug용 keystore 복호화하여 xcode가 인식하는 위치에 생성
            echo "${{ secrets.ANDROID_DEBUG_KEYSTORE_BASE64 }}" | base64 --decode > $HOME/.android/debug.keystore
            #실제 플레이스토어 배포할 경우 release용 jks 파일 또한 복호화 필요(release 브랜치 yml 파일에만 넣으면 됨)
            echo "${{ secrets.ANDROID_RELEASE_KEYSTORE_BASE64 }}" | base64 --decode > "${{ github.workspace }}/android/keystore/key.jks"

8, steps 설정 - 5 : SDK 설치 및 flutter pub get 실행

    	# SDK 설치를 지원하는 모듈 같은 개념
      - uses: actions/setup-java@v2
        with:
        # 프로젝트 JAVA 버전 맞춰서
          java-version: '11'
          distribution: 'adopt'
			
        # SDK 설치를 지원하는 모듈 같은 개념
      - uses: subosito/flutter-action@v2.3.0
        with:
        # 프로젝트 Flutter 버전 맞춰서
          flutter-version: '3.3.1'
			
      - name: Install packages
        run: flutter pub get

9, steps 설정 - 6 : build ipa 및 테스트 플라잇 배포

      - name: Build ipa
        run: |
          flutter build ipa \
            --release \
            # flavor로 빌드 환경을 관리하는 경우 
            --flavor dev \
            --export-options-plist=ios/Runner/ExportOptions.plist
            #이전에 추출했던 ExportOptions.plist의 설정대로 certificate, profile파일 사용
      
      - name: 사용할 app store connect api 키 가져오기
        env:
          PRIVATE_API_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_PRIVATE_KEY_BASE64 }}
          API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
        run: | 
          mkdir -p ~/private_keys
          echo -n "$PRIVATE_API_KEY_BASE64" | base64 --decode --output ~/private_keys/AuthKey_$API_KEY.p8

      - name: TestFlight에 업로드
        env:
          API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
          API_ISSUER : ${{ secrets.APP_STORE_CONNECT_ISSUER_ID  }}
        #자신의 프로젝트 빌드 결과물 경로와 프로젝트명으로 수정
        run: xcrun altool --output-format xml --upload-app -f ${{ github.workspace }}/build/ios/ipa/pjname.ipa -t ios --apiKey $API_KEY --apiIssuer $API_ISSUER

10, steps 설정 - 7 : build apk 및 구글 드라이브 업로드

      - name: Build apk
        run: |
          flutter build apk \\
            --release \\
            # flavor로 빌드 환경을 관리하는 경우 
            --flavor dev
		
      #apk 파일명은 입맛에 맞게
      - name: 오늘 날짜에 따라 apk 파일명 설정
        run: echo "TODAYS_DATE=$(date +%y%m%d)" >> $GITHUB_ENV

      - name: Google Drive에 apk 파일 업로드
        uses: willo32/google-drive-upload-action@v1
        with:
          credentials: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_CREDENTIALS }}
          target: ${{ github.workspace }}/build/app/outputs/flutter-apk/app-dev-release.apk
          parent_folder_id: ${{ secrets.GOOGLE_DRIVE_DEV_FOLDER_ID }}
          name: dev_${{ env.TODAYS_DATE }}.apk

 

다음에는 브랜치 간 버전 업데이트 및 동기화 워크플로우에 대해 작성하겠다