Skip to main content

Python ๋นŒ๋“œ ๋ฐ ํ…Œ์ŠคํŠธ

CI(์—ฐ์† ํ†ตํ•ฉ) ์›Œํฌํ”Œ๋กœ๋ฅผ ๋งŒ๋“ค์–ด Python ํ”„๋กœ์ ํŠธ๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์„ธ์š”.

์ฐธ๊ณ  ํ•ญ๋ชฉ

GitHub ํ˜ธ์ŠคํŠธ ์‹คํ–‰๊ธฐ๋Š” ํ˜„์žฌ GitHub Enterprise Server์—์„œ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์†Œ๊ฐœ

์ด ๊ฐ€์ด๋“œ์—์„œ๋Š” Python ํŒจํ‚ค์ง€๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๊ณ  ๊ฒŒ์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค.

GitHub ํ˜ธ์ŠคํŠธ ์‹คํ–‰๊ธฐ์—๋Š” Python ๋ฐ PyPy๋ฅผ ํฌํ•จํ•˜๋Š” ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ์‚ฌ์ „ ์„ค์น˜๋œ ๋„๊ตฌ ์บ์‹œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ์•„๋ฌด๊ฒƒ๋„ ์„ค์น˜ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ตœ์‹  ์†Œํ”„ํŠธ์›จ์–ด ๋ฐ ์‚ฌ์ „ ์„ค์น˜๋œ Python ๋ฐ PyPy ๋ฒ„์ „์˜ ์ „์ฒด ๋ชฉ๋ก์€ GitHub ํ˜ธ์ŠคํŒ… ์‹คํ–‰๊ธฐ์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.

ํ•„์ˆ˜ ์กฐ๊ฑด

YAML ๋ฐ GitHub Actions์˜ ๊ตฌ๋ฌธ์— ๋Œ€ํ•ด ์ž˜ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์›Œํฌํ”Œ๋กœ ์ž‘์„ฑ์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.

Python๊ณผ pip์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ํ•™์Šต์„ ์„ ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋‹ค์Œ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

GitHub Enterprise Server์—์„œ ์ž์ฒด ํ˜ธ์ŠคํŒ… ์‹คํ–‰๊ธฐ ์‚ฌ์šฉ

์‹คํ–‰๊ธฐ๋ฅผ ์ž์ฒด ํ˜ธ์ŠคํŒ…ํ•˜๋Š” GitHub Enterprise Server์—์„œ actions/setup-LANGUAGE ๊ฐ™์€ ์„ค์ • ์ž‘์—…์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ์ธํ„ฐ๋„ท์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์—†๋Š” ์‹คํ–‰๊ธฐ์—์„œ ๋„๊ตฌ ์บ์‹œ๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ธํ„ฐ๋„ท์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์—†๋Š” ์ž์ฒด ํ˜ธ์ŠคํŒ… ์‹คํ–‰๊ธฐ์—์„œ ๋„๊ตฌ ์บ์‹œ ์„ค์ •์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.

Python ์›Œํฌํ”Œ๋กœ ํ…œํ”Œ๋ฆฟ ์‚ฌ์šฉ

๋น ๋ฅด๊ฒŒ ์‹œ์ž‘ํ•˜๋ ค๋ฉด ์›Œํฌํ”Œ๋กœ ํ…œํ”Œ๋ฆฟ์„ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ .github/workflows ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

