Gradle git hook

Gradle plugin을 검색해보면 3rd party 플러그인들이 몇개 나온다. 그나마 릴리즈가 좀 되었고 최신까지 관리가 된 com.star-zero.gradle.githook 플러그인을 골라봤다. 사실 이 플러그인이 뒤에 설명할 commitlint 플러그인에서 함께 사용하고 있어서 고른 부분이 더 크다.

Install

2021.06.10 기준 최신버전은 v1.2.1이다. (2020.08.29 이후로 업데이트가 없다.)

plugins {
  id "com.star-zero.gradle.githook" version "1.2.1"
}

Legacy에서는 아래와 같이 적용 가능하다.

buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "com.star-zero.gradle:githook:1.2.1"
  }
}

apply plugin: "com.star-zero.gradle.githook"

Configuration

build.gradle에 아래와 같이 추가한다. 이후 gradle을 reload하면 .git/hooks 하위에 스크립트가 생성된다.

githook {
    gradleCommand = file("gradle_test")
    hooksDir = file(new File(rootDir, "githook_test/hooks"))
    failOnMissingHooksDir = false
    createHooksDirIfNotExist = false
    hooks {
        "pre-commit" {
            task = "lint test"
            shell = "echo 1"
        }
        "pre-push" {
            task = "someTask"
            shell = "someShell"
        }
    }
}
  • gradleCommand

    • Gradle command file
    • Default: <root_dir>/gradlew
  • hooksDir

    • Git hook directory. git init을 실행하면 기본값대로 생성된다.
    • Default: <root_dir>/.git/hooks
  • failOnMissingHooksDir

    • hooksDir이 없으면 빌드가 실패해야 하는지 여부를 나타낸다.
    • Default: true
  • createHooksDirIfNotExist

    • hooksDir이 없으면 새로 생성한다. (제곧내)
    • Default: false
  • hooks

    • Git hook 스크립트 파일명이 온다. (e.g. commit-msg)
    • 내부에는 gradle task나 shell 명령어를 정의하고 이는 git hook 스크립트에 복사된다.

Commitlint

역시나 gradle plugin 중 가장 최근까지 관리되고 있는 프로젝트로 골랐다. gradle-commitlint-pluginConventional Commits 룰에 대한 linting을 제공한다.

Configuration

build.gradle

plugins {
  id "com.star-zero.gradle.githook" version "1.2.1"
  id "ru.netris.commitlint" version "1.4.1"
}

githook {
  failOnMissingHooksDir = false
  createHooksDirIfNotExist = false
  hooks {
    "commit-msg" {
      task = "commitlint -Dmsgfile=\$1"
    }
  }
}

settings.gradle

pluginManagement {
  repositories {
	gradlePluginPortal()
  }
  resolutionStrategy {
	eachPlugin {
      if (requested.id.id == "ru.netris.commitlint") {
        useModule("ru.netris:commitlint-plugin:${requested.version}")
      }
		}
  }
}

rootProject.name = '***'

Test

커밋 컨벤션을 지키지 않으면 다음과 같은 에러 메시지를 볼 수 있다.

> Task :commitlint FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':commitlint'.
> Invalid commit type. See https://www.conventionalcommits.org/en/v1.0.0/

컨벤션을 지켜 입력하여 커밋에 성공했다.

> Task :commitlint
commitlint finished successfully

BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
[feat-lint c319439] chore(core): commitlint plugin 적용
 2 files changed, 25 insertions(+), 1 deletion(-)

결론

커밋 컨벤션은 문서에도 잘 설명되어 있지만 개발 이후 수반되는 빌드/배포 프로세스를 자동화 뿐만 아니라 해당 프로젝트를 보는 모든 사람들에게 변경된 사항을 제대로 알리기 위한 부분도 커 보인다. 팀 프로젝트를 많이 해왔지만 소규모 팀에서조차 이런 내용들이 제대로 전달되지 않아 배포시에 누락되거나 충돌 해결을 위해 대면하며 일일이 확인했던 기억들이 있다. 물론 초기 적용 시에 불편한 부분도 있을 이지만 잘 정착되도록 문화를 만들어가는 것은 중요한 부분 같다.