iBetter Books
수정

GitHub Actions에서 쉘 스크립트 활용

GitHub Actions는 GitHub 저장소에 내장된 CI/CD 플랫폼입니다. 코드를 푸시하거나 PR을 생성하는 이벤트에 반응해 자동으로 작업을 실행합니다. 그리고 그 작업의 대부분은 쉘 스크립트로 작성됩니다.

기본 구조

워크플로우 파일은 .github/workflows/ 디렉토리에 YAML 형식으로 작성합니다.

프로젝트/
├── .github/
│   └── workflows/
│       ├── ci.yml        ← CI 파이프라인
│       └── deploy.yml    ← 배포 파이프라인
├── scripts/
│   ├── ci_lint.sh
│   └── ci_test.sh
└── src/

워크플로우의 핵심 구조입니다.

# 새 파일: .github/workflows/ci.ymlname: CI Pipeline# 언제 실행할지on:  push:    branches: [ main, develop ]  pull_request:    branches: [ main ]# 실행할 작업들jobs:  build:    runs-on: ubuntu-24.04    steps:      - name: 저장소 체크아웃        uses: actions/checkout@v4      - name: 쉘 스크립트 실행        run: |          echo "Hello from GitHub Actions"          bash --version

run 키에서 쉘 스크립트 실행

run 키에 직접 쉘 명령을 작성합니다. 여러 줄은 |(파이프 블록)로 씁니다.

# 인라인 스크립트steps:  - name: 환경 정보 출력    run: |      echo "운영체제: $(uname -s)"      echo "아키텍처: $(uname -m)"      echo "Bash 버전: $BASH_VERSION"      echo "현재 디렉토리: $(pwd)"

외부 .sh 파일을 호출할 수도 있습니다. 복잡한 로직은 별도 파일로 관리하는 것이 더 좋습니다.

# 외부 스크립트 호출steps:  - name: 저장소 체크아웃    uses: actions/checkout@v4  - name: 스크립트 실행 권한 부여    run: chmod +x scripts/ci_lint.sh scripts/ci_test.sh  - name: 린트 검사    run: bash scripts/ci_lint.sh  - name: 테스트 실행    run: bash scripts/ci_test.sh

환경 변수와 시크릿

GitHub Actions에는 두 종류의 변수가 있습니다. 환경 변수는 워크플로우 파일에서 직접 설정하고, 시크릿은 GitHub 저장소 설정에서 암호화된 형태로 관리합니다.

# 환경 변수와 시크릿 활용jobs:  deploy:    runs-on: ubuntu-24.04    # 워크플로우 레벨 환경 변수    env:      APP_NAME: myapp      DEPLOY_DIR: /var/www/myapp    steps:      - uses: actions/checkout@v4      - name: 배포 환경 확인        run: |          echo "브랜치: ${{ github.ref_name }}"          echo "커밋: ${{ github.sha }}"          echo "앱 이름: $APP_NAME"      - name: SSH 키 설정        run: |          mkdir -p ~/.ssh          echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_rsa          chmod 600 ~/.ssh/id_rsa          ssh-keyscan -H "${{ secrets.DEPLOY_HOST }}" >> ~/.ssh/known_hosts      - name: 서버에 배포        run: |          ssh "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" \            "cd $DEPLOY_DIR && git pull origin main && ./scripts/restart.sh"

${{ secrets.KEY }} 형식으로 시크릿을 참조합니다. 시크릿 값은 로그에 출력되지 않도록 GitHub Actions가 자동으로 가립니다.

조건부 실행

if 조건으로 특정 상황에서만 스텝을 실행합니다.

steps:  - name: main 브랜치에서만 배포    if: github.ref == 'refs/heads/main'    run: echo "프로덕션 배포 실행"  - name: PR에서만 실행    if: github.event_name == 'pull_request'    run: echo "PR 검사 실행"  - name: 이전 스텝 실패 시 알림    if: failure()    run: echo "파이프라인 실패 알림 전송"

실습: CI 워크플로우 전체 코드

린트, 테스트, 빌드를 순서대로 실행하는 CI 파이프라인을 작성합니다.

