Skip to main content

사전 수신 후크 스크립트 만들기

사전 수신 후크 스크립트를 사용하여 내용에 따라 푸시를 수락 또는 거부하기 위한 요구 사항을 만듭니다.

github/platform-samples 리포지토리에서 GitHub Enterprise Server에 대한 사전 수신 후크의 예를 볼 수 있습니다.

사전 수신 후크 스크립트 작성

GitHub Enterprise Server 인스턴스의 사전 수신 후크 환경에서 사전 수신 후크 스크립트가 실행됩니다. 사전 수신 후크 스크립트를 만들 때 사용 가능한 입력, 출력, 종료 상태 및 환경 변수를 고려합니다.

입력(stdin)

푸시가 발생하고 원격 리포지토리에 대한 참조가 업데이트되기 전에 GitHub Enterprise Server 인스턴스의 git-receive-pack 프로세스가 사전 수신 후크 스크립트를 호출합니다. 스크립트에 대한 표준 입력 stdin은 업데이트할 각 참조에 대한 줄을 포함하는 문자열입니다. 각 줄에는 ref의 이전 개체 이름, ref의 새 개체 이름 및 ref의 전체 이름이 포함됩니다.

<old-value> SP <new-value> SP <ref-name> LF

이 문자열은 다음 인수를 나타냅니다.

인수설명
<old-value>ref에 저장된 이전 개체 이름입니다.
새 ref를 만들 때 값은 40개의 0입니다.
<new-value>ref에 저장할 새 개체 이름입니다.
ref를 삭제하면 값은 40개의 0입니다.
<ref-name>ref의 전체 이름입니다.

git-receive-pack에 대한 자세한 내용은 Git 설명서에서 "git-receive-pack"을 참조하세요. ref에 대한 자세한 내용은 _Pro Git_의 "Git 참조"를 참조하세요.

출력(stdout)

스크립트에 대한 표준 출력 stdout은 클라이언트에 다시 전달됩니다. 명령줄 또는 사용자 인터페이스에서 모든 echo 문이 사용자에게 표시됩니다.

종료 상태

사전 수신 스크립트의 종료 상태는 푸시가 수락되는지 여부를 결정합니다.

종료 상태 값작업
0푸시가 수락됩니다.
0이 아닌 값푸시가 거부됩니다.

환경 변수

사전 수신 후크 스크립트에 대한 표준 입력 stdin 외에도 GitHub Enterprise Server는 스크립트 실행을 위해 Bash 환경에서 다음 변수를 사용할 수 있도록 합니다. 사전 수신 후크 스크립트의 stdin에 대한 자세한 내용은 "입력(stdin)"을 참조하세요.

스크립트를 실행할 트리거에 따라 사전 수신 후크 스크립트에 다양한 환경 변수를 사용할 수 있습니다.

항상 사용 가능

다음 변수는 항상 사전 수신 후크 환경에서 사용할 수 있습니다.

변수설명예제 값
$GIT_DIR
인스턴스의 원격 리포지토리 경로/data/user/repositories/a/ab/
a1/b2/34/100001234/1234.git
$GIT_PUSH_OPTION_COUNT
--push-option이 있는 클라이언트에서 보낸 푸시 옵션 수입니다. 자세한 내용은 Git 설명서의 “git-push”를 참조하세요.1
$GIT_PUSH_OPTION_N
N이 0부터 시작하는 정수인 경우 이 변수에는 클라이언트에서 보낸 푸시 옵션 문자열이 포함됩니다. 전송된 첫 번째 옵션은 GIT_PUSH_OPTION_0에 저장되고, 전송된 두 번째 옵션은 GIT_PUSH_OPTION_1에 저장되는 식입니다. 푸시 옵션에 대한 자세한 내용은 Git 설명서의 "git-push"를 참조하세요.abcd
$GIT_USER_AGENT
변경 내용을 푸시한 Git 클라이언트에서 보낸 사용자 에이전트 문자열git/2.0.0
$GITHUB_REPO_NAME
NAME/OWNER 형식으로 업데이트되는 리포지토리의 이름octo-org/hello-enterprise
$GITHUB_REPO_PUBLIC
업데이트되는 리포지토리가 퍼블릭인지 여부를 나타내는 부울
  • true: 리포지토리의 표시 여부가 퍼블릭임
  • false: 리포지토리의 표시 여부가 프라이빗 또는 내부임
