Bleeding edge

github action과 electron-builder를 이용하여 mac os dmg 배포하기 본문

Javascript/Electron

github action과 electron-builder를 이용하여 mac os dmg 배포하기

codevil 2024. 3. 18. 18:02

 

아 malware 아닙니다 구글형님.

이 글은 이 지긋지긋한 에러를 정리하며, mac os로 빌드를 하는 다른분을 위해 글을 작성했습니다.

 

간단한 상황 설명

기본적인 workflow를 이해하고 있으면 쉽게 사용할 수 있었던, win os와는 다르게 mac os는 배포를 할 때 준비해야할 것이 많았다. 물론 win os에서도 굳이 코드 사이닝을 진행했다면 똑같은 난이도 였겠지만, 연간 구독비가 없게 진행을 해보자라는 말도 안되는 꿈을 가지고 시작하여서,  github action으로 배포하더라도 문제가 없던 윈도우처럼 맥도 문제가 없지 않을까? 라는 생각을 가지고 코드 사이닝을 제외하고 시작하였다.

 

일단 코드 사이닝을 제외한다 라는 것에 대한 결론

안된다. 목표가 내 레포의 tag에서 원하는 os의 어플리케이션을 받도록 만들려는게 목표였고, mac os에서 별다른 설정 없이 사용할 수 있게 만드는게 목표였기 때문에 코드 사이닝은 필수였다.

- 물론, 허가되지 않은 프로그램 실행과 같이.. verify가 안된 developer의 어플리케이션을 실행가능한 설정을 사용하면 된다. 내가 원하지 않았을 뿐..

 

이 글에 들어가기에 앞서

mac os에 workflow에 대한 설명을 생략할까 하다가 내가 설정한 간단한 workflow파일과 설명을 넣는 것이 좋을 것 같아서 그 부분부터 설명을 하려고한다.

 

1. Workflow

1. 언제 작동하게할까?

아무래도 어플리케이션 특성상 백업이 쉽게 tag를 이용한 이정표가 필요하고, 메인 브랜치를 기준으로 업데이트를 하면서 진행을 할 예정이다보니, tag를 기준으로 작동하게 작성하였다.

2. 어떤 os

내가 linux를 사용하지 않다보니 qa도 힘들 것 같고, 확인도 힘들 것 같으니 mac os와 window os에서 빌드를 하도록 만들기로 했다.

name: Release

on:
  push:
    tags:
      - "v*"

jobs:
  release:
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [windows-latest, macos-latest]

    steps:
      - name: Check out Git repository
        uses: actions/checkout@v1

      - name: Install Node.js, NPM and Yarn
        uses: actions/setup-node@v1
        with:
          node-version: 19

      - name: Build/release Electron app
        uses: samuelmeuli/action-electron-builder@v1
        with:
          github_token: ${{ secrets.github_token }}
          release: ${{ startsWith(github.ref, 'refs/tags/v') }}

on push 보면 tags v 이용하여 태그, strategy.matrix.os 이용하여 작동하는 os 선택하였다.

잠깐 주의사항!

github_token에 대한 것은 굳이 내가 레포에서 secrets에 입력을 하지 않아도 작동을 하고, 저 값이 없으면 에러가 나올테니 지우지 말자.

 

2. 코드 사이닝

코드 사이닝을 하려면 우선, certification을 만들어야한다. 정말 많은 글에서 apple developer에서 받아야한다고 해서 그 방법을 시도하면서 얼마나 시간을 날렸는지 모르겠다. apple developer로 만들어서 진행하다보면, 아마 verify가 안된 인증서라고 나올 것이다. 

 

2-1 XCODE

설치가 안되어있다면 app store에서 xcode를 설치하자. (나중에 앱 만들 때도 필요하니 설치가 안된분은 트라이!!) xcode를 켜고

 

Settings > Manage Certificates를 누르자

 

 

그리고 Apple Development > Export Certicate를 누르자.

 

지금 이 암호는 아주 요긴하게 사용될 예정이니 잘 기억하도록하자. 이제 save as 를 사용하면 p12라는 파일이 만들어진다. 그러면 이 파일을 base64로 변형하자. (업로드를 하는 방법이 있지만, 이 방법이 더 편해보이니 이 방법을 사용하자)

base64 -i certs.p12 -o encoded.txt

여기서 사용된 certs.p12는 위에서 Save as로 저장한 <파일이름>.<확장자> 이고 encoded.txt는 base64로 변환된 텍스트를 볼 수 있다.

 

2-2 git secrets and variables

이제 git actions에서 위에서 저장한 base64 값과, 그 암호를 저장하기 위해 깃허브로 이동하자.

맨 아래에 Secrets and variables > Actions로 들어가면 Git actions에서 사용할 수 있는 변수들을 업데이트할 수 있다. 다른 이름으로 해도 전혀 지장이 없지만, 나 같은 경우는 Samuel Meuli가 쓴 글을 참조하여 레퍼런스를 삼았기 때문에 이 글에 있는 변수를 따오겠다.

MAC_CERTS : <base64 파일에 저장된 텍스트내용을 저장>

MAC_CERTS_PASSWORD : <base64 파일을 저장하면서 입력한 비밀번호>

 

2-3 Workflow 

이제 코드 사이닝에 필요한 값은 모두 준비하였으니 워크 플로우에 코드 사이닝을 추가하자

name: Release

on:
  push:
    tags:
      - "v*"