GitHub์€(๋Š”) ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์ด๋ฏธ ํ•˜๋‚˜ ์ด์ƒ์˜ .py ํŒŒ์ผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ์— ์ž‘๋™ํ•ด์•ผ ํ•˜๋Š” Python์šฉ ์›Œํฌํ”Œ๋กœ ํ…œํ”Œ๋ฆฟ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ€์ด๋“œ์˜ ํ›„์† ์„น์…˜์—์„œ๋Š” ์ด ์›Œํฌํ”Œ๋กœ ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉ์ž ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์˜ˆ์‹œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  1. GitHub์—์„œ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ๊ธฐ๋ณธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.

  2. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ด๋ฆ„ ์•„๋ž˜์—์„œ ์ž‘์—…์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.

    "github/docs" ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ํƒญ ์Šคํฌ๋ฆฐ์ƒท. "์ž‘์—…" ํƒญ์€ ์ฃผํ™ฉ์ƒ‰ ์œค๊ณฝ์„ ์œผ๋กœ ๊ฐ•์กฐ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

  3. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์›Œํฌํ”Œ๋กœ๊ฐ€ ์ด๋ฏธ ์žˆ๋Š” ๊ฒฝ์šฐ ์ƒˆ ์›Œํฌํ”Œ๋กœ๋ฅผ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.

  4. "์›Œํฌํ”Œ๋กœ ์„ ํƒ" ํŽ˜์ด์ง€์—๋Š” ๊ถŒ์žฅ๋˜๋Š” ์›Œํฌํ”Œ๋กœ ํ…œํ”Œ๋ฆฟ์˜ ์„ ํƒ ํ•ญ๋ชฉ์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. "Python ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜"์„ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.

  5. "Python ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜" ์›Œํฌํ”Œ๋กœ์—์„œ ๊ตฌ์„ฑ์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.

    "Python ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜" ์›Œํฌํ”Œ๋กœ ํ…œํ”Œ๋ฆฟ์„ ์ฐพ์„ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ, ๋‹ค์Œ ์›Œํฌํ”Œ๋กœ ์ฝ”๋“œ๋ฅผ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ .github/workflows ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์žˆ๋Š” python-app.yml(์ด)๋ผ๋Š” ์ƒˆ ํŒŒ์ผ์— ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.

    YAML
    name: Python application
    
    on:
      push:
        branches: [ "main" ]
      pull_request:
        branches: [ "main" ]
    
    permissions:
      contents: read
    
    jobs:
      build:
        runs-on: ubuntu-latest
    
        steps:
        - uses: actions/checkout@v4
        - name: Set up Python 3.13
          uses: actions/setup-python@v5
          with:
            python-version: "3.13"
        - name: Install dependencies
          run: |
            python -m pip install --upgrade pip
            pip install ruff pytest
            if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
        - name: Lint and format Python code with ruff
          run: |
           # Lint with the default set of ruff rules with GitHub Annotations
           ruff check --format=github --target-version=py39
           # Verify the code is properly formatted
           ruff format --diff --target-version=py39
        - name: Test with pytest
          run: |
            pytest
    
  6. ํ•„์š”์— ๋”ฐ๋ผ ์›Œํฌํ”Œ๋กœ๋ฅผ ํŽธ์ง‘ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Python ๋ฒ„์ „์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

  7. ๋ณ€๊ฒฝ ๋‚ด์šฉ ์ปค๋ฐ‹์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.

Python ๋ฒ„์ „ ์ง€์ •

GitHub ํ˜ธ์ŠคํŠธ ์‹คํ–‰๊ธฐ์—์„œ ์‚ฌ์ „ ์„ค์น˜๋œ ๋ฒ„์ „์˜ Python ๋˜๋Š” PyPy๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด setup-python ์ž‘์—…์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ด ์ž‘์—…์€ ๊ฐ ์‹คํ–‰๊ธฐ์˜ ๋„๊ตฌ ์บ์‹œ์—์„œ Node.js์˜ ํŠน์ • ๋ฒ„์ „์„ ์ฐพ์•„ ํ•„์ˆ˜ ์ด์ง„ ํŒŒ์ผ์„ PATH์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋‚˜๋จธ์ง€ ์ž‘์—… ๋™์•ˆ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ํŠน์ • ๋ฒ„์ „์˜ Python์ด ๋„๊ตฌ ์บ์‹œ์— ์‚ฌ์ „ ์„ค์น˜๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด setup-python ์ž‘์—…์ด python-versions ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์—์„œ ์ ์ ˆํ•œ ๋ฒ„์ „์„ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

setup-python ์ž‘์—…์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋‹ค์–‘ํ•œ ์‹คํ–‰๊ธฐ ๋ฐ Python ๋ฒ„์ „ ๊ฐ„์— ์ผ๊ด€๋œ ๋™์ž‘์„ ๋ณด์žฅํ•˜๋ฏ€๋กœ GitHub Actions๋กœ Python์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ ๊ถŒ์žฅ๋˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ž์ฒด ํ˜ธ์ŠคํŠธ ์‹คํ–‰๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ Python์„ ์„ค์น˜ํ•˜๊ณ  ์ด๋ฅผ PATH์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ setup-python ์ž‘์—…์„ ์ฐธ์กฐํ•˜์„ธ์š”.