$GITHUB_USER_IP
푸시를 시작한 클라이언트의 IP 주소192.0.2.1
$GITHUB_USER_LOGIN
푸시를 시작한 계정의 사용자 이름octocat

웹 인터페이스 또는 API의 푸시에 사용 가능

$GITHUB_VIA 변수는 웹 인터페이스 또는 GitHub Enterprise Server에 대한 API를 통해 후크를 트리거하는 ref 업데이트가 발생할 때 사전 수신 후크 환경에서 사용할 수 있습니다. 값은 ref를 업데이트한 작업을 설명합니다.

작업자세한 정보
auto-merge deployment api
API를 사용하여 만든 배포를 통해 기본 분기 자동 병합"배포에 대한 REST API 엔드포인트"
blob#save
웹 인터페이스에서 파일의 콘텐츠로 변경"파일 편집"
branch merge api
API를 통해 분기 병합"분기 및 해당 설정에 대한 REST API 엔드포인트"
branches page delete button
웹 인터페이스에서 분기 삭제"리포지토리 내에서 분기 만들기 및 삭제"
git refs create api
API를 통해 ref 만들기"Git 참조에 대한 REST API 엔드포인트"
git refs delete api
API를 통해 ref 삭제"Git 참조에 대한 REST API 엔드포인트"
git refs update api
API를 통한 ref 업데이트"Git 참조에 대한 REST API 엔드포인트"
git repo contents api
API를 통해 파일의 콘텐츠로 변경"리포지토리 콘텐츠에 대한 REST API 엔드포인트"
merge자동 병합을 사용하여 끌어오기 요청 병합"끌어오기 요청 자동 병합"
merge base into head
기본 분기에 엄격한 상태 검사가 필요한 경우(예: 끌어오기 요청의 업데이트 분기를 통해) 기본 분기에서 토픽 분기 업데이트"보호된 분기 정보"
pull request branch delete button
웹 인터페이스의 끌어오기 요청에서 토픽 분기 삭제"끌어오기 요청에서 분기 삭제 및 복원"
pull request branch undo button
웹 인터페이스의 끌어오기 요청에서 토픽 분기 복원"끌어오기 요청에서 분기 삭제 및 복원"
pull request merge api
API를 통해 끌어오기 요청 병합"끌어오기 요청에 대한 REST API 엔드포인트"
pull request merge button
웹 인터페이스 끌어오기 요청 병합"끌어오기 요청 병합"
pull request revert button
끌어오기 요청 되돌리기"끌어오기 요청 되돌리기"
releases delete button
릴리스 삭제"리포지토리에서 릴리스 관리"
stafftools branch restore
사이트 관리자 대시보드에서 분기 복원"관리 웹 UI에서 인스턴스 등록"
tag create api
API를 통해 태그 만들기"Git 태그에 대한 REST API 엔드포인트"
web branch create
웹 인터페이스를 통해 분기 만들기"리포지토리 내에서 분기 만들기 및 삭제"

끌어오기 요청 병합에 사용 가능

다음 변수는 후크를 트리거하는 푸시가 끌어오기 요청의 병합으로 인한 푸시인 경우 사전 수신 후크 환경에서 사용할 수 있습니다.

변수설명예제 값
$GITHUB_PULL_REQUEST_AUTHOR_LOGIN
끌어오기 요청을 작성한 계정의 사용자 이름octocat
$GITHUB_PULL_REQUEST_HEAD
끌어오기 요청 토픽 분기의 이름(USERNAME:BRANCH 형식)octocat:fix-bug
$GITHUB_PULL_REQUEST_BASE
끌어오기 요청 기본 분기의 이름(USERNAME:BRANCH 형식)octocat:main

SSH 인증을 사용하여 푸시에 사용 가능