jobs:
  release:
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [windows-latest, macos-latest]

    steps:
      - name: Check out Git repository
        uses: actions/checkout@v1

      - name: Install Node.js, NPM and Yarn
        uses: actions/setup-node@v1
        with:
          node-version: 19
    
      - name: Build/release Electron app
        uses: samuelmeuli/action-electron-builder@v1
        with:
          # GitHub token, automatically provided to the action
          # (No need to define this secret in the repo settings)
          github_token: ${{ secrets.github_token }}
          mac_certs: ${{ secrets.MAC_CERTS }}
          mac_certs_password: ${{ secrets.MAC_CERTS_PASSWORD }}
          release: ${{ startsWith(github.ref, 'refs/tags/v') }}

 

이제 워크 플로우에서는 더 이상 이 텍스트를 안볼 수 있다.

 

하지만 다음친구가 등장한다.

또 다른 에러라뇨 센세!

 

사실 이 에러를 보니 다른 app_id라 던지 이슈 같은 변수가 예시 workflow에 있었는지를 알 수 있었다.

 

2. APP에 대한 설정

2-1 App store Connect API

https://appstoreconnect.apple.com/access/users 이 링크에 들어가자

Users and Access > Integrations > App Store Connect API

여기서 새로운 API Key를 생성하자. 

생성을 하고나면 관련된 파일을 Download를 받을 수 있다. 이 파일을 app_id라고 부르겠다. 이 파일은 다운로드를 받고 나서 나중에 못받을 수 있으니 주의하자 (밑에 파일을 보면 다운로드가 막혔다는 것을 알 수 있다. 그리고 각 column에 맞는 Key_id와 맨 위의 Issuer_Id를 저장하자

 

2-2 git secrets and variables

API_KEY

API_KEY_ID

API_KEY_ISSUER_ID

위에서 구한 파일을 각각 1-2처럼 Git action의 변수에 넣자. API_KEY는 vs code로 열고 내용을 붙여넣자!(바로 안열리는 경우가 있어서 vs code로 열자고 남겼습니다)

 

2-3 Workflow

name: Release

on:
  push:
    tags:
      - "v*"

jobs:
  release:
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [windows-latest, macos-latest]

    steps:
      - name: Check out Git repository
        uses: actions/checkout@v1

      - name: Install Node.js, NPM and Yarn
        uses: actions/setup-node@v1
        with:
          node-version: 19
      - name: Prepare for app notarization (macOS)
        if: startsWith(matrix.os, 'macos')
        # Import Apple API key for app notarization on macOS
        run: |
          mkdir -p ~/private_keys/
          echo '${{ secrets.API_KEY }}' > ~/private_keys/AuthKey_${{ secrets.API_KEY_ID }}.p8
      
      - name: Build/release Electron app
        uses: samuelmeuli/action-electron-builder@v1
        with:
          # GitHub token, automatically provided to the action
          # (No need to define this secret in the repo settings)
          github_token: ${{ secrets.github_token }}
          mac_certs: ${{ secrets.MAC_CERTS }}
          mac_certs_password: ${{ secrets.MAC_CERTS_PASSWORD }}
          release: ${{ startsWith(github.ref, 'refs/tags/v') }}
        env:
          # macOS notarization API key
          API_KEY_ID: ${{ secrets.API_KEY_ID }}
          API_KEY_ISSUER_ID: ${{ secrets.API_KEY_ISSUER_ID }}

이제 workflow는 끝났다. 단지, appId를 넣지 않아서 에러가 나올뿐..

 

3 package.json  + 기타 등등

3-1 AppId 생성

이제 거의 마무리에 왔다. 우선 appId를 넣기 위해 apple developer로 이동하자.

https://developer.apple.com/account/resources/identifiers/list

 

Certificates, Identifiers & Profiles > + >  App IDS > App을 선택후 Continue

 

이제 요 Bundle Id에서 사용될 주소가 AppId가 되시겠다~ Bundle Id는 보통 com.<그룹>.<상세> 이런식으로 이름을 짓는다. 예를들면 네이버의 스노우라고 하면 com.naver.snow 이런식으로..?

3-2  entitlements.mac.plist

entitlements.mac.plist파일을 resource가 있는 곳(build 폴더나, resource폴더와 같이 assets을 모아서 관리해도 좋다)에 파일을 만들고 다음과 같은 내용을 저장하자.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>com.apple.security.cs.allow-jit</key>
		<true/>
		<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
		<true/>
		<key>com.apple.security.cs.allow-dyld-environment-variables</key>
		<true/>
	</dict>
</plist>

 

3-3 package.json

{
	//...
	"appId": "com.category.detail",
    "mac": {
      "hardenedRuntime": true,
      "gatekeeperAssess": false,
      "entitlements": "./resources/entitlements.mac.plist",
      "entitlementsInherit": "./resources/entitlements.mac.plist",
      "target": [
        {
          "target": "dmg",
          "arch": [
            "universal"
          ]
        }
      ]
    },
 }

이제 다음과 같은 내용을 넣으면 끝이다. 참고로 entitlements에 대한 폴더 주소는 임의로 정한 주소이니 본인이 선호하는 링크로 넣는 것을 추천한다. 하지만 꼭 지켜야할 것은 entitlements와 entitlementsInherit 둘 다 꼭 넣어야한다. 둘중 하나라도 빠지는 순간 github actions는 계속 빙빙 돈다. 어떤 사람은 5시간을 돌렸다는 글도 봤다. (필자는 entitlements만 넣고 20분 걸려서 취소하고 또 돌리고를 2번 반복했다)

 

마무리

다음날이 과제테스트가 있지만, 글도 나중에 지나면 쓰기 싫어지니까 후다닥 작성했다. +ㅆ+ 이번에 mac과 win둘 다 올렸으니 이제 주변사람들에게 뿌려서 공유할 생각에 절로 신난다 두둠칫!