포스트

[GitHub 100일 챌린지] Day 86 - 커스텀 Actions 만들기

[GitHub 100일 챌린지] Day 86 - 커스텀 Actions 만들기

100일 챌린지 Day 86 - 재사용 가능한 커스텀 Actions를 만들어봅니다.

배울 내용

  1. Actions 유형 이해
  2. JavaScript Action 만들기
  3. Action 공유하기

Actions 유형

1. JavaScript Action

1
2
3
4
5
6
7
8
9
특징:
- Node.js로 작성
- 빠른 실행
- GitHub API 쉽게 사용

사용처:
- API 호출
- 파일 처리
- 간단한 자동화

2. Docker Action

1
2
3
4
5
6
7
8
9
특징:
- 모든 언어 사용 가능
- 독립적인 환경
- 복잡한 작업 가능

사용처:
- 특정 언어/도구 필요
- 복잡한 빌드
- 시스템 도구 사용

3. Composite Action

1
2
3
4
5
6
7
8
특징:
- 여러 Step 조합
- YAML로 작성
- 재사용성 높음

사용처:
- 반복되는 Step 패턴
- 워크플로우 템플릿

JavaScript Action 만들기

프로젝트 구조

1
2
3
4
5
my-action/
├── action.yml       # Action 메타데이터
├── index.js         # 메인 코드
├── package.json
└── README.md

1. action.yml 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
name: 'Hello Action'
description: '인사 메시지를 출력합니다'
author: 'Your Name'

inputs:
  who-to-greet:
    description: '인사할 대상'
    required: true
    default: 'World'

outputs:
  greeting-message:
    description: '생성된 인사 메시지'

runs:
  using: 'node20'
  main: 'index.js'

2. package.json 작성

1
2
3
4
5
6
7
8
9
{
  "name": "hello-action",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "@actions/core": "^1.10.0",
    "@actions/github": "^5.1.1"
  }
}

3. index.js 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const core = require('@actions/core');
const github = require('@actions/github');

try {
  // Input 가져오기
  const nameToGreet = core.getInput('who-to-greet');
  
  // 로직 실행
  const message = `Hello ${nameToGreet}!`;
  console.log(message);
  
  // Output 설정
  core.setOutput('greeting-message', message);
  
  // GitHub 컨텍스트
  const payload = JSON.stringify(github.context.payload, null, 2);
  console.log(`이벤트 payload: ${payload}`);
  
} catch (error) {
  core.setFailed(error.message);
}

4. 의존성 설치

1
npm install

Action 사용하기

로컬 저장소에서

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# .github/workflows/test.yml
name: Test Action

on: push

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Hello Action 사용
        uses: ./  # 현재 저장소의 Action
        with:
          who-to-greet: 'GitHub'

다른 저장소에서

1
2
3
4
steps:
  - uses: username/my-action@v1
    with:
      who-to-greet: 'GitHub'

Composite Action 만들기

action.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
name: 'Node.js 설정  테스트'
description: 'Node.js 설정, 의존성 설치, 테스트 실행'

inputs:
  node-version:
    description: 'Node.js 버전'
    required: true
    default: '20'

runs:
  using: 'composite'
  steps:
    - name: Node.js 설정
      uses: actions/setup-node@v4
      with:
        node-version: $
        cache: 'npm'
    
    - name: 의존성 설치
      shell: bash
      run: npm ci
    
    - name: 테스트 실행
      shell: bash
      run: npm test

사용

1
2
3
4
5
6
steps:
  - uses: actions/checkout@v4
  
  - uses: username/setup-and-test@v1
    with:
      node-version: '18'

Docker Action 만들기

프로젝트 구조

1
2
3
4
5
my-docker-action/
├── action.yml
├── Dockerfile
├── entrypoint.sh
└── README.md

action.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
name: 'Docker Hello'
description: 'Docker 기반 Action'

inputs:
  name:
    description: '이름'
    required: true

runs:
  using: 'docker'
  image: 'Dockerfile'
  args:
    - $

Dockerfile

1
2
3
4
5
6
FROM alpine:latest

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh

1
2
3
#!/bin/sh

echo "Hello $1!"

실전 예제: PR 크기 체크

action.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
name: 'PR Size Checker'
description: 'PR의 변경 라인 수를 확인합니다'

inputs:
  max-lines:
    description: '최대 허용 라인 수'
    required: true
    default: '500'

outputs:
  is-valid:
    description: 'PR이 유효한지 여부'

runs:
  using: 'node20'
  main: 'index.js'

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const core = require('@actions/core');
const github = require('@actions/github');

async function run() {
  try {
    const maxLines = parseInt(core.getInput('max-lines'));
    const token = process.env.GITHUB_TOKEN;
    const octokit = github.getOctokit(token);
    
    const { context } = github;
    const pr = context.payload.pull_request;
    
    if (!pr) {
      core.setFailed('PR 정보를 찾을 수 없습니다');
      return;
    }
    
    const { data: files } = await octokit.rest.pulls.listFiles({
      owner: context.repo.owner,
      repo: context.repo.repo,
      pull_number: pr.number,
    });
    
    let totalChanges = 0;
    files.forEach(file => {
      totalChanges += file.additions + file.deletions;
    });
    
    console.log(`총 변경 라인: ${totalChanges}`);
    console.log(`최대 허용 라인: ${maxLines}`);
    
    const isValid = totalChanges <= maxLines;
    core.setOutput('is-valid', isValid);
    
    if (!isValid) {
      core.setFailed(
        `PR이 너무 큽니다! (${totalChanges} > ${maxLines})`
      );
    } else {
      console.log('✅ PR 크기가 적절합니다');
    }
    
  } catch (error) {
    core.setFailed(error.message);
  }
}

run();

사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
name: Check PR Size

on: pull_request

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: username/pr-size-checker@v1
        with:
          max-lines: 500
        env:
          GITHUB_TOKEN: $

Action 배포

버전 태그

1
2
3
4
5
6
7
# 태그 생성
git tag -a v1.0.0 -m "First release"
git push origin v1.0.0

# 메이저 버전 태그 (권장)
git tag -fa v1 -m "Update v1"
git push origin v1 --force

사용자들이 사용

1
2
3
4
5
# 특정 버전
- uses: username/my-action@v1.0.0

# 메이저 버전 (자동 업데이트)
- uses: username/my-action@v1

정리

완료 체크:

  • Actions 유형 이해
  • JavaScript Action 작성
  • Composite Action 작성
  • Action 배포

핵심 요약:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Action 유형:
- JavaScript: 빠르고 간단
- Docker: 모든 언어 지원
- Composite: Step 재사용

필수 파일:
- action.yml: 메타데이터
- index.js 또는 Dockerfile
- README.md

배포:
- Git 태그 사용
- v1, v1.0.0 형식
- README에 사용 예시

다음: Day 87 - 캐시와 Artifacts →


← Day 86 | 전체 커리큘럼 | Day 87 →

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.