변수설명예제 값
$GITHUB_PUBLIC_KEY_FINGERPRINT
변경 내용을 푸시한 사용자의 퍼블릭 키 지문a1:b2:c3:d4:e5:f6:g7:h8:i9:j0:k1:l2:m3:n4:o5:p6

권한 설정 및 GitHub Enterprise Server에 사전 수신 후크 푸시

GitHub Enterprise Server 인스턴스의 리포지토리에 사전 수신 후크 스크립트가 포함되어 있습니다. 사이트 관리자는 리포지토리 권한을 고려하고 적절한 사용자만 액세스할 수 있도록 해야 합니다.

후크를 단일 리포지토리에 통합하는 것이 좋습니다. 통합 후크 리포지토리가 퍼블릭인 경우 정책 적용을 설명하는 데 README.md를 사용할 수 있습니다. 또한 끌어오기 요청을 통해 기여를 수락할 수 있습니다. 그러나 사전 수신 후크는 기본 분기에서만 추가할 수 있습니다. 테스트 워크플로의 경우 구성이 있는 리포지토리의 포크를 사용해야 합니다.

  1. Mac 사용자의 경우 스크립트에 다음 실행 권한이 있는지 확인합니다.

    sudo chmod +x SCRIPT_FILE.sh
    

    Windows 사용자의 경우 스크립트에 다음 실행 권한이 있는지 확인합니다.

    git update-index --chmod=+x SCRIPT_FILE.sh
    
  2. GitHub Enterprise Server 인스턴스에서 사전 수신 후크를 위해 지정된 리포지토리에 커밋하고 푸시합니다.

    git commit -m "YOUR COMMIT MESSAGE"
    git push
    
  3. GitHub Enterprise Server 인스턴스에 사전 수신 후크를 만듭니다.

로컬에서 사전 수신 스크립트 테스트

