Programming/Git

husky로 git hook에 conventional commit 적용하기

WANJIN 2021. 5. 15. 10:44
반응형

Conventional Commits?

커밋 메세지에 사용자와 기계 모두가 이해할 수 있는 의미를 부여하기 위한 스펙

명확한 커밋 히스토리를 생성하기 위한 간단한 규칙을 제공

커밋 히스토리를 이용하여 더 쉽게 자동화된 도구를 만듦

이 컨벤션은 커밋 메세지에 신규 기능 추가, 문제 수정, 커다란 변화가 있음을 기술함으로써 유의적 버전(Sementic Versioning)과 일맥상통

git 으로 commit 시에 일괄된 양식을 유지 → 그 양식을 바탕으로 버전 관리나 Change Log 를 자동으로 만들 수 있음

커밋 메시지 구조

<타입>[적용 범위(선택 사항)]: <설명>

[본문(선택 사항)]

[꼬리말(선택 사항)]

커밋 메시지 구조적 요소

Git hook에 Conventional Commit 적용하기

Commit 메시지를 아름답고 정갈하게 유지하기 위해 conventional commit을 git hook에 적용하는 과정을 소개하려고 한다.

1. 패키지에 husky 적용하기

husky 가 뭔가요?

husky는 git hook을 손쉽게 제어하도록 도와주는 npm 라이브러리이다.

git hook?

git을 쓰다가 특정 이벤트(커밋할 때, 푸시할 때 등등)가 벌어졌을 때, 그 순간에 ‘갈고리’를 걸어서 특정 스크립트가 실행되도록 도와주는 것!

물론 husky를 쓰지 않더라도 git hook을 설정할 수 있는 공식적인 방법은 따로 있다.
.git/hooks 폴더에 들어가서 스크립트를 작성하면 된다.

그러나 .git/hooks 폴더 안에 스크립트 파일을 넣게 되면 그 파일은 git에 기록되지 않아서 따로 관리해야 한다는 단점이 있다.

git hook으로 npm scripts를 제어하고 싶을 때, 예컨대 npm test 등의 명령어를 써야 한다면 스크립트를 작성하는 게 번거롭다.

husky는 굳이 .git/hooks 폴더를 건드리지 않고도 git hook 스크립트를 제어할 수 있게 도와준다.

husky 설치하기

npx husky-init && npm install

package.json 파일에는 아래와 같은 script가 추가된 것을 확인할 수 있다.

"prepare": "husky install"

그리고, .husky 디렉토리가 생성된 것도 확인할 수 있다.

.husky/pre-commit 파일이 생성되었는데, 기존에 test 명령어가 있었기 때문에 이를 감지하여 아래와 같이 추가되었다.

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm test

2. 패키지에 commitlint 적용하기

commitlint 는 뭔가요?

commit 에 대한 lint를 확인하여 성공/실패를 리턴해주는 도구이다.

commitlint 설치하기

npm install -D @commitlint/cli @commitlint/config-conventional

commitlint config 추가하기

루트 디렉토리에 commitlint.config.js 파일을 추가해준다.

module.exports = { extends: ['@commitlint/config-conventional'] };

commitlint의 기본 컨벤션

module.exports = {
    parserPreset: 'conventional-changelog-conventionalcommits',
    rules: {
        'body-leading-blank': [1, 'always'],
        'body-max-line-length': [2, 'always', 100],
        'footer-leading-blank': [1, 'always'],
        'footer-max-line-length': [2, 'always', 100],
        'header-max-length': [2, 'always', 100],
        'subject-case': [
            2,
            'never',
            ['sentence-case', 'start-case', 'pascal-case', 'upper-case'],
        ],
        'subject-empty': [2, 'never'],
        'subject-full-stop': [2, 'never', '.'],
        'type-case': [2, 'always', 'lower-case'],
        'type-empty': [2, 'never'],
        'type-enum': [
            2,
            'always',
            [
                'build',
                'chore',
                'ci',
                'docs',
                'feat',
                'fix',
                'perf',
                'refactor',
                'revert',
                'style',
                'test',
            ],
        ],
    },
};

3. commit-msg hook 적용하기

commit-msg 훅은 최종적으로 커밋이 완료되기 전에, 프로젝트 상태나 커밋 메시지를 검증하기 위해 사용한다.

husky hook 에 commit-msg 추가하기

npx husky add .husky/commit-msg 'npx --no-install commitlint --edit $1'

이렇게 하면, commitlint 의 컨벤션으로 husky가 commit-msg 훅을 실행한다.

commit-msg hook 동작 확인하기

그럼 어디한번 동작하는지 확인해볼까.

우선 잘못된 커밋 메시지를 넣어보자.

git commit -m 'hello-world'

두둥탁- 짠-.

subject 가 비어있으면 안되고, type 도 비어있으면 안된다고 지적해준다.

이번엔 제대로된 메시지를 넣어보자.

git commit -m 'test: hello world'

아주아주 잘되는 것을 확인할 수 있다.

git kraken 에서 동작 확인하기

야심차게 git kraken 에서도 잘못된 커밋메시지로 커밋을 시도해봤다.

그런데 웬걸 😱

커밋이 너무 잘된다... 젠장.

commit-msg 훅 로그를 들여다보니.

별 말도 없다. 어쩌란 건지..

그래서 issue tracking 을 해보니, 아래와 같은 문제점이 있다고 한다.

