๐Ÿ’ป Github Action์œผ๋กœ ๋ฐ์Šคํฌํƒ‘ ์•ฑ์„ ์ž๋™์œผ๋กœ ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ๋ฒ•

Motivation

pyqt6๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ฐœ๋ฐœํ•œ python ๋ฐ์Šคํฌํƒ‘ ์•ฑ์„ ๋ฐฐํฌํ•˜๊ธฐ๊นŒ์ง€ ์„ฑ๊ณตํ–ˆ๋‹ค. pyinstaller๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ํ•˜๋‚˜์˜ ์‹คํ–‰ ํŒŒ์ผ๋กœ ์••์ถ•ํ•˜์—ฌ ๋ฐฐํฌํ–ˆ์ง€๋งŒ, ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ์•Œ์ˆ˜์—†๋Š” ์˜ค๋ฅ˜๋“ค์ด ๋ฐœ์ƒํ–ˆ๊ณ  ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ์œ„ํ•œ ๋ฐฉ๋ฒ•์„ ๋ชจ์ƒ‰ํ–ˆ๋‹ค.

์šฐ์„ , pyinstaller๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ฑ์„ ํŒจํ‚ค์ง•ํ•˜๋Š” ๊ณผ์ •์€ ๋‚˜๋ฆ„์˜ ๋ถˆํŽธํ•จ์ด ๋™๋ฐ˜ํ•œ๋‹ค. window์˜ ๊ฒฝ์šฐ exe ํŒŒ์ผ, macOs์˜ ๊ฒฝ์šฐ app ๋ฒˆ๋“ค๋กœ ์ด๋ฃจ์–ด์ง„๋‹ค. windows์šฉ ์•ฑ์„ ๋นŒ๋“œํ•˜๋ ค๋ฉด windows์—์„œ pyintstaller๋ฅผ ์‹คํ–‰ํ•ด์•ผํ•˜๋ฉฐ, mac์šฉ ์•ฑ์„ ๋นŒ๋“œํ•˜๋ ค๋ฉด mac์—์„œ pyinstaller๋ฅผ ์‹คํ–‰ํ•ด์•ผํ•œ๋‹ค.

์ฆ‰, ๊ฐ os์˜ ์‹คํ–‰ ํ™˜๊ฒฝ์ด ์•„๋‹ˆ๋ฉด ๋นŒ๋“œํ•  ์ˆ˜ ์—†๋‹ค. (ํ…Œ์ŠคํŠธ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค) ๋‚˜๋Š” mac m2๋ฅผ ๋ฉ”์ธ ์ปดํ“จํ„ฐ๋กœ ์“ฐ๊ณ ์žˆ๊ธฐ์— ์œˆ๋„์šฐ ํŒจํ‚ค์ง•์ด ์• ๋งคํ–ˆ๊ณ  ๊ฒฐ๊ตญ, ์˜ˆ์ „์— ์“ฐ๋˜ gram 14์ธ์น˜์งœ๋ฆฌ๋ฅผ ๋“ค๊ณ ๋‹ค๋‹ˆ๋ฉฐ ์•ฑ์„ ๋นŒ๋“œํ•˜๊ณ ๋‹ค๋…”๋‹ค.

ํ…Œ์ŠคํŠธ์˜ ํ•œ๊ณ„๋ฅผ ๋„˜์–ด mac์˜ ๋‹ค์–‘ํ•œ ํ™˜๊ฒฝ(Intel ๊ธฐ๋ฐ˜ ๋“ฑ)์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜๋ฅผ ์žก์ง€ ๋ชปํ•˜๊ฒŒ ๋˜์ž, CI/CD(์ง€์†์  ํ†ตํ•ฉ/๋ฐฐํฌ) ๋ฐฉ๋ฒ•์„ ๋„์ž…ํ•ด์•ผ๊ฒ ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

GitHub Action์„ ์„ ํƒ

