[V1] AI CI - 100-hours-a-week/KTB4-10th-Cloud GitHub Wiki

AI CI

PR 단계별 검증

feature → dev

기능의 변경사항을 dev 브랜치에 통합하는 단계. 기본적인 검증만 수행함.

  • 애플리케이션 빌드 가능 여부
  • 코드 규칙 검증
  • 라이브러리, 패키지 검사

실패해도 Merge 가능.

dev → main

main은 실제 배포의 기준이 되는 브랜치. 해당 단계에서는 마지막으로 Docker 이미지 빌드 가능 여부까지 확인함.

  • 애플리케이션 빌드 가능 여부
  • 코드 규칙 검증
  • 라이브러리, 의존성 검사
  • 도커 이미지 빌드 가능 여부 확인
    • 배포 대상인 이미지만 레지스트리에서 관리하기 위해 최종 머지 때 이미지 빌드 및 레지스트리에 푸시

실패해도 Merge 가능.

Workflow

GitHub Actions는 GitHub에 Push/PR 될 때 자동으로 테스트 및 빌드 가능 여부를 검증하는 CI 도구로 사용함.

name: KGB AI CI

on:
  pull_request:
    branches:
      - dev
      - main

jobs:
  ai-ci:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"

      - name: Install uv
        uses: astral-sh/setup-uv@v6

      - name: Install dependencies
        run: uv sync --frozen

      - name: Ruff Lint
        run: uv run ruff check . --output-format=github

      - name: Ruff Format Check
        run: uv run ruff format --check .

      - name: Docker Image Build
        if: github.base_ref == 'main'
        run: docker build -t ai:ci .

Ruff 설정

Ruff란?

Python용 Linter(코드 정적 검사)와 Formatter(코드 포맷터) 기능을 모두 제공하는 도구. 기존에 Flake8(Lint) + isort(Import 정렬) + Black(Format)처럼 여러 도구를 조합해서 쓰던 역할을 Ruff 하나로 대체함. Rust로 작성되어 있어 기존 Python 기반 Lint 도구보다 실행 속도가 훨씬 빠름.

개발 환경과 CI에서 동일한 검사 기준을 사용하기 위해 pyproject.toml에 Ruff 규칙 명시

초기 설정

[tool.ruff]
target-version = "py314"
line-length = 88

[tool.ruff.lint]
select = ["E", "F", "I"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
  • E: pycodestyle 오류 계열
  • F: Pyflakes 오류 계열
  • I: import 정렬
  • ruff check: Lint 검사
  • ruff format --check: 포맷 수정 없이 규칙 준수 여부만 검사

Ruff 검사 규칙

E — pycodestyle Error

Python 코드의 기본적인 스타일 규칙 위반을 검사함.

예를 들어 들여쓰기, 공백, 한 줄의 코드 작성 방식 등 Python 코드의 기본적인 형태가 규칙에 맞는지 확인함.

# 잘못된 예
if True:
  print("hello")  # 잘못된 들여쓰기

# 수정
if True:
    print("hello")

단순히 코드를 예쁘게 만드는 목적뿐 아니라 팀원이 작성한 Python 코드의 기본적인 형태를 일정하게 유지하기 위해 사용함.


F — Pyflakes

코드의 모양보다는 실제 코드에서 발생할 가능성이 있는 문제를 정적으로 검사함.

대표적으로 다음과 같은 문제를 탐지함.

  • 사용하지 않는 import
  • 정의되지 않은 변수 사용
  • 사용되지 않는 변수/import
  • 동일한 이름의 중복 정의 등

예:

import os

print(message)

위 코드에서는:

  • os를 import했지만 사용하지 않음
  • message가 정의되지 않은 상태에서 사용됨

과 같은 문제를 Ruff가 탐지할 수 있음. 실수 가능성이 있는 코드를 CI 단계에서 발견하는 데 의미가 있음.


I — isort / Import 정렬

Python의 import 순서와 그룹을 일관되게 관리함.

예:

from fastapi import FastAPI
import os
from app.service import guide_service

Ruff가 정리하면 다음과 같이 표준 라이브러리, 외부 라이브러리, 프로젝트 내부 모듈 등을 구분하여 관리할 수 있음.

import os

from fastapi import FastAPI

from app.service import guide_service

개발자마다 import를 다른 순서로 작성하면서 발생하는 불필요한 코드 차이를 줄이기 위해 사용함.


ruff check .

현재 프로젝트 전체를 대상으로 활성화된 Lint 규칙을 검사함.

현재 설정이 다음과 같다면:

[tool.ruff.lint]
select = ["E", "F", "I"]

다음을 검사함.

ruff check .
    │
    ├─ E → 기본 Python 스타일 오류
    ├─ F → 사용하지 않는 import, 정의되지 않은 변수 등의 코드 문제
    └─ I → import 순서

CI에서는:

uv run ruff check . --output-format=github

으로 실행함.

--output-format=github을 사용하면 Ruff가 발견한 문제를 GitHub Actions에서 파일과 코드 위치를 확인하기 쉬운 형태로 출력함.


ruff format --check .

Ruff Formatter 기준으로 코드 포맷이 맞는지 검사함.

예를 들어 개발자가 작성한 코드가:

def hello( name ):
    print( "hello",name )

Formatter 기준에 맞지 않는다면 CI에서 이를 감지함.

중요한 점은 --check 옵션임.

ruff format .

은 실제 파일을 자동으로 수정하지만,

ruff format --check .

은 파일을 수정하지 않고 "포맷이 필요한 파일이 존재하는가?"만 검사함.

따라서 CI에서는 코드를 임의로 수정하지 않고 포맷 위반 여부만 확인할 수 있음.

개발자는 로컬에서 필요할 경우:

uv run ruff format .

으로 자동 포맷 후 다시 Push할 수 있음.


Lint와 Format의 차이

명령 목적 예시
ruff check . 코드 문제 및 규칙 위반 탐지 미사용 import, 정의되지 않은 변수, import 순서
ruff format --check . 코드 모양이 Formatter 기준과 일치하는지 확인 공백, 줄바꿈, 코드 배치
ruff format . 코드를 Formatter 기준으로 실제 수정 로컬 개발 시 자동 포맷

따라서 CI에서는 다음 두 검사를 모두 수행함.

uv run ruff check . --output-format=github
uv run ruff format --check .

이를 통해 코드의 정적 문제와 팀의 코드 포맷을 각각 검증함.

초기에는 E, F, I부터 적용 후 필요에 따라 확대 가능함.

uv sync --frozen 이후 uv run ruff를 사용하므로 Ruff를 프로젝트 개발 의존성으로 관리 필요함.

[dependency-groups]
dev = [
    "ruff>=0.x.x",
]

실제 버전은 프로젝트에서 사용하는 Ruff 버전으로 고정 필요함.