Husky v5 and Gitkraken #875

  • 버전 7.5부터 Gitkraken은 core.hookspath를 지원하지 않으며 .git/hooks을 사용할 것입니다.이 문제를 해결하는 방법은 매우 간단합니다.
    .git/hooks 에서 허스키의 디렉토리로의 심볼릭 링크를 만듭니다.
    최선의 방법은 아니지만 내가 찾은 유일한 방법입니다.

      $ rm -rf .git/hooks && ln -s ../.husky .git/hooks

    You may add this command to your package.json postinstall script, alongside husky install:

  • { "scripts": { "postinstall": "husky install && rm -rf .git/hooks && ln -s ../.husky .git/hooks" } }

  • From you project root:

  • 참고로, git 공식 문서에 이런 글이 있다.
    By default the hooks directory is $GIT_DIR/hooks, but that can be changed via the core.hooksPath configuration variable.

  • husky v5부터는 기본 git의 경로 ( .git/hooks)가 아닌 사용자 지정 후크 경로 ( .husky) 가 사용됩니다 . 이것은 로컬 git 구성 core.hookspath키 설정을 통해 수행됩니다.

하...

이렇게까지 해서 맥 Big Sur OS 에도 최적화되지 못한 채, 커밋 날릴때마다 7초 이상 기다리게 하더니, 이제는 깃훅 path 커스텀도 막아버리는 무례한 깃크라켄의 만행을, 돈을 내가면서 참아줘야 하는걸까.

결국 했다.

gitkraken 을 사용하지 않는 컨트리뷰터들을 위해 아래 postinstall script 는 추가하지 않고, 로컬에서만 .git/hooks 에 심볼링 링크를 걸어주는 것으로 끝냈다.

4. 컨벤션에 맞게 커밋하기

컨벤션에 맞게 커밋하는 것을 도와주는 commitizen cli 도구를 사용하려고 한다.

commitizen 설치하기

npm i -D commitizen

commitizen 어댑터 구성하기

commitizen으로 커밋을 만들기 위한 방식은 어떤 어댑터를 사용하느냐에 따라 달라진다.

여기에서는 cz-conventional-changelog 어댑터 사용할 것이다.

npx commitizen init cz-conventional-changelog --save-dev --save-exact

위 명령어는 아래 3가지를 수행한다.

  1. cz-conventional-changelog 어댑터를 설치한다.
  2. 그리고 dev dependency에 추가한다.
  3. 아래처럼 config.commitizenpackage.json 에 추가한다.
"config": {
    "commitizen": {
      "path": "cz-conventional-changelog"
    }
  }

이렇게 설정하면, 커밋하려고 할 때 사용할 어댑터를 commitizen에게 알려준다.

commitizen script 추가하기

package.json에 아래와 같은 script 를 추가한다.

husky와 함께 쓰는 경우는 commit 명령어로 쓰지 말라는 경고가 있으니 주의하자.

"scripts": {
...
    "cm": "cz"
  }
npx cz

or

npm run cm

위 명령어를 통해 commitizen으로 커밋을 정형화된 포맷으로 할 수 있다.

그러나, git commit 명령어를 실행시켰을 때, commitizen 을 먼저 실행시킬 수 있도록 설정한다면, 굳이 이 명령어를 쓰지 않아도 된다.

커밋 시 commitzen 실행하도록 husky에 훅 추가하기

npx husky add .husky/prepare-commit-msg 'exec < /dev/tty && node_modules/.bin/cz --hook || true'

commitizen 으로 커밋하기

git commit
  1. 원하는 type 선택하기
  2. 필요한 설명 입력하기
  3. 이슈에 연결하기(선택사항)
  4. 결과 확인

5. commitizen 어댑터 변경하기 (선택사항)

엇, 그런데 commitizen 에서 제공하는 어댑터 중에 commitlint 를 사용하는 것이 있다는 것을 뒤늦게 알아버렸다.

위에 commit-msg 훅에서 commitlint를 사용하고 있어서, 일관성 유지에는 commitlint를 사용하는 것이 좋을 것 같아서 commitlint 어댑터로 바꿔보겠다.

기존 어댑터 삭제하기

위에서 추가해준 cz-conventional-changelog 어댑터를 삭제해주자.

npm uninstall -D cz-conventional-changelog

새로운 어댑터 추가하기

npm i -D @commitlint/prompt

commitizen 어댑터 설정 변경하기

아래처럼 package.json 에서 config.commitizen 을 변경해주자.

"script": {
...
"cm": "git-cz"
},
"config": {
    "commitizen": {
       "path": "./node_modules/@commitlint/prompt"
    }
  }

변경된 어댑터 동작 확인하기

git commit

전혀 다른 방식으로 커밋이 진행되는 것을 확인할 수 있다.

마치며.

마지막에 바꾼 어댑터.
써보니까 너무 별로여서 다시 기존 어댑터로 롤백했다.
ㅎㅎ..

그로부터 며칠 뒤.
prepare-commit-msg 훅을 뻈다.
이제 대충 커밋 컨벤션이 머리에 있는데 저 과정으로 커밋하는게 영 불편했다.
그래서 필요한 경우에는 npm run cm 으로 실행시키는 것으로.

References

https://www.huskyhoochu.com/npm-husky-the-git-hook-manager/

https://git-scm.com/book/ko/v2/Git맞춤-Git-Hooks

https://blog.cookapps.io/guide/conventional-commits/

https://www.conventionalcommits.org/ko/v1.0.0/

https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines

반응형