ํด๋ผ์šฐ๋“œ ๊ตฌ์ถ•์—๋Š” ์—ฌ๋Ÿฌ ์˜ต์…˜์ด ์žˆ์ง€๋งŒ, github๋ฅผ ์ง€์†์ ์œผ๋กœ ํ™œ์šฉํ•˜๊ณ  ๋ฐฐํฌํ•˜๊ณ ์žˆ๊ธฐ์— GitHub Action์„ ์„ ํƒํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค. GitHub Action์€ CI/CD ๋„๊ตฌ๋กœ public ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ๋ผ๋ฉด ๋ฌด๋ฃŒ๋กœ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค. Linux, Windows, MacOs๋ฅผ ์‹คํ–‰ํ•˜๋Š” ํด๋ผ์šฐ๋“œ ์‹œ์Šคํ…œ์—์„œ ์‹คํ–‰๋  ์›Œํฌํ”Œ๋กœ๋ฅผ ์ •์˜ํ• ์ˆ˜์žˆ๊ณ ,์ด๋Š” ๋‹ค์–‘ํ•œ ํ”Œ๋ ›ํผ์—์„œ ๊ตฌ์ถ•ํ•ด์•ผํ•˜๋Š” ์‚ฌ๋ก€์— ์ ํ•ฉํ•˜๋‹ค. (์ด๊ฒŒ ๋‹น์ตœ ๋ฌด์Šจ๋ง์ด๋ƒ ํ•˜๋ฉด, github ์—์„œ ๊ทธ๋ƒฅ ๊ฐ๊ฐ์˜ os ์ปดํ“จํ„ฐ๋ฅผ ๋นŒ๋ ค์ฃผ์–ด์„œ ์ด๊ฑธ ๋ฐ”ํƒ•์œผ๋กœ ์•ฑ์„ ๋นŒ๋“œํ• ์ˆ˜์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.)


GitHub Action ์‹œ์ž‘ํ•˜๊ธฐ

GItHub Actions๋ฅผ ์‹œ์ž‘ํ•˜๋ ค๋ฉด ์•ฑ ์ €์žฅ์†Œ์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํด๋”๋ฅผ ๋งŒ๋“ ๋‹ค

mkdir -p .github/workflows/

๊ทธ๋ฆฌ๊ณ  build.yml ์— ์ž‘์—…์„ ์ •์˜ํ•˜๋Š” ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•œ๋‹ค. github action ์ด build.yml์„ ์ฝ์–ด์˜ค๋ฉด์„œ ๋นŒ๋“œํ•ด์ค€๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” ์ˆœ์„œ๋Œ€๋กœ ๋นŒ๋“œ ๋ช…๋ น์–ด๋ฅผ ์•Œ๋งž๊ฒŒ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋˜๋Š”๋ฐ, ๋‚˜์˜ ๊ฒฝ์šฐ pyinstaller๋กœ ๊ฐ๊ฐ์˜ os ์—์„œ ํŒŒ์ผ์„ ํŒจํ‚ค์ง•ํ•ด์ฃผ๋„๋ก ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค.

action ์„ ์œ„ํ•ด์„œ ์„ค์ •ํ•ด๋ณด์ž๋ฉด,

  1. push : push ์ด๋ฒคํŠธ์—์„œ ๋‹ค์Œ ์ž‘์—…์ด ์‹คํ–‰๋˜๋„๋ก ์ •์˜ํ•œ๋‹ค. ์ปค๋ฐ‹์— 'v'๋ฅผ ์ ‘๋‘์‚ฌ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ํƒœ๊ทธ๊ฐ€ ์ง€์ •๋œ ๊ฒฝ์šฐ์—๋งŒ ํ•ด๋‹น๋˜๊ฒŒ ํ•ด์ฃผ์—ˆ๋‹ค.
name: Build

on:
  push:
    tags:
      - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
  1. createrrelease : github์—์„œ ์ƒˆ release๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ž‘์—…์„ ๋‹ด๋‹นํ•œ๋‹ค.
jobs:
  createrelease:
    name: Create Release
    runs-on: ubuntu-latest
    steps:
      - name: Create Release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          draft: false
          prerelease: false
      - name: Output Release URL File
        run: echo "${{ steps.create_release.outputs.upload_url }}" > release_url.txt
      - name: Save Release URL File for publish
        uses: actions/upload-artifact@v1
        with:
          name: release_url
          path: release_url.txt
  1. build : createrelease ์ž‘์—…์ด ์™„๋ฃŒ๋œ ํ›„์— ์‹คํ–‰๋œ๋‹ค. strategyํŒŒํŠธ์—์„œ ๋‘๊ฐ€์ง€ ๋ฒ„์ „์„ ์ •์˜ํ• ์ˆ˜์ด์“ด๋ฐ MacOs์™€ WIndows ๋ฒ„์ „์ด๋‹ค. ์ด ๊ณผ์ •๋“ค์€ ์กฐ๊ธˆ๋” ์„ธ๋ถ€์ ์œผ๋กœ ๋ณผ์ˆ˜์žˆ๋Š”๋ฐ,
    1. ๊ฐ๊ฐ์˜ ์‹œ์Šคํ…œ์˜ ๋™์ž‘์„ ์ •์˜ํ•ด์ค€๋‹ค.
    2. actions/checkout@v1 ์ €์žฅ์†Œ๊ฐ€ ํด๋ผ์šฐ๋“œ ์‹œ์Šคํ…œ์— ๋ณต์ œ๋œ๋‹ค.
    3. ์ €์žฅ์†Œ์—์„œ requirements.txt ๋ฅผ ํ†ตํ•ด ์ข…์†์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์ค€๋‹ค.
    4. ํŒจํ‚ค์ง€๋Š” pyinstaller๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑ๋œ๋‹ค.
    5. createrelease์—์„œ ์ž‘์—…ํ•œ realase url์ด ํ‘œํ•จ๋œ ํŒŒ์ผ์„ ๋‹ค์šด๋ฐ›๋Š”๋‹ค
    6. ํŒŒ์ผ์—์„œ release url ์„ ์ถ”์ถœํ•ด ๋ณ€์ˆ˜์— ์ €์žฅํ•œ๋‹ค
    7. ๊ทธ release url ์— ํŒจํ‚ค์ง• ์•ฑ๋“ค์„ ์—…๋กœ๋“œํ•œ๋‹ค.