์•„๋ž˜ ํ…Œ์ด๋ธ”์—์„œ๋Š” ๊ฐ GitHubํ˜ธ์ŠคํŠธ ์‹คํ–‰๊ธฐ์—์„œ ๋„๊ตฌ ์บ์‹œ์˜ ์œ„์น˜๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

UbuntuMacWindows
๋„๊ตฌ ์บ์‹œ ๋””๋ ‰ํ„ฐ๋ฆฌ/opt/hostedtoolcache/*/Users/runner/hostedtoolcache/*C:\hostedtoolcache\windows\*
Python ๋„๊ตฌ ์บ์‹œ/opt/hostedtoolcache/Python/*/Users/runner/hostedtoolcache/Python/*C:\hostedtoolcache\windows\Python\*
PyPy ๋„๊ตฌ ์บ์‹œ/opt/hostedtoolcache/PyPy/*/Users/runner/hostedtoolcache/PyPy/*C:\hostedtoolcache\windows\PyPy\*

์ž์ฒด ํ˜ธ์ŠคํŒ… ์‹คํ–‰๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ setup-python ์ž‘์—…์„ ์‚ฌ์šฉํ•˜์—ฌ ์ข…์†์„ฑ์„ ๊ด€๋ฆฌํ•˜๋„๋ก ์‹คํ–‰๊ธฐ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ setup-python ์ถ”๊ฐ€ ์ •๋ณด์—์„œ ์ž์ฒด ํ˜ธ์ŠคํŠธ ์‹คํ–‰๊ธฐ๋กœ setup-python ์‚ฌ์šฉ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

GitHub๋Š” ์˜๋ฏธ ์ฒด๊ณ„ ๋ฒ„์ „ ๊ด€๋ฆฌ ๊ตฌ๋ฌธ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์˜๋ฏธ ์ฒด๊ณ„ ๋ฒ„์ „ ๊ด€๋ฆฌ ์‚ฌ์šฉ ๋ฐ ์˜๋ฏธ ์ฒด๊ณ„ ๋ฒ„์ „ ๊ด€๋ฆฌ ์‚ฌ์–‘์„ ์ฐธ์กฐํ•˜์„ธ์š”.

์—ฌ๋Ÿฌ Python ๋ฒ„์ „ ์‚ฌ์šฉ

๋‹ค์Œ ์˜ˆ์‹œ์—์„œ๋Š” ์ž‘์—…์— ๋Œ€ํ•œ ํ–‰๋ ฌ์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ Python ๋ฒ„์ „์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์›Œํฌํ”Œ๋กœ์—์„œ ์ž‘์—… ๋ณ€ํ˜• ์‹คํ–‰์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.

YAML
name: Python package

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["pypy3.10", "3.9", "3.10", "3.11", "3.12", "3.13"]

    steps:
      - uses: actions/checkout@v4
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      # You can test your matrix by printing the current Python version
      - name: Display Python version
        run: python -c "import sys; print(sys.version)"

ํŠน์ • Python ๋ฒ„์ „ ์‚ฌ์šฉ

ํŠน์ •ํ•œ Python ๋ฒ„์ „์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ: 3.12. ๋˜๋Š” ์˜๋ฏธ ์ฒด๊ณ„ ๋ฒ„์ „ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ์‹  ๋ถ€ ๋ฆด๋ฆฌ์Šค๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์˜ˆ์‹œ์—์„œ๋Š” Python 3์˜ ์ตœ์‹  ๋ถ€ ๋ฆด๋ฆฌ์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

YAML
name: Python package

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        # This is the version of the action for setting up Python, not the Python version.
        uses: actions/setup-python@v5
        with:
          # Semantic version range syntax or exact version of a Python version
          python-version: '3.x'
          # Optional - x64 or x86 architecture, defaults to x64
          architecture: 'x64'
      # You can test your matrix by printing the current Python version
      - name: Display Python version
        run: python -c "import sys; print(sys.version)"

๋ฒ„์ „ ์ œ์™ธ

์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” Python ๋ฒ„์ „์„ ์ง€์ •ํ•˜๋ฉด setup-python์ด ##[error]Version 3.7 with arch x64 not found ์˜ค๋ฅ˜์™€ ํ•จ๊ป˜ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์—๋Š” ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฒ„์ „์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

์‹คํ–‰ํ•˜์ง€ ์•Š์œผ๋ ค๋Š” Python ๊ตฌ์„ฑ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ์›Œํฌํ”Œ๋กœ์—์„œ exclude ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ GitHub Actions์— ๋Œ€ํ•œ ์›Œํฌํ”Œ๋กœ ๊ตฌ๋ฌธ์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.

YAML
name: Python package

on: [push]

jobs:
  build:

    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        python-version: ["3.9", "3.11", "3.13", "pypy3.10"]
        exclude:
          - os: macos-latest
            python-version: "3.11"
          - os: windows-latest
            python-version: "3.11"

๊ธฐ๋ณธ Python ๋ฒ„์ „ ์‚ฌ์šฉ

์ข…์†์„ฑ์„ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋ฏ€๋กœ setup-python์„ ์‚ฌ์šฉํ•˜์—ฌ ์›Œํฌํ”Œ๋กœ์—์„œ ์‚ฌ์šฉ๋˜๋Š” Python ๋ฒ„์ „์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. setup-python์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด python์„ ํ˜ธ์ถœํ•  ๋•Œ PATH์—์„œ ์„ค์ •๋œ Python์˜ ๊ธฐ๋ณธ ๋ฒ„์ „์ด ๋ชจ๋“  ์…ธ์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. Python์˜ ๊ธฐ๋ณธ ๋ฒ„์ „์€ GitHub ํ˜ธ์ŠคํŠธ ์‹คํ–‰๊ธฐ๋งˆ๋‹ค ๋‹ค๋ฅด๋ฉฐ ์ด๋กœ ์ธํ•ด ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ์˜ˆ์ƒ๋ณด๋‹ค ์ด์ „ ๋ฒ„์ „์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

GitHub ํ˜ธ์ŠคํŠธ ์‹คํ–‰๊ธฐ์„ค๋ช…
UbuntuUbuntu ์‹คํ–‰๊ธฐ์—๋Š” /usr/bin/python ๋ฐ /usr/bin/python3์— ์„ค์น˜๋œ ์—ฌ๋Ÿฌ ๋ฒ„์ „์˜ ์‹œ์Šคํ…œ Python์ด ์žˆ์Šต๋‹ˆ๋‹ค. Ubuntu์™€ ํ•จ๊ป˜ ํŒจํ‚ค์ง€๋œ Python ๋ฒ„์ „์€ GitHub๊ฐ€ ๋„๊ตฌ ์บ์‹œ์— ์„ค์น˜ํ•˜๋Š” ๋ฒ„์ „์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.
Windows๋„๊ตฌ ์บ์‹œ์— ์žˆ๋Š” Python ๋ฒ„์ „์„ ์ œ์™ธํ•˜๊ณ  Windows์—๋Š” ๋™์ผํ•œ ๋ฒ„์ „์˜ ์‹œ์Šคํ…œ Python์ด ์ œ๊ณต๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์‹คํ–‰๊ธฐ์™€ ์ผ๊ด€๋œ ๋™์ž‘์„ ์œ ์ง€ํ•˜๊ณ  setup-python ์ž‘์—… ์—†์ด ์ฆ‰์‹œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ Python์„ ํ—ˆ์šฉํ•˜๊ธฐ ์œ„ํ•ด GitHub๋Š” ๋„๊ตฌ ์บ์‹œ์˜ ๋ช‡ ๊ฐ€์ง€ ๋ฒ„์ „์„ PATH์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
macOSmacOS ์‹คํ–‰๊ธฐ์—๋Š” ๋„๊ตฌ ์บ์‹œ์˜ ์ผ๋ถ€์ธ ๋ฒ„์ „ ์™ธ์— ๋‘˜ ์ด์ƒ์˜ ์‹œ์Šคํ…œ Python ๋ฒ„์ „์ด ์„ค์น˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์‹œ์Šคํ…œ Python ๋ฒ„์ „์€ /usr/local/Cellar/python/* ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์žˆ์Šต๋‹ˆ๋‹ค.

์ข…์†์„ฑ ์„ค์น˜

GitHub ํ˜ธ์ŠคํŠธ ์‹คํ–‰๊ธฐ์—๋Š” pip ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์ž๊ฐ€ ์„ค์น˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์ „์— pip์„ ์‚ฌ์šฉํ•˜์—ฌ PyPI ํŒจํ‚ค์ง€ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์—์„œ ์ข…์†์„ฑ์„ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•„๋ž˜ YAML์€ pip ํŒจํ‚ค์ง€ ์„ค์น˜ ํ”„๋กœ๊ทธ๋žจ๊ณผ setuptools ๋ฐ wheel ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๊ฑฐ๋‚˜ ์—…๊ทธ๋ ˆ์ด๋“œํ•ฉ๋‹ˆ๋‹ค.

์ข…์†์„ฑ์„ ์บ์‹œํ•˜์—ฌ ์›Œํฌํ”Œ๋กœ ์†๋„๋ฅผ ๋†’์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ข…์†์„ฑ ์บ์‹ฑ ์ฐธ์กฐ์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.

YAML
steps:
- uses: actions/checkout@v4
- name: Set up Python
  uses: actions/setup-python@v5
  with:
    python-version: '3.x'
- name: Install dependencies
  run: python -m pip install --upgrade pip setuptools wheel

์š”๊ตฌ ์‚ฌํ•ญ ํŒŒ์ผ

pip์„ ์—…๋ฐ์ดํŠธํ•œ ํ›„ ์ผ๋ฐ˜์ ์ธ ๋‹ค์Œ ๋‹จ๊ณ„๋Š” requirements.txt์—์„œ ์ข…์†์„ฑ์„ ์„ค์น˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ pip๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

YAML
steps:
- uses: actions/checkout@v4
- name: Set up Python
  uses: actions/setup-python@v5
  with:
    python-version: '3.x'
- name: Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt

์ข…์†์„ฑ ์บ์‹ฑ

setup-python์ž‘์—…์„ ์‚ฌ์šฉํ•˜์—ฌ ์ข…์†์„ฑ์„ ์บ์‹œํ•˜๊ณ  ๋ณต์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์‹œ์—์„œ๋Š” pip์— ๋Œ€ํ•œ ์ข…์†์„ฑ์„ ์บ์‹œํ•ฉ๋‹ˆ๋‹ค.

YAML
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    cache: 'pip'
- run: pip install -r requirements.txt
- run: pip test

๊ธฐ๋ณธ์ ์œผ๋กœ setup-python ์ž‘์—…์€ ์ „์ฒด ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์—์„œ ์ข…์†์„ฑ ํŒŒ์ผ(pip์˜ ๊ฒฝ์šฐ requirements.txt, pipenv์˜ ๊ฒฝ์šฐ Pipfile.lock ๋˜๋Š” poetry์˜ ๊ฒฝ์šฐ poetry.lock)์„ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ setup-python ์ถ”๊ฐ€ ์ •๋ณด์—์„œ ํŒจํ‚ค์ง€ ์ข…์†์„ฑ ์บ์‹ฑ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

์‚ฌ์šฉ์ž ์ง€์ • ์š”๊ตฌ์‚ฌํ•ญ์ด ์žˆ๊ฑฐ๋‚˜ ์บ์‹ฑ์— ๋Œ€ํ•œ ์„ธ๋ถ€์ ์ธ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ cache ์ž‘์—…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. pip๋Š” ์‹คํ–‰๊ธฐ์˜ ์šด์˜ ์ฒด์ œ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์œ„์น˜์— ์ข…์†์„ฑ์„ ์บ์‹œํ•ฉ๋‹ˆ๋‹ค. ์บ์‹œํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ๋กœ๋Š” ์‚ฌ์šฉํ•˜๋Š” ์šด์˜ ์ฒด์ œ์— ๋”ฐ๋ผ ์œ„์˜ Ubuntu ์˜ˆ์‹œ์™€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ cache ์ž‘์—… ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์—์„œ Python ์บ์‹ฑ ์˜ˆ์‹œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

์ฝ”๋“œ ํ…Œ์ŠคํŠธ

์ฝ”๋“œ๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด ๋กœ์ปฌ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

pytest ๋ฐ pytest-cov๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ

์ œ์—์„œpytest``pytest-cov ๊ทธ๋Ÿฐ ๋‹ค์Œ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํ–‰๋˜์–ด JUnit ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅ๋˜๊ณ  ์ฝ”๋“œ ๊ฒ€์‚ฌ ๊ฒฐ๊ณผ๋Š” Cobertura๋กœ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ JUnit ๋ฐ Cobertura๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

YAML
steps:
- uses: actions/checkout@v4
- name: Set up Python
  uses: actions/setup-python@v5
  with:
    python-version: '3.x'
- name: Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt
- name: Test with pytest
  run: |
    pip install pytest pytest-cov
    pytest tests.py --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html

Ruff๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ ๋ฆฐํŒ… ๋ฐ/๋˜๋Š” ์„œ์‹ ์ง€์ •

๋‹ค์Œ ์˜ˆ์‹œ์—์„œ๋Š” ruff์„(๋ฅผ) ์„ค์น˜ํ•˜๊ฑฐ๋‚˜ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๊ณ  ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  ํŒŒ์ผ์„ ๋ฆฐํŠธํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ Ruff๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

YAML
steps:
- uses: actions/checkout@v4
- name: Set up Python
  uses: actions/setup-python@v5
  with:
    python-version: '3.x'
- name: Install the code linting and formatting tool Ruff
  run: pipx install ruff
- name: Lint code with Ruff
  run: ruff check --output-format=github --target-version=py39
- name: Check code formatting with Ruff
  run: ruff format --diff --target-version=py39
  continue-on-error: true

์„œ์‹ ์ง€์ • ๋‹จ๊ณ„๊ฐ€ continue-on-error: true ์„ค์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์„œ์‹ ์ง€์ • ๋‹จ๊ณ„๊ฐ€ ์„ฑ๊ณตํ•˜์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ ์›Œํฌํ”Œ๋กœ๊ฐ€ ์‹คํŒจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์„œ์‹ ์ง€์ • ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ–ˆ์œผ๋ฉด ์›Œํฌํ”Œ๋กœ๊ฐ€ ์ƒˆ ๋ฌธ์ œ๋ฅผ catchํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋‹น ์˜ต์…˜์„ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

tox๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์‹คํ–‰

GitHub Actions๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด tox๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์—ฌ๋Ÿฌ ์ž‘์—…์— ์ž‘์—…์„ ๋ถ„์‚ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠน์ • ๋ฒ„์ „์„ ์ง€์ •ํ•˜๋Š” ๋Œ€์‹  PATH์—์„œ Python ๋ฒ„์ „์„ ์„ ํƒํ•˜๋ ค๋ฉด -e py ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ tox๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ tox๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

YAML
name: Python package

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python: ["3.9", "3.11", "3.13"]

    steps:
      - uses: actions/checkout@v4
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python }}
      - name: Install tox and any other packages
        run: pip install tox
      - name: Run tox
        # Run tox using the version of Python in `PATH`
        run: tox -e py

์›Œํฌํ”Œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์•„ํ‹ฐํŒฉํŠธ๋กœ ํŒจํ‚ค์ง€

์›Œํฌํ”Œ๋กœ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„ ๋ณผ ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋กœ๊ทธ ํŒŒ์ผ, ์ฝ”์–ด ๋คํ”„, ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ๋˜๋Š” ์Šคํฌ๋ฆฐ์ƒท์„ ์ €์žฅํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์›Œํฌํ”Œ๋กœ ์•„ํ‹ฐํŒฉํŠธ์™€ ๋ฐ์ดํ„ฐ ์ €์žฅ ๋ฐ ๊ณต์œ ์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.

๋‹ค์Œ ์˜ˆ์‹œ๋Š” upload-artifact ์ž‘์—…์„ ์‚ฌ์šฉํ•˜์—ฌ pytest ์‹คํ–‰์˜ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ด€ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ upload-artifact ์ž‘์—…์„ ์ฐธ์กฐํ•˜์„ธ์š”.

YAML
name: Python package

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]

    steps:
      - uses: actions/checkout@v4
      - name: Setup Python # Set Python version
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      # Install pip and pytest
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install pytest
      - name: Test with pytest
        run: pytest tests.py --doctest-modules --junitxml=junit/test-results-${{ matrix.python-version }}.xml
      - name: Upload pytest test results
        uses: actions/upload-artifact@v3
        with:
          name: pytest-results-${{ matrix.python-version }}
          path: junit/test-results-${{ matrix.python-version }}.xml
        # Use always() to always run this step to publish test results when there are test failures
        if: ${{ always() }}

PyPI์— ๊ฒŒ์‹œ

CI ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•˜๋ฉด Python ํŒจํ‚ค์ง€๋ฅผ PyPI์— ๊ฒŒ์‹œํ•˜๋„๋ก ์›Œํฌํ”Œ๋กœ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์„น์…˜์—์„œ๋Š” ๋ฆด๋ฆฌ์Šค๋ฅผ ๊ฒŒ์‹œํ•  ๋•Œ๋งˆ๋‹ค GitHub Actions๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ PyPI์— ํŒจํ‚ค์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์—์„œ ๋ฆด๋ฆฌ์Šค ๊ด€๋ฆฌ์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.

์•„๋ž˜ ์˜ˆ์‹œ ์›Œํฌํ”Œ๋กœ์—์„œ๋Š” ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒŒ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ PyPI๋ฅผ ์ธ์ฆํ•˜๋ฏ€๋กœ ์ˆ˜๋™์œผ๋กœ ๊ตฌ์„ฑ๋œ API ํ† ํฐ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

YAML
# ์ด ์›Œํฌํ”Œ๋กœ๋Š” GitHub์—์„œ ์ธ์ฆ๋˜์ง€ ์•Š์€ ์ž‘์—…์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
# ์ž‘์—…์€ ํƒ€์‚ฌ์—์„œ ์ œ๊ณตํ•˜๋ฉฐ
# ๋ณ„๋„์˜ ์„œ๋น„์Šค ์•ฝ๊ด€, ๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจ, ์ง€์› ์„ค๋ช…์„œ์—์„œ ๊ทœ์ •๋ฉ๋‹ˆ๋‹ค.
# ์ฐธ์กฐํ•˜์„ธ์š”.

# ์ปค๋ฐ‹ SHA์— ์ž‘์—…์„ ๊ณ ์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
# ์ตœ์‹  ๋ฒ„์ „์„ ์–ป์œผ๋ ค๋ฉด SHA๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
# ํƒœ๊ทธ ๋˜๋Š” ๋ถ„๊ธฐ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ๊ฒฝ๊ณ  ์—†์ด ์ž‘์—…์ด ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

name: Upload Python Package

on:
  release:
    types: [published]

permissions:
  contents: read

jobs:
  release-build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.x"

      - name: Build release distributions
        run: |
          # NOTE: put your own distribution build steps here.
          python -m pip install build
          python -m build

      - name: Upload distributions
        uses: actions/upload-artifact@v3
        with:
          name: release-dists
          path: dist/

  pypi-publish:
    runs-on: ubuntu-latest

    needs:
      - release-build

    permissions:
      # IMPORTANT: this permission is mandatory for trusted publishing
      id-token: write

    # Dedicated environments with protections for publishing are strongly recommended.
    environment:
      name: pypi
      # OPTIONAL: uncomment and update to include your PyPI project URL in the deployment status:
      # url: https://pypi.org/p/YOURPROJECT

    steps:
      - name: Retrieve release distributions
        uses: actions/download-artifact@v3
        with:
          name: release-dists
          path: dist/

      - name: Publish release distributions to PyPI
        uses: pypa/gh-action-pypi-publish@6f7e8d9c0b1a2c3d4e5f6a7b8c9d0e1f2a3b4c5d