GitHub Enterprise Server 인스턴스에서 만들거나 업데이트하기 전에 사전 수신 후크 스크립트를 로컬에서 테스트할 수 있습니다. 한 가지 방법은 사전 수신 후크를 실행할 수 있는 원격 리포지토리 역할을 하는 로컬 Docker 환경을 만드는 것입니다.

  1. 로컬에 Docker가 설치되어 있는지 확인합니다.

  2. 다음을 포함하는 Dockerfile.dev라는 파일을 만듭니다.

    FROM alpine:latest
    RUN \
      apk add --no-cache git openssh bash && \
      ssh-keygen -A && \
      sed -i "s/#AuthorizedKeysFile/AuthorizedKeysFile/g" /etc/ssh/sshd_config && \
      adduser git -D -G root -h /home/git -s /bin/bash && \
      passwd -d git && \
      su git -c "mkdir /home/git/.ssh && \
      ssh-keygen -t ed25519 -f /home/git/.ssh/id_ed25519 -P '' && \
      mv /home/git/.ssh/id_ed25519.pub /home/git/.ssh/authorized_keys && \
      mkdir /home/git/test.git && \
      git --bare init /home/git/test.git"
    
    VOLUME ["/home/git/.ssh", "/home/git/test.git/hooks"]
    WORKDIR /home/git
    
    CMD ["/usr/sbin/sshd", "-D"]
    
  3. always_reject.sh라는 테스트 사전 수신 스크립트를 만듭니다. 이 예제 스크립트는 리포지토리를 잠그는 데 유용한 모든 푸시를 거부합니다.

    #!/usr/bin/env bash
    
    echo "error: rejecting all pushes"
    exit 1
    
  4. always_reject.sh 스크립트에 실행 권한이 있는지 확인합니다.

    chmod +x always_reject.sh
    
  5. Dockerfile.dev를 포함하는 디렉터리에서 이미지를 빌드합니다.

    $ docker build -f Dockerfile.dev -t pre-receive.dev .
    [+] Building 4.5s (8/8) FINISHED
     => [internal] load build definition from Dockerfile.dev                                                                            0.0s
     => => transferring dockerfile: 641B                                                                                                0.0s
     => [internal] load .dockerignore                                                                                                   0.0s
     => transferring context: 2B                                                                                                     0.0s
     => [internal] load metadata for docker.io/library/alpine:latest                                                                    1.9s
     => [auth] library/alpine:pull token for registry-1.docker.io                                                                       0.0s
     => [1/3] FROM docker.io/library/alpine:latest@sha256:82d1e9d7ed48a7523bdebc18cf6290bdb97b82302a8a9c27d4fe885949ea94d1              0.0s
     => => resolve docker.io/library/alpine:latest@sha256:82d1e9d7ed48a7523bdebc18cf6290bdb97b82302a8a9c27d4fe885949ea94d1              0.0s
     => => sha256:82d1e9d7ed48a7523bdebc18cf6290bdb97b82302a8a9c27d4fe885949ea94d1 1.64kB / 1.64kB                                      0.0s
     => => sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70 528B / 528B                                          0.0s
     => => sha256:c1aabb73d2339c5ebaa3681de2e9d9c18d57485045a4e311d9f8004bec208d67 1.47kB / 1.47kB                                      0.0s
     => [2/3] RUN   apk add --no-cache git openssh bash &&   ssh-keygen -A &&   sed -i "s/#AuthorizedKeysFile/AuthorizedKeysFile/g" /e  2.3s
     => [3/3] WORKDIR /home/git                                                                                                         0.0s
     => exporting to image                                                                                                              0.1s
     => => exporting layers                                                                                                             0.1s
     => => writing image sha256:938447846e19a4328a85883fbd1ccf5eb919d97448cc7256efebf403d8b5a196                                        0.0s
     => => naming to docker.io/library/pre-receive.dev
    
  6. 생성된 SSH 키가 포함된 데이터 컨테이너를 실행합니다.

    docker run --name data pre-receive.dev /bin/true
    
  7. 테스트 사전 수신 후크 always_reject.sh를 데이터 컨테이너에 복사합니다.

    docker cp always_reject.sh data:/home/git/test.git/hooks/pre-receive
    
  8. sshd 및 후크를 실행하는 애플리케이션 컨테이너를 실행합니다. 반환되는 컨테이너 ID를 기록해 둡니다.

    $ docker run -d -p 52311:22 --volumes-from data pre-receive.dev
    > 7f888bc700b8d23405dbcaf039e6c71d486793cad7d8ae4dd184f4a47000bc58
    
  9. 생성된 SSH 키를 데이터 컨테이너에서 로컬 컴퓨터로 복사합니다.

    docker cp data:/home/git/.ssh/id_ed25519 .
    
  10. 테스트 리포지토리의 원격을 수정하고 Docker 컨테이너 내의 test.git 리포지토리로 푸시합니다. 이 예제에서는 git@github.com:octocat/Hello-World.git을 사용하지만 원하는 리포지토리를 사용할 수 있습니다. 이 예제에서는 로컬 컴퓨터(127.0.0.1)가 포트 52311을 바인딩하는 것으로 가정하지만, docker가 원격 머신에서 실행되는 경우 다른 IP 주소를 사용할 수 있습니다.

    $ git clone git@github.com:octocat/Hello-World.git
    $ cd Hello-World
    $ git remote add test git@127.0.0.1:test.git
    $ GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 52311 -i ../id_ed25519" git push -u test master
    > Warning: Permanently added '[127.0.0.1]:52311' (ECDSA) to the list of known hosts.
    > Counting objects: 7, done.
    > Delta compression using up to 4 threads.
    > Compressing objects: 100% (3/3), done.
    > Writing objects: 100% (7/7), 700 bytes | 0 bytes/s, done.
    > Total 7 (delta 0), reused 7 (delta 0)
    > remote: error: rejecting all pushes
    > To git@127.0.0.1:test.git
    >  ! [remote rejected] master -> master (pre-receive hook declined)
    > error: failed to push some refs to 'git@192.168.99.100:test.git'
    

    사전 수신 후크를 실행하고 스크립트의 출력을 에코한 후 푸시가 거부되었습니다.

추가 참고 자료