Github Actions로 Python CI 구축하기: Part1-Linting
본 글을 읽기전에 CI/CD와 Github Actions에 대한 개념이 확실하지 않다면, 아래의 링크들을 읽어보길 바란다.
CI/CD
Github Actions
Linting이란?
Linting은 소스 코드에서 잠재적인 오류나 스타일 규칙 위반을 찾아내고 보고하는 프로세스를 가리키는 용어이다. Linting은 오타나 문법 오류와 같은 기본적인 실수를 찾아내어 높은 품질의 코드를 작성하도록 도와준다.
파이썬에서 linting은 보통 'PEP 8'이라고 불리는 파이썬 스타일 가이드를 준수하는지 확인한다.
일반적으로 코드를 작성하는 동안에는 편집기나 통합 개발 환경 (IDE)과 같은 도구를 통해 실시간으로 linting 결과를 확인하고, 파이썬에서는 PyLint, Flake8, Pyflakes 등의 패키지 도구를 사용하여 자동화된 방식으로 코드를 검사할 수 있다.
코드 파일 준비
CI/CD 테스트를 위해 만든 repository다. Frok로 복사해서 앞으로 진행될 과정들을 테스트 해볼 수 있다.
테스트를 실행 할 main.py 파일에는 함수 2개가 담겨있다.
### main.py
import math
import os
def add(a, b) -> int:
return math.floor(a + b)
def to_sentence(s) -> str:
s = s.capitalize()
if s.endswith("."):
return s
else:
return s + "."
Github Actions CI(Linting) 구축 방법
Workflow 생성
워크플로우는 Github Actions의 핵심이다.
.github/workflows 디렉토리에 워크플로우 YAML 파일을 생성하여 작업을 정의한다.
간단한 워크플로우 예시를 작성해보자. Github Actions에 들어가보면 기본으로 제공하는 YAML 파일 템플릿들이 있다.
Python application을 선택하면 다음과 같은 yml 파일이 자동 생성되는데,
해당 파일은 Python 프로젝트의 Lint 테스트(flake8)와 유닛 테스트(pytest)를 실행하는 Actions 파일이다.
name: Python Application # 프로젝트의 이름을 정의
on:
push:
branches: [ "main" ] # "main" 브랜치로 push가 발생할 때 이 액션을 실행
pull_request:
branches: [ "main" ] # "main" 브랜치로 pull request가 발생할 때 이 액션을 실행
permissions:
contents: read # 이 액션이 읽기 권한을 가지도록 정의
jobs:
build: # "build" 라는 작업을 정의
runs-on: ubuntu-latest # Ubuntu 환경에서 실행
steps: # 작업 단계를 정의
- uses: actions/checkout@v3 # GitHub 리포지토리를 체크아웃하여 사용
- name: Set up Python 3.10 # Python 3.10을 설정
uses: actions/setup-python@v3 # Python 설정 액션 사용
with:
python-version: "3.10" # Python 버전을 3.10으로 지정
- name: Install dependencies # 의존성 설치
run: | # 아래 스크립트를 실행
python -m pip install --upgrade pip # pip 업그레이드
pip install flake8 pytest # flake8과 pytest 설치
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi # requirements.txt 파일이 있으면 해당 파일에 명시된 의존성 설치
- name: Lint with flake8 # flake8을 사용하여 코드 스타일과 오류 검사
run: | # 아래 스크립트를 실행
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # Python 구문 오류 또는 정의되지 않은 이름이 있을 경우 빌드 중지
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics # 모든 오류를 경고로 처리하도록 설정. GitHub 편집기는 127자의 너비를 가짐
- name: Test with pytest # pytest를 사용하여 테스트 실행
run: | # 아래 스크립트를 실행
# [테스트 스크립트는 이 자리에 들어와야 함]
set up a workflow yourself에 들에 들어가면,
코드가 통합되기전 진행 되었으면 하는 기능들(step)을 선택해서 블럭 쌓듯이 workflow를 만들수도 있다.
Marketplace에서 원하는 기능을 검색한뒤,
작성되어 있는 step 스크립트를 복사, 붙여넣기해서 workflow를 만들 수 있다.
아래와 같은 방법으로 여러개의 step을 추가 할 수 있다.
Workflow를 작성하고 나면, 정의한 Event가 발생할 때 작성한 Workflow가 실행되고,
Actions 탭에서 Workflow 실행 성공 여부를 확인 할 수 있다.
Github Actions에서 주로 사용하는 도구
Black
Black는 Python 코드 포맷터로, 코드의 일관성을 유지하고 스타일 가이드를 준수하도록 도와준다.
Black은 코드를 검사하고 필요한 경우 자동으로 포맷하여 코드 스타일을 일관성 있게 유지한다.
Flake8
Python 코드의 문법적 오류와 스타일을 검사하는 도구다.
코드 스타일 가이드를 준수하지 않거나 구문 오류가 있는 경우 경고 및 에러를 출력한다.
flake8은 자체도 여러 개의 도구들을 결합한 것이다
- pycodestyle: PEP 8 스타일 가이드 기반
- pyflakes: 코드 복잡도와 버그를 검사
- mccabe: 함수와 변수의 타입을 검사
Mypy
mypy는 정적 타입 검사 도구로, Python 코드의 변수와 함수의 타입을 검사하여 타입 관련 오류를 찾아낸다.
Python은 동적 타입 언어로, 변수의 타입을 런타임 시점에서 결정한다.
하지만 mypy는 타입 힌트를 통해 코드에 명시적으로 타입을 정의하고, 이를 기반으로 정적으로 타입을 검사한다.
mypy를 사용하여 타입 검사를 수행하면 코드의 가독성을 향상시키고 디버깅 시간을 단축하여 안정성을 높일 수 있다.
Isrot
isort는 Python 코드의 import문을 자동으로 정렬해주는 도구다.
코드의 가독성을 높이고, 표준화된 import 스타일을 유지하며, import문의 순서를 일관성 있게 관리할 수 있도록 도와준다.
isort는 코드를 분석하여 import문을 그룹화하고, 내장 모듈, 서드파티 패키지, 프로젝트 내의 모듈 순서를 정리하여 재배치한다.
이를 통해 복잡한 import문을 간결하고 일관성 있게 관리할 수 있다.
커스텀 Workflow
아래는 CI 테스트를 위해 worklfow를 정의한 .yml 파일이다.
각각 라인에 대한 설명은 주석으로 적어두었다.
### python-lint.yml
# 워크플로우 이름
name: (PR) Format Python Code
# 워크플로우를 실행할 이벤트 설정
on:
push:
branches: [ "master" ] # "master" 브랜치로 push가 발생할 때 이 액션을 실행
pull_request:
branches: [ "master" ] # "master" 브랜치로 pull request가 발생할 때 이 액션을 실행
# 작업 정의
jobs:
python-code-format:
runs-on: ubuntu-20.04 # Ubuntu 20.04 환경에서 실행
# 실행할 단계 정의
steps:
# GitHub 리포지토리를 체크아웃 (클론)
- name: Checkout code
uses: actions/checkout@v2
# Python 환경 설정
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.10" # Python 3.10 버전 사용
architecture: "x64" # 64비트 아키텍처 사용
# Python 버전 확인
- name: Display Python version
run: python --version # Python 버전 표시
# 필요한 Python 패키지 설치
- name: Install packages
run: pip install black isort autopep8
# 코드 포맷팅 작업 수행
- name: Formatter
run: |
black . # Black를 사용하여 코드 포맷팅
autopep8 --recursive --in-place --aggressive --aggressive . # autopep8를 사용하여 코드 포맷팅
isort . # isort를 사용하여 코드 포맷팅
# Pull Request 생성
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
commit-message: Auto code format # 커밋 메시지
title: Fixes by format action # 풀 리퀘스트 제목
body: This is an auto-generated PR with fixes. # 풀 리퀘스트 본문
labels: automated pr # 레이블
branch: python-code-format-patches # 새로운 브랜치 이름
위에서 모듈등을 통해 자동 변경된 사항들을 Pull request를 통해 반영되도록 작업이 작성되었다.
혹시 Pull Request가 아니라 Commit 으로 반영하게 하고 싶다면 아래의 부분을 주석처리 하고,
# Pull Request 생성
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
commit-message: Auto code format # 커밋 메시지
title: Fixes by format action # 풀 리퀘스트 제목
body: This is an auto-generated PR with fixes. # 풀 리퀘스트 본문
labels: automated pr # 레이블
branch: python-code-format-patches # 새로운 브랜치 이름
다음과 같이 Commit 명령어를 입력해주면 된다.
## 변경사항 Commit
- name: Commit format changes
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
git commit -am "Automated format fixes"
git push
CI 실행
이제 코드를 수정하고, main 브랜치에 Push 하면 위에서 설정한 Workflow를 실행한다.
실행된 Workflow 결과는 Github 브랜치 Actions 탭에서 확인 할 수 있다.
권한설정 에러
처음 CI Wrokflow 중 isort 변경 사항을 코드에 반형하는 push isort changes 과정에서 "Permission to sokuli-bit/study-ci-cd.git denied to github-actions[bot]" 에러가 발생 할 수 있다.
이유는 GitHub Actions의 기본적인 GITHUB_TOKEN이 해당 저장소에 대한 push 권한을 가지고 있지 않기 때문이다.
깃헙 공식 문서에 GITHUB_TOKEN이 Workflow에서 코드를 수정 할 수 있는 권한을 주는 방법이 나와있다.
Setting > Actions > General > WorkFlow permissions
설명에 따라 위의 순서로 페이지에 들어가면 Read repository contents and packages permissions가 선택되어 있을텐데 아래의 이미지처럼 위의 Read and write perissions로 바꿔주고 다시 push 하면 commit이 잘 이루어진다.
마무리
이 글에서는 Github Actions를 활용하여 Python 프로젝트에서 Linting을 자동화하는 방법을 알아보았다. 코드 스타일 오류를 자동으로 검출하고 수정하는 것은 프로젝트의 일관성을 유지하고 버그를 사전에 방지하는 데 매우 유용하다.
다음 글에서는 Github Actions를 활용하여 Python 프로젝트에서 테스트 자동화를 설정하는 방법을 정리해보려고 한다.
https://yoon001.tistory.com/85?category=1060034
'# Development > DevOps' 카테고리의 다른 글
[Ubuntu] 리눅스 메모리 사용량 모니터링: sysstat sar (0) | 2023.11.06 |
---|---|
[CI/CD] Github Actions로 Python CI 구축하기: Part2-Testing (0) | 2023.10.26 |
[CI/CD] GitHub Actions란? (0) | 2023.07.21 |
[CI/CD] CI/CD란 무엇인가? (0) | 2023.07.20 |