ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Github action IOS] CI/CD Testflight 자동화 배포 하기
    개발/Swift 2022. 5. 19. 12:44

     

     

    [iOS] Swift Modern Collection View & MVVM 패턴 가이드 강의 - 인프런

    MVVM 패턴과 Modern Collection View를 사용해 네트워킹을 구현하고, 다양하고 동적인 Collection View를 자유자재로 다룰 수 있게 됩니다., Swift iOS UI, 제대로 다루는 핵심 기술! 📲 iOS Swift 레이아웃 구현을

    www.inflearn.com

    테스트플라이트 배포까지 여러 정보들을 필요로한다.

    • Certification & Provisioning profile 암호화 파일
    • Certification & Provisioning profile 암호 비밀번호
    • Certification 비밀번호
    • ExportOptions.plist
    • Appstore issuer id
    • Appstore api key
    • Appstore api 프라이빗키

    하나하나 준비하여 세팅해보도록 하자.

    Certification & Provisioning profile 암호화 파일 , Certification 비밀번호, Certification & Provisioning profile 암호 비밀번호

    Certification 은 키체인에서 찾아 내보내기해야한다. 내보내기 위해서 login - My Certificates로 가준다.

    내보내기를 할때 Certification 비밀번호를 입력하게 될것이다.

    해당 비밀번호를 저장해두자

     

    Provisioning profile 은 developer.apple.com 에가서

    해당 앱이 사용중인 profile을 다운 받으면 된다.

     

    이렇게 Certification & Provisioning profile 두개가 준비되었다.

    이 두개를 암호화 해서 git 을통해 원격으로 보낼것이다.

    암호화를 하기 위해서는 gnupg라는것을 사용하게 된다.

    brew 를 통해 인스톨 한다

    brew install gnupg2
    

    인스톨이후에 암호화를 하려면

    gpg -c certification파일명.p12
    gpg -c profile파일명.mobileprovision
    

    을통해 암호화를 하게 된다. 그러면 passphrase를 입력하게 되는데 (Certification & Provisioning profile 암호 풀 비밀번호) 추후에 필요하므로 그것을 저장해두자.

    암호화가 끝나면 .gpg로 끝나는 파일이 각각 생성된다.

    certification파일명.p12.gpg

    profile파일명.mobileprovision.gpg

    이 두 파일을 프로젝트폴더/.github/secrets/ 안에 넣어준 후 git push 를 하여 저장소에 넣어줘야한다.

    이렇게 Certification & Provisioning profile 암호화 파일 , Certification 비밀번호, Certification & Provisioning profile 암호 비밀번호 세가지가 준비되었다.

    ExportOptions.plist

    해당 파일은 archive 파일을 가지고 ipa 파일을 만들때 사용된다.

    ipa파일은 testflight 배포를 위해 필요하다.

    ExportOptions.plist 파일을 얻기 위해서는 수동으로 한번 작업을 해야한다.

    xcode를 통해 아카이브를 한후 window→organizer→DistributeApp 을 누른후

    App store connect → Export 를 누르면 ExportOptions.plist 파일이 생성된다.

    해당 파일을 git 을통해 push 하여 올려주면 된다. 경로는 프로젝트 루트경로(workspace와 동일path)에 넣어준다.

    Appstore issuer id , Appstore api key , Appstore api 프라이빗키

    앱스토어 관련 정보는 마지막 testflight배포를 하기 위해 필요하다. 위 정보를 얻기위해

    appstoreconnect로 간다.

    사용자및 액세스 → 키로 이동하면 키를 만들수있다.

    원하는 이름과 액세스를 지정해준다

    그러면 private key를 단 한번 볼수있다. 이떄 이 private 키를 저장해두자.

    그리고 issuer id 와 api key 를 볼수있다.

    Secrets

    이렇게 모든 정보들이 준비가 되었다. 이 정보들중 몇가지들을 repository(배포할) secrets 에 넣어줘야한다.

    secrests 은 repository setting → Secrets → actions 에가서 Newrepository secret 을 눌러 생성해준다.

    원하는 키값을 넣어주고 값을 넣어준다.

    이렇게 총 6가지가 나오는데 PERSONAL_ACCESS_TOKEN 은 내 프로젝트에서 private pod 을 의존하고 있기 때문에 필요하다. private pod 을 install 하기위해서는 PAT가 필요하다. 만약 private repo를 쓰고 있지 않다면 필요없다.

    Workflow 작성

    이제 마지막으로 workflow를 작성해보자.

    workflow는 action - new workflow에서 생성 가능하다.

    on:
      push:
        branches: [ release ]
    

    처음으로 브랜치 설정을 해주는데 위와 같이 지정해주면

    release 브랜치로 push 되는 순간 action이 발생된다. 할 작업이 testflight 배포이므로

    release 브랜치로 지정해줬다.

    jobs:
      build:
    
        runs-on: macos-latest
        env:
          XC_PROJECT: ${{ '프로젝트명.xcworkspace' }}
          XC_SCHEME: ${{ '프로젝트명' }}
          XC_CONFIGURATION: ${{ 'Automation' }}
          XC_ARCHIVE_PATH: ${{ '프로젝트명.xcarchive' }}
          XC_EXPORT_PATH: ${{ './artifacts' }}
          KEYCHAIN: ${{ 'temp.keychain' }}
          ENCRYPTED_CERTS_FILE_PATH: ${{ '.github/secrets/Certification.p12.gpg' }}
          DECRYPTED_CERTS_FILE_PATH: ${{ '.github/secrets/Certification.p12' }}
          ENCRYPTED_PROVISION_FILE_PATH: ${{ '.github/secrets/Profile명.mobileprovision.gpg' }}
          DECRYPTED_PROVISION_FILE_PATH: ${{ '.github/secrets/Profile명.mobileprovision' }} 
          SIGNING_DECODE_PWD: ${{ secrets.SIGNING_DECODE_PWD }}       
          CERTS_PWD: ${{ secrets.CERTS_PWD }}
          PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }}
    
    

    env 설정을 해주자. 아까 secrets에서 적었던 것들을 여기에 지정해주면 된다.

      SIGNING_DECODE_PWD: ${{ secrets.SIGNING_DECODE_PWD }}       
      CERTS_PWD: ${{ secrets.CERTS_PWD }}
      PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }}
    
    

    간단히 secrets.KEY 를 통해 아까 지정한 secrets value를 받아올수 있다.

    XC_PROJECT: ${{ '프로젝트명.xcworkspace' }}
    XC_SCHEME: ${{ '프로젝트명' }}
    XC_CONFIGURATION: ${{ 'release' }}
    XC_ARCHIVE_PATH: ${{ '프로젝트명.xcarchive' }}
    

    본인 프로젝트 명에 맞게 지정해준다. 의존하는 pod 이 있다면 project가 아닌 workspace를 써야한다

    XC_CONFIGURATION 은 아카이브할때 build configuration을 의미한다. release 를 지정해주면 된다.

    KEYCHAIN: ${{ 'temp.keychain' }}
    ENCRYPTED_CERTS_FILE_PATH: ${{ '.github/secrets/Certification.p12.gpg' }}
    DECRYPTED_CERTS_FILE_PATH: ${{ '.github/secrets/Certification.p12' }}
    ENCRYPTED_PROVISION_FILE_PATH: ${{ '.github/secrets/Profile명.mobileprovision.gpg' }}
    DECRYPTED_PROVISION_FILE_PATH: ${{ '.github/secrets/Profile명.mobileprovision' }} 
    
    

    아카이브 전에 git action 이 돌고있는 가상환경 안에서 키체인 설정을 해줘야 하는데 그때 사용될 키 체인이 필요하다. 이름은 자유롭게 지어도 된다.

     

    아까 암호화하여 git에 올려주었던 Certificate & Profile 파일들의 경로를 ENCRYPTED_CERTS_FILE_PATH , ENCRYPTED_PROVISION_FILE_PATH 에 넣어줬다.

    그리고 복호화 될때 파일명을 미리 적어두었다

    DECRYPTED_CERTS_FILE_PATH , DECRYPTED_PROVISION_FILE_PATH

     

    앱스토어 관련한 secrets는 env 세팅없이 직접사용해줄것이다.

     

    steps에서는 이제 실제 작업할 내용들을 적어두게 된다.

    단계별로

    • checkout
    • credential setup
    • pod install
    • create keychain
    • code signing
    • archive
    • export
    • testflight

    로 나뉘어진다.

    checkout & credential setup & pod install

    steps:
        - name: "Checkout" 
          uses: actions/checkout@v3
        - name: :"Credentials setup"
          run: |  
            git config --global url."".insteadOf "<https://github.com/>"
    		- name: "Pod install"
          run: |
            pod install --repo-update --clean-install
    

    actions/checkout@v3 브랜치로 checkout을 한다. 이것은 디폴트로 지정되있는것을 쓰면 될것이다.

    Credentials setup 에서는 url 세팅을 해줬는데

    pod install 을 할때 private repo 를 의존하는경우 접근 권한이 필요 하지만 git action 이 돌아가는 환경에서는 내 git 계정의 정보가 저장되어있지 않기 때문에 접근이 불가능 하다

    이를 해결하기 위해서는 PAT를 주소에 넣어주는방법이 있다

    따라서 podfile 에서 지정된 private repo url이 https://github.com/PrivateRepo.git 이라면

    나중에 pod install 이 될때 해당 주소를

    https://PERSONAL_ACCESS_TOKEN@github.com/PrivateRepo.git 으로 변경해주는 설정을 넣어 둔것이다.

    Keychain

    - name: "Keychain"
          run: |
            security create-keychain -p "" "$KEYCHAIN" 
            security list-keychains -s "$KEYCHAIN" 
            security default-keychain -s "$KEYCHAIN" 
            security unlock-keychain -p "" "$KEYCHAIN"
            security set-keychain-settings -lut 1200
            security list-keychains
    

    여기서는 keychain을 아까 지정한 이름대로 만들어준다.

    여기서 주의할점이 하나있는데 바로 unlock 이다.

      security unlock-keychain -p "" "$KEYCHAIN"
    

    keychain이 unlock 이 되어있어야 이후 아카이브할때

    코드사이닝을 성공할수있다. 만약 lock이 되어있다면 Archive 가 끝나지 않고 계속 돌게된다.

    기본적으로 unlock을 하면 300초를 유지시켜주고 이후에는 다시 lock이 걸린다.

    만약 unlock 이후에 300초가 지났다면 lock이 걸리고 그 이후에는 아카이브에 실패할것이다.

    그래서 만약 archive 작업이 오래걸린다면 unlock 유지 시간을 늘려야한다

      security set-keychain-settings -lut 1200
    
    

    위와 같이 지정해줘서 원하는 유지시간으로 늘릴수 있다.

    code signing

    - name: "Configure Code Signing"
          run: |
            gpg -d -o "$DECRYPTED_CERTS_FILE_PATH" --pinentry-mode=loopback --passphrase "$SIGNING_DECODE_PWD" "$ENCRYPTED_CERTS_FILE_PATH"
            gpg -d -o "$DECRYPTED_PROVISION_FILE_PATH" --pinentry-mode=loopback --passphrase "$SIGNING_DECODE_PWD" "$ENCRYPTED_PROVISION_FILE_PATH"
            security import "$DECRYPTED_CERTS_FILE_PATH" -k "$KEYCHAIN" -P "$CERTS_PWD" -A
            security set-key-partition-list -S apple-tool:,apple: -s -k "" "$KEYCHAIN"
            mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
            echo `ls .github/secrets/*.mobileprovision`
            for PROVISION in `ls .github/secrets/*.mobileprovision`
              do
                UUID=`/usr/libexec/PlistBuddy -c 'Print :UUID' /dev/stdin <<< $(security cms -D -i ./$PROVISION)`
              cp "./$PROVISION" "$HOME/Library/MobileDevice/Provisioning Profiles/$UUID.mobileprovision"
            done
    

    가장먼저 암호화 해둔 파일들을 가져와서 복호하 하는 작업을한다.

    gpg -d -o "$DECRYPTED_CERTS_FILE_PATH" --pinentry-mode=loopback --passphrase "$SIGNING_DECODE_PWD" "$ENCRYPTED_CERTS_FILE_PATH"
    gpg -d -o "$DECRYPTED_PROVISION_FILE_PATH" --pinentry-mode=loopback --passphrase "$SIGNING_DECODE_PWD" "$ENCRYPTED_PROVISION_FILE_PATH"
    

    이후에는 Certification을 키체인에 import 한다. set-key-partion-list 를 하게되면 접근권한 허용 dialog가 뜨지 않게하기위해 필요하다 한다.

    security import "$DECRYPTED_CERTS_FILE_PATH" -k "$KEYCHAIN" -P "$CERTS_PWD" -A
    security set-key-partition-list -S apple-tool:,apple: -s -k "" "$KEYCHAIN"
    
     mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
            echo `ls .github/secrets/*.mobileprovision`
            for PROVISION in `ls .github/secrets/*.mobileprovision`
              do
                UUID=`/usr/libexec/PlistBuddy -c 'Print :UUID' /dev/stdin <<< $(security cms -D -i ./$PROVISION)`
              cp "./$PROVISION" "$HOME/Library/MobileDevice/Provisioning Profiles/$UUID.mobileprovision"
            done
    

    마지막으로 Profile 을 가져온후 파일명에 UUID를 넣어주는 작업을 한다.

    usr/Libarary/MobileDevice/ProvisioningProfiles 폴더 경로에 가보면

    실제로 이런 파일이 생성 되고있었음을 확인할수 있었다.

    Archive & Export

    - name: "Archive"
          run: |
            xcodebuild clean archive -workspace "$XC_PROJECT" -scheme "$XC_SCHEME" -configuration "$XC_CONFIGURATION" -archivePath "$XC_ARCHIVE_PATH" "OTHER_CODE_SIGN_FLAGS=--keychain '$KEYCHAIN'"
            mkdir artifacts
            
        - name: "EXPORT"
          run: | 
            xcodebuild -exportArchive -archivePath "$XC_ARCHIVE_PATH" -exportOptionsPlist ExportOptions.plist -exportPath "$XC_EXPORT_PATH"
    
    

    이제 아카이브를 실행하면된다. archive 가 완료되면 artifacts라는 폴더를 만들게 했고

    이후 export 에서 생성되는 파일들을 artifacts 폴더에 넣을것이다

    위에 env 에서 생략했지만 해당 경로를 XC_EXPORT_PATH 에 넣어줬다

    XC_EXPORT_PATH: ${{ './artifacts' }}
    

    archive가 완료 되면 루트 경로에 프로젝트명.xcarchive 파일이 생성된다.

    이파일과 ExportOptions.plist 두개를 사용해서 export 를 진행하게 된다.

    export 후에는 /artifacts/프로젝트명.ipa 파일이 생성된다. 이 ipa 파일을 testflight 에 업로드 하기만 하면된다

    Upload testflight

    - name: "TestFlight"
          uses: apple-actions/upload-testflight-build@v1
          with:
            app-path: ./artifacts/프로젝트명.ipa
            issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }}
            api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }}
            api-private-key: ${{ secrets.APPSTORE_PRIVATE_KEY }}
    

    upload testflight 브랜치를 사용하면 알아서 업로드를 해준다

    이때 with에 ipa 파일과

    secrets에 저장 해두었던 appstore 정보를 넣어주었다.

    키 값에 문제가 없다면 성공적으로 업로드가 될것이다.

     

    버전같은경우는 project.pbxproj 파일에 명시된 버전으로 올라간다.

    따라서 release 푸쉬 하기전 버전세팅을 해두고 푸시하면 될것이다.

    전체 코드

    name: Release
    
    on:
      push:
        branches: [ release/* ]
    
    jobs:
      build:
    
        runs-on: macos-latest
        env:
          PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }}
          XC_PROJECT: ${{ '프로젝트명.xcworkspace' }}
          XC_SCHEME: ${{ '프로젝트명' }}
          XC_CONFIGURATION: ${{ 'release' }}
          XC_ARCHIVE_PATH: ${{ '프로젝트명.xcarchive' }}
          XC_EXPORT_PATH: ${{ './artifacts' }}
          KEYCHAIN: ${{ 'temp.keychain' }}
          ENCRYPTED_CERTS_FILE_PATH: ${{ '.github/secrets/certs.p12.gpg' }}
          DECRYPTED_CERTS_FILE_PATH: ${{ '.github/secrets/certs.p12' }}
          ENCRYPTED_PROVISION_FILE_PATH: ${{ '.github/secrets/Distribution.mobileprovision.gpg' }}
          DECRYPTED_PROVISION_FILE_PATH: ${{ '.github/secrets/Distribution.mobileprovision' }} 
          SIGNING_DECODE_PWD: ${{ secrets.SIGNING_DECODE_PWD }}       
          CERTS_PWD: ${{ secrets.CERTS_PWD }} 
        steps:
        - name: "Checkout" 
          uses: actions/checkout@v3
        - name: :"Credentials setup"
          run: |  
            git config --global url."".insteadOf "<https://github.com/>"
        
            
        - name: "Pod install"
          run: |
            pod install --repo-update --clean-install
        - name: "Keychain"
          run: |
            security create-keychain -p "" "$KEYCHAIN" 
            security list-keychains -s "$KEYCHAIN" 
            security default-keychain -s "$KEYCHAIN" 
            security unlock-keychain -p "" "$KEYCHAIN"
            security set-keychain-settings -lut 1200
            security list-keychains
            
        - name: "Configure Code Signing"
          run: |
            gpg -d -o "$DECRYPTED_CERTS_FILE_PATH" --pinentry-mode=loopback --passphrase "$SIGNING_DECODE_PWD" "$ENCRYPTED_CERTS_FILE_PATH"
            gpg -d -o "$DECRYPTED_PROVISION_FILE_PATH" --pinentry-mode=loopback --passphrase "$SIGNING_DECODE_PWD" "$ENCRYPTED_PROVISION_FILE_PATH"
            security import "$DECRYPTED_CERTS_FILE_PATH" -k "$KEYCHAIN" -P "$CERTS_PWD" -A
            security set-key-partition-list -S apple-tool:,apple: -s -k "" "$KEYCHAIN"
            mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
            echo `ls .github/secrets/*.mobileprovision`
            for PROVISION in `ls .github/secrets/*.mobileprovision`
              do
                UUID=`/usr/libexec/PlistBuddy -c 'Print :UUID' /dev/stdin <<< $(security cms -D -i ./$PROVISION)`
              cp "./$PROVISION" "$HOME/Library/MobileDevice/Provisioning Profiles/$UUID.mobileprovision"
            done
      
        - name: "Archive"
          run: |
            ls
            xcodebuild clean archive -workspace "$XC_PROJECT" -scheme "$XC_SCHEME" -configuration "$XC_CONFIGURATION" -archivePath "$XC_ARCHIVE_PATH" "OTHER_CODE_SIGN_FLAGS=--keychain '$KEYCHAIN'"
            mkdir artifacts
            
        - name: "EXPORT"
          run: | 
            xcodebuild -exportArchive -archivePath "$XC_ARCHIVE_PATH" -exportOptionsPlist ExportOptions.plist -exportPath "$XC_EXPORT_PATH"
            pwd
        - name: "TestFlight"
          uses: apple-actions/upload-testflight-build@v1
          with:
            app-path: ./artifacts/프로젝트명.ipa
            issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }}
            api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }}
            api-private-key: ${{ secrets.APPSTORE_PRIVATE_KEY }}
    

     

    댓글

Designed by Tistory.