νμ¬ νμ ν©λ₯ν μ§λ 4κ°μ μ λ λμλ€. λλ νμμ λ½μ 첫 λ°±μλ κ°λ°μμκ³ , κΈ°μ‘΄μλ λΆλ¬Έμ₯λ ν λΆμ΄μ λ°±μλ κ°λ°μ λͺ¨λ λ΄λΉνκ³ κ³μ ¨λ€. κ°λ° νμμ΄ 1λͺ μμ 2λͺ μ΄ λλ 건 λ¨μν μ«μκ° λμ΄λλ κ² λΏλ§ μλλΌ νμ μ΄ λΆνμνλ κ·Έλμμ κ°λ° νλ‘μΈμ€μ λ¬Ένλ₯Ό λͺ¨λ ν΅μ§Έλ‘ λ€μ΄λ΄μΌνλ νλͺ μ΄ νμν μΌμ΄λΌκ³ μκ°λλ€.
μ μ¬ μ΄λ°μλ μ κ· μ μ¬μλ₯Ό μν README μμ±λΆν° μμν΄ λ‘컬 νκ²½ μ ν λ°©λ², git λΈλμΉ μ λ΅ μ립 λ± κ°λ°νκΈ° μ’μ νκ²½ λ§λ€κΈ°!λ₯Ό μ£Όλμ μΌλ‘ μ§ννλ€. λ΄κ° κ³ μμ νλλΌλ λ€μ μ¬λμ κ³ μμ λ ν μ μλλ‘ λ§λ€μ΄λλ©΄ μ’μ κ² κ°λ¨ μκ°μ νκΈ° λλ¬Έμ νλμ© μ°¨κ·Όμ°¨κ·Ό ν΄λκ° μ μμλ€.
μ΄ν μ΄μλ₯Ό ν λΉλ°κ³ API κ°λ°μ μ§ννκ² λμλλ°, μ΄λ―Έ λ§μ μ μ λ€μ΄ μ¬μ©μ€μΈ μλΉμ€λ₯Ό κ°μ ν΄λκ°λ μμ μ΄ μ½μ§λ§μ μμλ€. νΉνλ μμ§ μ΅μνμ§ μμ κΈ°μ‘΄ μ½λλ€ μ¬μ΄μ λ΄ νΌμ³λ₯Ό μ§μ΄λ£μΌλ €λ μ¬μ΄λμ΄ννΈλ₯Ό μμνκΈ°κ° νλ€μκ³ , κΆν λ¬Έμ κ° λ―Όκ°ν λλ©μΈμ΄λ€λ³΄λ μ¬λ¬ κ³μ μΌλ‘ ν μ€νΈλ₯Ό ν΄μΌ νλλ° μ΄λ₯Ό λ°λ³΅νλ μμ μ΄ κ΅μ₯ν λ²κ±°λ‘μ λ€. νλ‘ κ°λ°>ν μ€νΈ>λ°°ν¬νλ μ¬μ΄ν΄μ λλ©΄μ μμμΉλͺ»ν μ₯μ λ₯Ό λ§μ£ΌμΉλ μΌλ λΉλ²νλ€. μ΄λ κ² κ°λ°νλ€κ° λμμλ μ₯μ μ CSμ μλ¬λ¦¬λ μ΄λμ΄ λ―Έλκ° κΈ°λ€λ¦΄ λΏπμ΄λΌλ μκ°μ΄ λ€μκ³ , ν μ€νΈ/λ°°ν¬ κ·μΉ λ Όμλ₯Ό μν μ리λ₯Ό λ§λ€μ΄ νμλΆλ€κ» λ΄ μκ°μ 곡μ λλ¦¬κ² λμλ€.
κ·Έ μ€ κ°μ₯ μ κ·Ήμ μΌλ‘ μ΄ννλ λΆλΆμ ν μ€νΈμ½λ μμ±κ³Ό ν μ€νΈ μλν νλ‘μΈμ€ λμ μ΄μλ€. ν μ€νΈλ₯Ό λμμ€ μΈλ ₯μ΄ λΆμ‘±νλ€λ©΄ μ½λλΌλ λ λμμ£Όλλ‘ λ§λ€μ΄μΌκ² λ¨ μκ°μ΄ λ€μκ³ , ν μ€νΈμ½λ μμ±μ ν΄λ³΄μλ κ²°λ‘ μ λλ¬νλ€. ν μ€νΈμ½λκ° μμ΄λ©΄ κ·Έλ§νΌ λ΄κ° λ°λ³΅μ μΌλ‘ ν΄μΌ ν ν μ€νΈκ° μ€μ΄λ€ κ²μ΄κ³ λλ³΄λ€ λ μ ννκ² μ€λ₯λ₯Ό μ§μ΄μ€ μ μμ κ²μ΄λ€.
CI ν΄μ TravisCI, CircleCI λ± μ¬λ¬κ°μ§κ° μλλ°, λ²μ κ΄λ¦¬λ₯Ό Git/GithubμΌλ‘ νκ³ μμΌλ githubμ actions λ₯Ό νμ©ν΄ λλλ‘μ΄λ©΄ μ¬μ© μ€μΈ νλ«νΌ λ΄μμ νκ²½μ ꡬμΆνκΈ°λ‘ νλ€.
νλ‘μ νΈλ Django κΈ°λ°μ΄λ©° APIλ μ£Όλ‘ Django Rest Frameworkλ‘ μμ±λμλ€.
μ°λ¦¬ νλ‘μ νΈμ μ½λ κ³³κ³³μλ μ±λ₯ ν₯μμ μν΄ replica DBλ₯Ό λͺ
μμ μΌλ‘ λ°λΌλ³΄λλ‘ νλ λΆλΆμ΄ μλλ°, μ΄λ₯Ό ν
μ€νΈνλ €λ replica
DBλ₯Ό μ°Ύμ μ μλ€λ μ€λ₯κ° λ°μνλ€.
μ΄λ₯Ό ν΄κ²°νλ €λ©΄ ν
μ€νΈμ½λμ databases
μ μ¬μ©ν DBλ₯Ό λͺ
μμ μΌλ‘ μμ±ν΄μ£Όμ΄μΌ νλ€.
class ExampleAPITest(APITransactionTestCase):
databases = ["default", "replica"] # νΉμ databases = "__all__"
λν λ°μ΄ν°λ² μ΄μ€λ₯Ό μ΄μ©ν΄ ν
μ€νΈν λͺ©μ μ΄λΌλ©΄ APISimpleTestCase
λ APITestCase
λ μ·¨μ§μ λ§μ§ μλλ€.
λμ APITransactionTestCase
λ₯Ό μ¬μ©νμ.
μ΄λ νΈλμμ
μμ ν
μ€νΈλ₯Ό λννκ³ κ° ν
μ€νΈλ₯Ό μμν λ λ°μ΄ν°λ² μ΄μ€λ₯Ό λ‘€λ°±ν μ μλλ‘ νλ€.
λ΄κ° ꡬμΆνλ € ν νλ‘μΈμ€λ κ°λ¨νλ€. λκ΅°κ° PR(Pull Request)μ μ¬λ¦¬κ±°λ μ€νλ PRμ Pushλ₯Ό νλ©΄ actions workflowsλ₯Ό ν΅ν΄ μ€ν¬λ¦½νΈλ₯Ό λκ³ ν μ€νΈκ° μ±κ³΅νλ©΄ μ¬λμΌλ‘ μ±κ³΅νλ€λ λ©μμ§λ₯Ό 보λ΄κ³ λ¨Έμ§ λ²νΌμ νμ±νμν€λ λ°©μμ΄λ€.
μ°μ νλ‘μ νΈ root νμμ .github
ν΄λλ₯Ό λ§λ€κ³ κ·Έ νμμ workflows
ν΄λλ₯Ό λ§λ λ€.
CI μ½λ μΈμλ actionsλ₯Ό νμ©ν μλν μ€ν¬λ¦½νΈκ° μΆκ°λ μ μμΌλ pull-request.yml
νΉμ ci.yml
μ κ°μ μ΄λ¦μΌλ‘ νμΌμ νλ λ§λ λ€.
.github/workflows/pull-request.yml
name: Pull request
on:
pull_request:
branches:
- "**"
actionsμ μ΄λ¦μ μ νκ³ μ΄λ€ μ‘μ μ΄ μ΄λ€ λΈλμΉμ λ°μνμ λ ν΄λΉ μ€ν¬λ¦½νΈλ₯Ό λ릴 μ§μ λν κ·μΉμ μ λλ€. λλ λͺ¨λ λΈλμΉμ λν΄ PRμ΄ λ°μνλ©΄ ν μ€νΈλ₯Ό λ릴 κ³νμ΄μκΈ° λλ¬Έμ μμ κ°μ΄ μμ±νλ€.
μ νμΌμ μ΄μ΄μ μμ±νλ©΄ λλ€.
jobs:
test:
timeout-minutes: 20
runs-on: ubuntu-latest
env:
example: ${{ secrets.example }}
νλμ workflowμ λν΄ μ¬λ¬ jobμ λ±λ‘ν μ μλ€.
λλ test
λΌλ μ΄λ¦μ jobμ μμ±νκ³ , timeout-minutes
λ 20λΆμΌλ‘ μμ±νλ€.
ν΄λΉ μ‘μ
μ΄ μ€ννλλ° 20λΆμ΄ μ΄κ³Όλλ©΄ μλ failμ΄ λλ 쑰건μ΄λ€.
runs-on
μ ν΄λΉνλ λΆλΆμ μ΄λ€ μλ² λ²μ μμμ λμνλλ‘ ν μ§ μ§μ νλ λΆλΆμ΄λ€.
env
λ job λ΄μμ μ¬μ©λ νκ²½λ³μλ₯Ό μ μ₯νλ 곡κ°μ΄λ€.
μ€ν¬λ¦½νΈμ μ§μ μμ±νκΈ°μλ λ―Όκ°ν ν€ μ 보λ κ³μ μ 보λ Github repositoryμ Settings > Secrets > Actionsμ λ±λ‘ν΄λκ³ secrets
λ₯Ό ν΅ν΄ λ³μλ‘μ κ°μ Έμ μΈ μ μλλ‘ μ€μ νλ€.
steps:
- name: Checkout
uses: actions/checkout@v2
steps
νμμλ ν
μ€νΈλ₯Ό μν΄ νμν κ²λ€μ νλμ© μμ±ν΄μ£Όλ©΄ λλ€.
uses
μλ λ€μν μ€νμμ€λ₯Ό μμ±νμ¬ νμ©ν μ μλλ°, checkout@v2 λ ${{ github.workspace }}
νμμ 체ν¬μμνλ actionμΌλ‘ μ€ν¬λ¦½νΈμμ μ κ·Όν μ μλλ‘ νλ€.
- name: Build containers
run: docker-compose -f docker-compose.test.yml build
νλ‘μ νΈλ docker-compose
λ‘ μ»¨ν
μ΄λλ₯Ό λΉλνκ³ μ€νν μ μλλ‘ κ΅¬μ±λμ΄μλ€.
νλ‘λμ
/κ°λ° νκ²½κ³Ό λ€λ₯΄κ² test νκ²½μμλ AWS RDSλ₯Ό μ¬μ©νμ§ μκ³ μ§μ PostgreSQL 컨ν
μ΄λλ₯Ό λ°λ‘ λμ μ°κ²°νμ¬ μ¬μ©νλλ‘ κ΅¬μ±νμΌλ―λ‘ μ΄μ λ°λ₯Έ docker-compose.test.yml
νμΌμ λ§λ€μλ€.
- name: Copy files
run: echo "${{ secrets.file_name }}" > ${{ github.workspace }}/file_name.yml
κΈ°μ‘΄ μλ²μμ scp
λ₯Ό ν΅ν΄ νΉμ νμΌμ κ°μ ΈμμΌ νλ μ΄μκ° μμ΄ actionsμ μ€νμμ€λ₯Ό μ°Ύμμ μ μ©ν΄λ³΄λ μμ€μ vpn λ± μΆκ°λ‘ μ€μ ν΄μ£Όμ΄μΌ νλ κ²λ€μ΄ λ§μμ Έ ν΄λΉ νμΌμ μ 보λ github secretsμ μ μ₯ν΄λκ³ λ³΅μ¬νμ¬ μ°λλ‘ νλ€.
- name: Start containers
run: docker-compose -f docker-compose.test.yml up -d
- name: Check docker status
run: docker-compose -f docker-compose.test.yml logs --tail=100 && docker-compose ps
- name: Migration
run: docker-compose -f docker-compose.test.yml exec -T web python manage.py migrate
- name: Run tests
run: docker-compose -f docker-compose.test.yml exec -T web python manage.py test
- name: Stop containers
if: always()
run: docker-compose -f docker-compose.test.yml down
λ컀 컨ν μ΄λλ₯Ό λμ°κ³ DBλ₯Ό λ§μ΄κ·Έλ μ΄μ νκ³ ν μ€νΈλ₯Ό λ리λ λΆλΆμ΄λ€. ν μ€νΈκ° λλλ©΄ 컨ν μ΄λλ₯Ό μ’ λ£μν€λλ‘ νλ€.
- name: Send result to slack
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
author_name: Github Action Test
fields: repo,message,commit,author,action,eventName,ref,workflow,job,took
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
if: always()
action-slack@v3 λ₯Ό ν΅ν΄ workflowκ° λλλ©΄ ν
μ€νΈ κ²°κ³Όλ₯Ό μ¬λ μ±λλ‘ λ°μλ³Ό μ μλλ‘ μ€μ νλ€.
fields
μλ λ°κ³ μΆμ μ 보λ€(λ νμ§ν 리λͺ
, λ©μμ§, 컀λ°, μ μ λ±)μ μ
λ§μ λ§κ² μ€μ ν΄μ£Όλ©΄ λλ€.
μ€ν¬λ¦½νΈ μμ±μ μλ£νκ³ , PRμ μ¬λ €λ³΄λ©΄ actionsμ μλ¦λ€μ΄ workflow κ²°κ³Όλ¬Όμ΄ μμΈλ€.
μ¬λ μ±λλ‘ ν μ€νΈ κ²°κ³Ό λν μ μμ μΌλ‘ μ€λ κ²μ νμΈν μ μλ€.
νμ΄ Github Free νλμ μ¬μ©μ€μ΄κ³ ν΄λΉ νλ‘μ νΈκ° μ€νμμ€ νλ‘μ νΈκ° μλλΌλ©΄ actions μμμκ°μ΄ μ 2,000λΆμΌλ‘ μ νλμ΄ μλ κ²μ μ°Έκ³ νμλ©΄ μ’μ κ² κ°λ€. (μ°λ¦¬ νμ ν νλ μ κ·Έλ μ΄λλ₯Ό νλ€.)
ν μ€νΈμ½λ μμ±μ΄ λͺ¨λ λ²κ·Έλ₯Ό νΌν μ μλλ‘ ν΄μ£Όμ§λ μλλ€. κ°λ°μκ° ν μ€νΈμ½λ μ체λ₯Ό μλͺ» μμ±νλ€λμ§, νμν ν μ€νΈλ₯Ό μμ±νμ§ λͺ»νλ€λμ§ νλ μ΄μ μμ λ°μνλ λ²κ·Έλ€λ μκΈ΄λ€.
κ³ λ―Όμ ν΄κ²°νμ λ λ€λ₯Έ κ³ λ―Όλ€μ΄ μ겨λ¬λ€.
μ΄μ²λΌ κ³ λ―Όμ κ³ λ―Όμ΄ λμμ΄ μ겨λκ³ μμ§λ§, κ·Έλλ κ²°λ‘ μ λμ νκΈΈ μνλ€ μ΄λ€. μ¬μ μ λ²κ·Έλ₯Ό μ°Ύμ κ²½νλ λ§μμ§κ³ μκ³ , λ°©λν ν μ€νΈλ₯Ό pushν λλ§λ€ μλμΌλ‘ λλ €μ£Όλ μ΄λ κ² νΈν μ μλ€. νμμ΄ 2λͺ μμ 3λͺ μ΄ λμμ λλ λ λΉμ λ°ν μ μλ λ¬Ένκ° λμ§ μμκΉ μΆλ€.
μμ§μ μμ£Ό κ°λ¨ν νλ‘μΈμ€μ΄μ§λ§, μμΌλ‘ μλνκ° νμν λΆλΆμ κ³Όκ°νκ² μ²λ¦¬νμ¬ ν¨μ¬ λ μμ°μ± μλ νμ λ§λ€κ³ μΆλ€.