Git 서버 훅
GitLab v19.5Offering: GitLab Self-Managed
요약
Git 서버 훅은 GitLab 서버에서 사용자 정의 로직을 실행합니다. Git 서버 훅은 pre-receive, post-receive, update Git 서버 사이드 훅을 사용합니다. GitLab 관리자는 gitaly 명령으로 서버 훅을 구성합니다.
Git 서버 훅은 GitLab 서버에서 사용자 정의 로직을 실행합니다. 다음과 같은 Git 관련 작업을 실행하는 데 사용할 수 있습니다.
- 특정 커밋 정책 적용
- 리포지터리 상태에 따른 작업 수행
Git 서버 훅은 pre-receive, post-receive, update Git 서버 사이드 훅을 사용합니다.
GitLab 관리자는 gitaly 명령으로 서버 훅을 구성합니다. 이 명령은 다음 역할도 합니다.
- Gitaly 서버를 실행합니다.
- 여러 하위 명령을 제공합니다.
- Gitaly gRPC API에 연결합니다.
gitaly 명령을 사용할 수 없다면 서버 훅의 대안은 다음과 같습니다.
- 웹훅
- GitLab CI/CD
- 사용자가 직접 구성하는 Git 훅 인터페이스인 푸시 규칙
GitLab Helm 차트 인스턴스는 Gitaly 차트의 글로벌 서버 훅 정보를 참고합니다.
Geo는 보조 노드로 서버 훅을 복제하지 않습니다.
사전 요구 사항#
- 리포지터리의 스토리지 이름, Gitaly 구성 파일 경로
(Linux 패키지 인스턴스의 기본값은
/var/opt/gitlab/gitaly/config.toml), 리포지터리 상대 경로가 필요합니다. - 훅에 필요한 언어 런타임과 유틸리티를 Gitaly를 실행하는 각 서버에 설치해야 합니다.
리포지터리에 서버 훅 설정#
리포지터리에 서버 훅을 설정하는 방법은 다음과 같습니다.
-
사용자 정의 훅이 담긴 tar 파일을 생성합니다.
-
서버 훅이 의도대로 동작하도록 코드를 작성합니다. Git 서버 훅은 어떤 프로그래밍 언어로도 작성할 수 있습니다. 맨 위의 shebang이 언어 유형과 맞는지 확인합니다. 예를 들어 스크립트가 Ruby라면 shebang은 대개
#!/usr/bin/env ruby입니다.- 서버 훅을 하나만 만들려면 훅 유형과 이름이 같은 파일을 만듭니다. 예를 들어
pre-receive서버 훅이라면 파일 이름은 확장자 없이pre-receive여야 합니다. - 서버 훅을 여러 개 만들려면 훅 유형과 이름이 같은 디렉터리를 만듭니다. 예를 들어
pre-receive서버 훅이라면 디렉터리 이름은pre-receive.d여야 합니다. 훅 파일은 그 디렉터리에 넣습니다.
- 서버 훅을 하나만 만들려면 훅 유형과 이름이 같은 파일을 만듭니다. 예를 들어
-
서버 훅 파일이 실행 가능하고 백업 파일 패턴(
*~)과 일치하지 않는지 확인합니다. 서버 훅은 tar 파일 루트의custom_hooks디렉터리에 있어야 합니다. -
tar 명령으로 사용자 정의 훅 아카이브를 생성합니다. 예를 들면
tar -cf custom_hooks.tar custom_hooks입니다.
-
-
리포지터리의 Git 훅을 설정하도록 필요한 옵션과 함께
hooks set하위 명령을 실행합니다. 예를 들면 다음과 같습니다.cat custom_hooks.tar | sudo -u git -- /opt/gitlab/embedded/bin/gitaly hooks set --storage <storage> --repository <relative path> --config <config path>-
노드에 연결하려면 해당 노드의 유효한 Gitaly 구성 경로가 필요하며, 이 경로를
--config플래그에 전달합니다. -
사용자 정의 훅 tar 파일은
stdin으로 전달해야 합니다. 예를 들면 다음과 같습니다.cat custom_hooks.tar | sudo -u git -- /opt/gitlab/embedded/bin/gitaly hooks set --storage <storage> --repository <relative path> --config <config path>
-
-
Gitaly 클러스터(Praefect)를 사용한다면 모든 Gitaly 노드에서
hooks set하위 명령을 실행해야 합니다.
서버 훅 코드를 올바르게 구현했다면 Git 훅이 다음에 트리거될 때 실행됩니다.
Gitaly 클러스터(Praefect)의 서버 훅#
Gitaly 클러스터(Praefect)를 사용하면 하나의 리포지터리가 Praefect의 여러 Gitaly 스토리지로 복제될 수 있습니다. 따라서 훅 스크립트를 해당 리포지터리의 복제본이 있는 모든 Gitaly 노드에 복사해야 합니다. 해당 버전의 사용자 정의 리포지터리 훅 설정 절차를 그대로 따르되 스토리지마다 반복합니다.
스크립트를 복사할 위치는 리포지터리가 저장된 위치에 따라 달라집니다. 새 리포지터리는 해시 스토리지 경로가 아니라
Praefect가 생성한 복제본 경로로 생성됩니다. 복제본 경로를 확인하려면 -relative-path 옵션으로
예상되는 GitLab 해시 스토리지 경로를 지정해
Praefect 리포지터리 메타데이터를 조회합니다.
모든 리포지터리에 적용되는 글로벌 서버 훅 생성#
모든 리포지터리에 적용되는 Git 훅을 만들려면 글로벌 서버 훅을 설정합니다. 글로벌 서버 훅은 다음에도 적용됩니다.
- 프로젝트 및 그룹 위키 리포지터리. 스토리지 디렉터리 이름은
<id>.wiki.git형식입니다. - 프로젝트 하위의 디자인 관리 리포지터리. 스토리지 디렉터리 이름은
<id>.design.git형식입니다.
서버 훅 디렉터리 선택#
글로벌 서버 훅을 만들기 전에 사용할 디렉터리를 먼저 선택해야 합니다.
디렉터리는 gitlab.rb의 gitaly['configuration'][:hooks][:custom_hooks_dir]에서 설정합니다. 다음 중 하나를 선택합니다.
- 주석을 해제해 기본 제안값인
/var/opt/gitlab/gitaly/custom_hooks디렉터리를 사용합니다. - 직접 설정값을 추가합니다.
- 디렉터리는
gitaly/config.toml의[hooks]섹션에서 설정합니다. 다만gitaly/config.toml의 값이 비어 있거나 없으면 GitLab은gitlab-shell/config.yml의custom_hooks_dir값을 사용합니다. - 기본 디렉터리는
/home/git/gitlab-shell/hooks입니다.
글로벌 서버 훅 생성#
모든 리포지터리에 적용되는 글로벌 서버 훅을 만드는 방법은 다음과 같습니다.
- GitLab 서버에서 구성된 글로벌 서버 훅 디렉터리로 이동합니다.
- 구성된 글로벌 서버 훅 디렉터리에 훅 유형과 이름이 같은 디렉터리를 만듭니다. 예를 들어
pre-receive서버 훅이라면 디렉터리 이름은pre-receive.d여야 합니다. - 이 새 디렉터리 안에 서버 훅을 추가합니다. Git 서버 훅은 어떤 프로그래밍 언어로도 작성할 수 있습니다. 맨 위의
shebang(
#!)이 언어 유형과 맞는지 확인합니다. 예를 들어 스크립트가 Ruby라면 shebang은 대개#!/usr/bin/env ruby입니다. - 훅 파일을 실행 가능하게 만들고, 소유자가 Git 사용자인지 확인하며, 백업 파일 패턴(
*~)과 일치하지 않는지 확인합니다.
서버 훅 코드를 제대로 구현했다면 Git 훅이 다음에 트리거될 때 실행됩니다. 훅은 훅 유형 하위 디렉터리에서 파일 이름의 알파벳 순으로 실행됩니다.
리포지터리의 서버 훅 제거#
서버 훅을 제거하려면 빈 tar 파일을 hook set에 전달해 리포지터리에 훅이 없어야 함을 알립니다. 예를 들면 다음과 같습니다.
cat empty_hooks.tar | sudo -u git -- /opt/gitlab/embedded/bin/gitaly hooks set --storage <storage> --repository <relative path> --config <config path>
연결된 서버 훅#
GitLab은 서버 훅을 체인으로 실행할 수 있습니다. GitLab은 다음 순서로 서버 훅을 검색하고 실행합니다.
- 내장 GitLab 서버 훅. 이 서버 훅은 사용자가 수정할 수 없습니다.
<project>.git/custom_hooks/<hook_name>: 프로젝트별 훅. 이 위치는 하위 호환성을 위해 유지됩니다.<project>.git/custom_hooks/<hook_name>.d/*: 프로젝트별 훅 위치.<custom_hooks_dir>/<hook_name>.d/*: 편집기 백업 파일을 제외한 모든 실행 가능한 글로벌 훅 파일 위치.
서버 훅 디렉터리에서 훅은 다음과 같이 동작합니다.
- 알파벳 순으로 실행됩니다.
- 훅이 0이 아닌 값으로 종료되면 실행이 중단됩니다.
서버 훅에서 사용할 수 있는 환경 변수#
서버 훅에 어떤 환경 변수든 전달할 수 있지만, 지원되는 환경 변수만 사용하는 것이 안전합니다.
모든 서버 훅에서 다음 GitLab 환경 변수를 지원합니다.
| 환경 변수 | 설명 |
|---|---|
GL_ID |
푸시를 시작한 사용자 또는 SSH 키의 GitLab 식별자. 예: user-2234 또는 key-4. |
GL_PROJECT_PATH |
GitLab 프로젝트 경로. |
GL_PROTOCOL |
이 변경에 사용된 프로토콜. http(HTTP를 사용한 Git push), ssh(SSH를 사용한 Git push), web(그 밖의 모든 작업) 중 하나. |
GL_REPOSITORY |
project- 접두사가 붙은 GitLab 프로젝트 ID. 예: project-1234 |
GL_USERNAME |
푸시를 시작한 사용자의 GitLab 사용자 이름. |
pre-receive와 post-receive 서버 훅에서 다음 Git 환경 변수를 지원합니다.
| 환경 변수 | 설명 |
|---|---|
GIT_ALTERNATE_OBJECT_DIRECTORIES |
격리 환경의 대체 오브젝트 디렉터리. |
GIT_OBJECT_DIRECTORY |
격리 환경의 GitLab 프로젝트 경로. |
GIT_PUSH_OPTION_COUNT |
푸시 옵션의 수. |
GIT_PUSH_OPTION_<i> |
특정 푸시 옵션의 값. <i>는 0부터 GIT_PUSH_OPTION_COUNT에 정의된 값보다 1 작은 값까지입니다. |
사용자 정의 오류 메시지#
서버 훅이 푸시를 거부할 때는 사용자가 거부 사유와 해결 방법을 알 수 있도록 명확한 오류 메시지를 제공합니다. 사용자 정의 오류 메시지는 훅이 푸시를 거부할 때 GitLab UI와 사용자 터미널에 표시됩니다.
사용자 정의 오류 메시지가 없으면 사용자에게는 (pre-receive hook declined) 같은 일반 메시지만 보입니다.
명확한 오류 메시지는 사용자가 다음과 같이 대처하도록 돕습니다.
- 푸시가 거부된 이유를 파악합니다.
- 관리자에게 문의하지 않고 문제를 해결합니다.
- 지원 요청을 줄입니다.
사용자 정의 오류 메시지를 표시하려면 스크립트가 다음을 수행해야 합니다.
- 사용자 정의 오류 메시지를 스크립트의
stdout또는stderr로 전송합니다. - 각 메시지 앞에
GL-HOOK-ERR:접두사를 붙이고, 접두사 앞에는 어떤 문자도 오지 않도록 합니다.
예를 들면 다음과 같습니다.
# Bad: Generic message
echo "GL-HOOK-ERR: Commit rejected.";
# Good: Specific message with action
echo "GL-HOOK-ERR: Commit rejected: Commit message must include an issue reference (for example, #1234).";
관련 주제#
문제 해결#
Git 서버 훅을 다룰 때 다음과 같은 문제가 발생할 수 있습니다.
오류: pre-receive hook declined#
사용자가 GitLab 리포지터리에 푸시할 때 (pre-receive hook declined)가 포함된 오류 메시지를
받을 수 있습니다. 예를 들면 다음과 같습니다.
! [remote rejected] main (pre-receive hook declined)
error: failed to push some refs to 'https://gitlab.example.com/group/project'
이 오류는 pre-receive 훅이 푸시를 거부했음을 의미합니다. pre-receive 훅은 리포지터리에서 참조가 갱신되기 전에 실행됩니다. Git은 푸시를 거부할 수 있는 서버 사이드 훅 세 가지를 제공합니다.
pre-receive: 참조가 갱신되기 전에 실행됩니다. 푸시 전체를 거부할 수 있습니다.update: 갱신되는 브랜치마다 한 번씩 실행됩니다. 개별 브랜치를 거부할 수 있습니다.post-receive: 모든 참조가 갱신된 뒤에 실행됩니다. 푸시를 거부할 수는 없지만 훅이 실패하면 오류가 발생할 수 있습니다.
(pre-receive hook declined) 오류는 보통 pre-receive 또는 update 훅에서 발생합니다.
원인을 확인하는 방법은 다음과 같습니다.
-
(pre-receive hook declined)메시지 바로 앞의 출력을 확인합니다. 이 출력에는 푸시가 거부된 이유가 담겨 있는 경우가 많습니다. 예를 들면 다음과 같습니다.remote: GitLab: The default branch of a project cannot be deleted. ! [remote rejected] main (pre-receive hook declined) -
훅이 실패한 이유를 자세히 확인하려면 Gitaly 로그를 확인합니다.
sudo grep PreReceiveHook /var/log/gitlab/gitaly/current | jq . -
리포지터리에 사용자 정의 서버 훅이 구성되어 있다면 사용자 정의 훅 코드에 문제가 없는지 검토합니다.
pre-receive 훅 실패의 일반적인 원인은 다음과 같습니다.
- 기본 브랜치 보호: 기본 브랜치를 삭제하거나 강제로 갱신하는 푸시는 거부됩니다.
소스 리포지터리의 기본 브랜치가 대상 리포지터리와 다를 때
git push --mirror에서 이 상황이 발생합니다. - 푸시 규칙: 커밋 메시지 요건, 파일 크기 제한, 작성자 이메일 제한 등 구성된 푸시 규칙을 푸시가 위반한 경우입니다.
- 사용자 정의 서버 훅: 사용자 정의 서버 훅 스크립트가 푸시를 거부한 경우입니다. 사용자 정의 훅 코드와 오류 메시지를 검토합니다.
- 타임아웃: 훅 실행이 너무 오래 걸려 중단된 경우입니다. Gitaly 로그에서 타임아웃 오류를 확인합니다.
- LFS 오브젝트: 필요한 Git LFS 오브젝트가 리포지터리에 없는 경우입니다.
사용자가 훅 실패를 이해하도록 돕고 푸시가 거부된 이유를 명확히 전달하려면 사용자 정의 오류 메시지를 사용합니다. 사용자 정의 오류 메시지는 GitLab UI와 사용자 터미널에 표시됩니다.