build:
    name: Build packages
    needs: createrelease
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include:
          - os: macos-latest
            TARGET: macos
            CMD_BUILD: |
              pyinstaller app.spec
              cd dist/ 
              zip -r9 DICOM_Anonymizer.zip DICOM_Anonymizer
              cd ../
            OUT_FILE_NAME: DICOM_Anonymizer.zip
            ASSET_MIME: application/zip
          - os: windows-latest
            TARGET: windows
            CMD_BUILD: pyinstaller app.spec
            OUT_FILE_NAME: DICOM_Anonymizer.exe
            ASSET_MIME: application/x-msdownload

    steps:
      - uses: actions/checkout@v1
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: 3.12
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      - name: Build with pyinstaller for ${{ matrix.TARGET }}
        run: ${{ matrix.CMD_BUILD }}
      - name: Download Release URL File
        uses: actions/download-artifact@v1
        with:
          name: release_url
      - name: Get Release File Name & Upload URL
        id: get_release_info
        shell: bash
        run: echo ::set-output name=upload_url::$(<release_url/release_url.txt)
      - name: Upload Release Asset
        id: upload-release-asset
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.get_release_info.outputs.upload_url }}
          asset_path: ./dist/${{ matrix.OUT_FILE_NAME }}
          asset_name: ${{ matrix.OUT_FILE_NAME }}
          asset_content_type: ${{ matrix.ASSET_MIME }}

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜์—ฌ ์™„์„ฑ๋œ build.yml ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

name: Build

on:
  push:
    tags:
      - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10

jobs:
  createrelease:
    name: Create Release
    runs-on: ubuntu-latest
    steps:
      - name: Create Release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          draft: false
          prerelease: false
      - name: Save Release URL File
        run: echo "${{ steps.create_release.outputs.upload_url }}" > release_url.txt
      - name: Upload Release URL File
        uses: actions/upload-artifact@v1
        with:
          name: release_url
          path: release_url.txt

  build:
    name: Build packages
    needs: createrelease
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include:
          - os: macos-latest
            TARGET: macos
            CMD_BUILD: |
              pyinstaller app.spec
              cd dist/ 
              zip -r9 DICOM_Anonymizer.zip DICOM_Anonymizer
              cd ../
            OUT_FILE_NAME: DICOM_Anonymizer.zip
            ASSET_MIME: application/zip
          - os: windows-latest
            TARGET: windows
            CMD_BUILD: pyinstaller app.spec
            OUT_FILE_NAME: DICOM_Anonymizer.exe
            ASSET_MIME: application/x-msdownload

    steps:
      - uses: actions/checkout@v1
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: 3.12
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      - name: Build with pyinstaller for ${{ matrix.TARGET }}
        run: ${{ matrix.CMD_BUILD }}
      - name: Download Release URL File
        uses: actions/download-artifact@v1
        with:
          name: release_url
      - name: Get Release File Name & Upload URL
        id: get_release_info
        shell: bash
        run: echo ::set-output name=upload_url::$(<release_url/release_url.txt)
      - name: Upload Release Asset
        id: upload-release-asset
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.get_release_info.outputs.upload_url }}
          asset_path: ./dist/${{ matrix.OUT_FILE_NAME }}
          asset_name: ${{ matrix.OUT_FILE_NAME }}
          asset_content_type: ${{ matrix.ASSET_MIME }}


GitHub Action ์‹คํ–‰ํ•˜๊ธฐ

์•Œ์ˆ˜์—†๋Š” permission ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ• ์ˆ˜์žˆ๋Š”๋ฐ, action ์˜ permission ๊ด€๋ จ ํ•˜์—ฌ ์„ค์ •์„ ๋งŒ์ ธ์ค˜์•ผํ•œ๋‹ค.

์ด์™€ ๊ฐ™์ด ์“ฐ๊ธฐ ์ฝ๊ธฐ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•ด์•ผ ์ œ๋Œ€๋กœ ์‹คํ–‰์ด ๋œ๋‹ค.