# 새 파일: .github/workflows/ci.ymlname: CI Pipelineon:  push:    branches: [ main, develop ]  pull_request:    branches: [ main ]jobs:  # ─── 린트 검사 ────────────────────────────────────────────────────────────  lint:    name: 린트 검사    runs-on: ubuntu-24.04    steps:      - name: 저장소 체크아웃        uses: actions/checkout@v4      - name: ShellCheck 설치        run: sudo apt-get install -y shellcheck      - name: 스크립트 실행 권한        run: chmod +x scripts/ci_lint.sh      - name: 린트 실행        run: bash scripts/ci_lint.sh  # ─── 테스트 ───────────────────────────────────────────────────────────────  test:    name: 테스트    runs-on: ubuntu-24.04    needs: lint   # lint 완료 후 실행    steps:      - name: 저장소 체크아웃        uses: actions/checkout@v4      - name: 스크립트 실행 권한        run: chmod +x scripts/ci_test.sh      - name: 테스트 실행        run: bash scripts/ci_test.sh      - name: 테스트 결과 업로드        if: always()        uses: actions/upload-artifact@v4        with:          name: test-results          path: test-results/  # ─── 매트릭스 빌드 ────────────────────────────────────────────────────────  # 매트릭스는 여러 환경 조합을 자동으로 테스트하는 기능입니다.  # 여기서는 OS 조합으로 호환성을 검증합니다.  # (각 OS에 사전 설치된 Bash 버전이 사용됩니다)  matrix-test:    name: 호환성 테스트 (${{ matrix.os }})    runs-on: ${{ matrix.os }}    needs: lint    strategy:      matrix:        os: [ ubuntu-22.04, ubuntu-24.04 ]      fail-fast: false   # 하나 실패해도 나머지 계속 실행    steps:      - uses: actions/checkout@v4      - name: Bash 버전 확인        run: bash --version      - name: 스크립트 테스트        run: bash scripts/ci_test.sh
#!/bin/bash# 새 파일: scripts/ci_lint.sh# CI 린트 검사 스크립트set -euo pipefailGREEN='\033[0;32m'RED='\033[0;31m'NC='\033[0m'echo "ShellCheck 린트 검사를 시작합니다..."echo ""ERRORS=0# 모든 쉘 스크립트 검사while IFS= read -r -d '' script; do    echo -n "검사 중: $script ... "    if output=$(shellcheck "$script" 2>&1); then        echo -e "${GREEN}통과${NC}"    else        echo -e "${RED}실패${NC}"        echo "$output"        ERRORS=$((ERRORS + 1))    fidone < <(find scripts/ -name "*.sh" -print0 2>/dev/null)echo ""if [ $ERRORS -gt 0 ]; then    echo -e "${RED}린트 실패: ${ERRORS}개 파일에서 오류 발견${NC}"    exit 1fiecho -e "${GREEN}모든 린트 검사 통과${NC}"exit 0
#!/bin/bash# 새 파일: scripts/ci_test.sh# CI 테스트 스크립트set -euo pipefailGREEN='\033[0;32m'RED='\033[0;31m'NC='\033[0m'RESULTS_DIR="test-results"mkdir -p "$RESULTS_DIR"PASSED=0FAILED=0# 간단한 테스트 함수assert_equal() {    local description=$1    local expected=$2    local actual=$3    if [ "$expected" = "$actual" ]; then        echo -e "${GREEN}  PASS${NC}: $description"        PASSED=$((PASSED + 1))    else        echo -e "${RED}  FAIL${NC}: $description"        echo "    기대값: $expected"        echo "    실제값: $actual"        FAILED=$((FAILED + 1))    fi}echo "테스트를 시작합니다..."echo ""# 테스트 실행 (실제 프로젝트에 맞게 수정)source scripts/lib.sh 2>/dev/null || trueassert_equal "Bash 버전 5 이상" "5" "${BASH_VERSINFO[0]}"assert_equal "운영체제 확인" "Linux" "$(uname -s)"# 테스트 결과 저장cat > "$RESULTS_DIR/summary.txt" << EOF테스트 결과: $(date)통과: $PASSED실패: $FAILEDEOFecho ""echo "결과: 통과 $PASSED, 실패 $FAILED"if [ $FAILED -gt 0 ]; then    exit 1fiecho -e "${GREEN}모든 테스트 통과${NC}"exit 0

실행 결과 확인

워크플로우 파일을 저장소에 푸시하면 GitHub 저장소의 Actions 탭에서 실행 상태를 확인할 수 있습니다.

CI Pipeline — 실행 중
├── lint         ✓ 통과 (12초)
├── test         ✓ 통과 (8초)
└── matrix-test
    ├── ubuntu-22.04  ✓ 통과
    └── ubuntu-24.04  ✓ 통과

매트릭스 빌드는 여러 환경에서 동시에 테스트를 실행해 호환성을 보장합니다. 각 OS 조합이 별도의 가상 머신에서 병렬로 실행됩니다.