← 글 목록

notes

CICD - semantic-release

CI/CD 파이프라인에 semantic-release를 붙이는 설정·릴리스 흐름 메모.

커밋 메시지 컨벤션(feat/fix/…)만 맞춰 두면 버전·CHANGELOG·깃 태그를 자동으로 올려 주는 게 semantic-release다. Jenkinsfile 쪽은 MD - semantic-release 글에 따로 있다.

  • 커밋 메시지만 컨벤션대로 쓰면 버전, CHANGELOG, 태그를 CI가 알아서 올린다.
  • fix·docs 같은 건 patch, feat는 minor, BREAKING CHANGE는 major다.
  • 컨벤션에 안 맞는 커밋만 있으면 릴리스가 안 나간다. 태그가 안 생기면 커밋 로그부터 본다.

흐름

브랜치에 푸시될 때만 돌리게 해서, 로컬에서 버전을 수동으로 올리지 않는다. package.json version은 0.0.0-development처럼 두고 도구가 채우게 하는 편.

내가 쓰는 .releaserc.json

{
  "branches": ["main", "dev/test", "dev/style", "dev/kms"],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "@semantic-release/changelog",
    [
      "@semantic-release/git",
      {
        "message": "chore(release): v${nextRelease.version} [skip ci]"
      }
    ]
  ],
  "releaseRules": [
    { "type": "docs", "release": "patch" },
    { "type": "refactor", "release": "patch" },
    { "type": "style", "release": "patch" },
    { "type": "test", "release": "patch" },
    { "type": "chore", "release": "patch" },
    { "type": "feat", "release": "minor" },
    { "type": "fix", "release": "patch" }
  ]
}

포인트:

  • branches — main만이 아니라 팀에서 쓰는 dev/* 도 릴리스 대상으로 열어 둔 상태. 환경마다 태그 전략이 다르면 여기만 고친다.
  • @semantic-release/git message — 기본 메시지가 길면 커밋 메시지 길이 제한에 걸린다. 짧게 chore(release): v… [skip ci]로 고정.
  • releaseRules — docs/refactor/style/test/chore도 patch로 올려서 “문서만 고쳤는데 버전이 안 움직임”을 줄임. feat만 minor.

나중에 알게 된 건데, release 브랜치는 최대 3개까지다. 지금처럼 main에 dev/* 세 개를 다 넣으면 개수가 넘어서, dev 쪽은 { "name": "dev/test", "prerelease": "test" }처럼 prerelease로 돌리는 게 맞다. 그러면 1.4.0-test.1 같은 버전이 붙는다.

플러그인 순서

npm 배포·GitHub Release가 필요하면 @semantic-release/npm, @semantic-release/github를 추가한다. npm은 git보다 앞에 둬야 바뀐 package.json 버전이 같이 커밋된다. Jenkins에서 돌릴 때는 GH_TOKEN을 credentials로 넣고 npx semantic-release를 부른다.

처음 세팅할 때는 로컬에서 npx semantic-release --dry-run --no-ci로 다음 버전이 뭐로 나오는지만 확인했다. 실제 태그는 안 만든다.

GitHub Actions로 돌릴 때 워크플로

fetch-depth: 0을 빼먹으면 이전 태그를 못 찾아서 매번 1.0.0부터 다시 계산하려고 한다.

name: release
on:
  push:
    branches: [main]
permissions:
  contents: write
  issues: write
  pull-requests: write
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx semantic-release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

커밋 컨벤션이랑 세트

Husky + commitlint 쓸 때와 맞춘다.

feat: 기능
fix: 버그
docs / style / refactor / test / chore: 위 룰상 patch
BREAKING CHANGE: major (푸터나 ! 문법)

메시지가 컨벤션을 벗어나면 analyzer가 release를 안 올려서 “왜 태그가 안 되지?”가 된다. 그때는 커밋 로그부터 본다.