๋˜ํ•œ, GITHUB_TOKEN ์„ ๋”ฐ๋กœ ์„ค์ •ํ•ด์ค˜์•ผํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ, ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค๊ณ  ํ•œ๋‹ค. (์ด์™ธ์˜ ํ† ํฐ์„ ์ถ”๊ฐ€์ ์œผ๋กœ ํ•ธ๋“ค๋งํ•˜๋ ค๋ฉด, action ์˜ ํ† ํฐ ๊ด€๋ จํ•˜์—ฌ ์„ค์ •์„ ํ•ด์ค˜์•ผํ•œ๋‹ค. )

์ด์ œ ๋นŒ๋“œ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ณด์ž. ๋‹ค์Œ์˜ ๋ช…๋ น์–ด๋ฅผ ์ณ๋ณด๋ฉด์„œ ํ…Œ์ŠคํŠธํ• ์ˆ˜์žˆ๋‹ค.

git commit -a -m "add autostart enable and disable scripts to pypi"
git tag v0.4.5
git push
git push origin --tags

์‹ค์ œ action ํƒญ์— ๋“ค์–ด๊ฐ€๋ณด๋ฉด build๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ๋‹ค๊ณ  ์•Œ๋ ค์ค€๋‹ค.

์„ค์ •ํ•œ๋Œ€๋กœ, release์—๊นŒ์ง€ ์ž˜ ์—…๋กœ๋“œ ๋˜์—ˆ๋‹ค.

๋‚˜์˜ ํ™˜๊ฒฝ์ธ m2 ์—์„œ ์‹คํ–‰ํ•ด๋ณด๋‹ˆ, ๋งž์ง€์•Š๋Š” exec ํ˜•์‹์ด๋ผ๊ณ  ์—๋Ÿฌ๊ฐ€ ๋‚˜๋Š”๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค. mac m2 ๊ธฐ์ค€์œผ๋กœ ๋‚˜์˜ arch ๋Š” arm64์ด๊ณ , m1๊ณผ m2 ์— ํƒ‘์žฌ๋˜๋Š” apple silicon์„ ์ง€์›ํ•˜๋Š” ๋ชจ๋ธ์ด ์ตœ๊ทผ 2023๋…„ 10์›”์— ์ •์‹ ์ถœ์‹œ๋˜์—ˆ๋‹ค๊ณ  ํ•œ๋‹ค. ์ฐธ๊ณ  : (Introducing the new, Apple silicon powered M1 macOS larger runner for GitHub Actions - The GitHub Blog)

๊ทธ๋Ÿฌ๋ฏ€๋กœ m1, m2 ์— ์ง€์›๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ๋ชจ๋‘ ์ˆ˜์ •ํ•ด์ค˜์•ผํ• ๊ฒƒ๊ฐ™๋‹ค.


Conclusion

๊ฒฐ๋ก ์ ์œผ๋กœ ํ†ตํ•ฉ๋œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์œ„ํ•ด์„œ๋Š” CI/CD๋ฅผ ๋„์ž…ํ•˜๋Š”๊ฒƒ์ด ๋„์›€์ด ๋œ๋‹ค. ๋‹ค์–‘ํ•œ os์— ๋งž์ถ”์–ด ๋นŒ๋“œ๋ฅผ ํ• ์ˆ˜์žˆ๊ณ , ์‹ค์ œ ๋‚˜์˜ ๋กœ์ปฌ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ ๋„ ๋นŒ๋“œ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์ ์ด ํŽธ๋ฆฌํ•˜๋‹ค. ๋˜ํ•œ, ํ†ตํ•ฉ๋˜๊ณ  ์•ˆ์ •๋œ ํ™˜๊ฒฝ์—์„œ ๋นŒ๋“œ๋ฅผ ์ง„ํ–‰ํ•˜๊ธฐ์— ์˜ค๋ฅ˜๊ฐ€ ๋‚  ํ™•๋ฅ ๋„ ์ ์–ด์ง„๋‹ค๋Š” ์ด์ ๋„ ์žˆ๋Š”๊ฒƒ๊ฐ™๋‹ค. ์ถ”ํ›„ ํ…Œ์ŠคํŠธ๋ฅผ ๊ฑฐ์น˜๋ฉฐ, ๋‹ค์–‘ํ•œ ๋ฒ„์ „์„ ์ถ”๊ฐ€ํ•ด์•ผํ• ๊ฒƒ๊ฐ™๋‹ค.

** cc sorce Build a multi OS Python app in the cloud: PyInstaller on GitHub Actions - Data-Dive GitHub ํ˜ธ์ŠคํŒ… ์‹คํ–‰๊ธฐ ์ •๋ณด - GitHub Docs