이 문서는 GitLab 저장소가 Bitbucket 저장소를 미러링하도록 Mirror Hook을 설정하는 방법의 가이드를 공유하기 위해 작성되었다.
| 도구명 | Bitbucket, GitLab |
|---|---|
| 버전 | Bitbucket : 8.19.8 GitLab : 15.2 |
| 비고 | - |
Bitbucket 저장소와 GitLab 저장소 연동 설정하기
1. GitLab에 저장소 생성하기
TroubleShooting
다음 에러는 GitLab 저장소에 ReadMe 파일이 있을 때 발생한 에러입니다.
2024-10-16 08:33:24,303 ERROR [MirrorRepositoryHook:thread-2] c.a.s.i.c.HazelcastBucketedExecutor Attempt 5 of 5 at processing bucket 'MirrorRepositoryHook@32:https://gitlab-playground.curvc.com/root/demo-project.git' for executor 'MirrorRepositoryHook' failed: com.atlassian.bitbucket.ServerException: An error occurred while executing an external process: Wrapping ProcessFailedException: 'git push --prune https://root:*****@gitlab-playground.curvc.com/root/demo-project.git --force --atomic +refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/* +refs/notes/*:refs/notes/*' exited with code 1 at com.atlassian.bitbucket.scm.git.command.GitCommandExitHandler.evaluateThrowable(GitCommandExitHandler.java:163) at com.atlassian.bitbucket.scm.git.command.GitCommandExitHandler.onError(GitCommandExitHandler.java:297) at com.atlassian.bitbucket.scm.DefaultCommandExitHandler.onExit(DefaultCommandExitHandler.java:32) at com.englishtown.bitbucket.hook.PasswordHandler.onExit(PasswordHandler.java:50) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.callExitHandler(AbstractFragmentResponseObserver.java:180) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.asResult(AbstractFragmentResponseObserver.java:150) at com.atlassian.stash.internal.scm.git.mesh.BidirectionalFragmentResponseObserver.asResult(BidirectionalFragmentResponseObserver.java:66) at com.atlassian.stash.internal.scm.git.mesh.GrpcPlumbingClient.call(GrpcPlumbingClient.java:71) at jdk.internal.reflect.GeneratedMethodAccessor794.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at jdk.internal.reflect.GeneratedMethodAccessor187.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at jdk.proxy3/jdk.proxy3.$Proxy477.call(Unknown Source) at com.atlassian.stash.internal.scm.git.mesh.MeshGitFreeFormCommandBuilder$1.call(MeshGitFreeFormCommandBuilder.java:105) at com.englishtown.bitbucket.hook.MirrorBucketProcessor.runMirrorCommand(MirrorBucketProcessor.java:130) at com.englishtown.bitbucket.hook.MirrorBucketProcessor.lambda$process$0(MirrorBucketProcessor.java:80) at com.atlassian.stash.internal.user.DefaultEscalatedSecurityContext.call(DefaultEscalatedSecurityContext.java:59) at com.englishtown.bitbucket.hook.MirrorBucketProcessor.process(MirrorBucketProcessor.java:70) at com.atlassian.stash.internal.concurrent.HazelcastBucketedExecutor$BucketProcessingBootstrapper.run(HazelcastBucketedExecutor.java:146) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.lang.Thread.run(Thread.java:840) ... 15 frames trimmed Caused by: com.englishtown.bitbucket.hook.PasswordHandler$PasswordSafeException: Wrapping ProcessFailedException: 'git push --prune https://root:*****@gitlab-playground.curvc.com/root/demo-project.git --force --atomic +refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/* +refs/notes/*:refs/notes/*' exited with code 1 at com.atlassian.stash.internal.scm.git.command.BioStdioHandler.onComplete(BioStdioHandler.java:92) at com.atlassian.stash.internal.scm.git.mesh.LatchedNioStdioHandler.onComplete(LatchedNioStdioHandler.java:61) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.maybeSummarize(AbstractFragmentResponseObserver.java:278) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.finish(AbstractFragmentResponseObserver.java:228) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.callOnExit(AbstractFragmentResponseObserver.java:200) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.onNext(AbstractFragmentResponseObserver.java:120) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.onNext(AbstractFragmentResponseObserver.java:41) at io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onMessage(ClientCalls.java:468) at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) at com.atlassian.stash.internal.scm.git.mesh.StatefulClientCallListener.onMessage(StatefulClientCallListener.java:45) at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) at com.atlassian.stash.internal.scm.git.mesh.DeadlinePropagatingClientInterceptor$DeadlinePropagatingListener.onMessage(DeadlinePropagatingClientInterceptor.java:169) at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInternal(ClientCallImpl.java:667) at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInContext(ClientCallImpl.java:654) at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37) at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133) ... 3 common frames omitted
GitLab에 아무 파일도 없는 상태 (ReadMe 파일도
) 인 경우에는 에러 발생하지 않습니다.
따라서, Initialize Repository with a README 선택 해제해야 합니다.
2. Bitbucket 저장소의 Mirror Hook 설정
- 저장소의 Repository settings → Hooks
Pipeline 에러
Bitbucket에서 Mirror Hook 활성화 후 거의 바로 저장소 clone 되지만, Pipeline 에서 에러 발생
GitLab 웹 페이지에 나타나는 에러
$ /build/build.sh bash: line 179: /build/build.sh: No such file or directory Uploading artifacts for failed job Uploading artifacts... Runtime platform arch=amd64 os=linux pid=19961 revision=66269445 version=17.3.1 WARNING: gl-auto-build-variables.env: no matching files. Ensure that the artifact path is relative to the working directory (/home/gitlab-runner/builds/2a7FHkMoh/0/test/mirroring-test) // build.sh 파일 X ERROR: No files to upload Cleaning up project directory and file based variables ERROR: Job failed: exit status 1
GitLab 로그
{
"severity": "WARN",
"time": "2024-10-16T01:28:01.706Z",
"class": "Git::BranchHooksService",
"correlation_id": "01JA9F0S8ZXF5785N7KSG01K2N",
"project_id": 6,
"project_path": "test/mirroring-test",
"message": "Error creating pipeline",
"errors": "Missing CI config file", // CI Config file 이 없어서 발생
"pipeline_params": {
"before": "23289086630c099e53bc5a8c614693fac7893568",
"after": "7a4af81869ff3a1bf222ad120e0fb69e93f70ce8",
"ref": "refs/heads/master",
"variables_attributes": [],
"checkout_sha": "7a4af81869ff3a1bf222ad120e0fb69e93f70ce8"
},
"retry": 0
}
CI Config file이 없어서 발생했기 때문에 추가합니다.
미러링 테스트
Bitbucket 저장소에서 Commit
GitLab 저장소에서 Commit 내역 업데이트
Bitbucket 저장소에서 Pull Request & Merge
GitLab 저장소에 업데이트
Bitbucket의 Git LFS 포함 저장소를 GitLab으로 미러링
에러 발생, LFS 파일이 포함된 Commit이 업데이트 되지 않습니다.
GitLab 로그
{
"correlation_id": "01JABWQS7EC26MEHMCZJ8J6EMG",
"duration_ms": 114,
"error": "LFS objects are missing. Ensure LFS is properly set up or try a manual \"git lfs push --all\".",
"level": "error", // 로그 레벨: 오류 발생
"method": "POST",
"msg": "Internal API error",
"pid": 286,
"status": 401, // 상태 코드: 권한 없음 (401 Unauthorized)
"time": "2024-10-17T00:06:15.354Z",
"url": "http://unix/api/v4/internal/allowed"
}
{
"component": "gitaly.StreamServerInterceptor",
"correlation_id": "01JABWQS7EC26MEHMCZJ8J6EMG",
"error": "GitLab: LFS objects are missing. Ensure LFS is properly set up or try a manual \"git lfs push --all\".", // 오류 메시지: Git LFS 오브젝트 누락
"grpc.meta.client_name": "gitaly-hooks",
"grpc.meta.deadline_type": "none",
"grpc.meta.method_operation": "accessor",
"grpc.meta.method_scope": "repository",
"grpc.meta.method_type": "bidi_stream",
"grpc.method": "PreReceiveHook", // gRPC 메서드: PreReceiveHook
"grpc.request.fullMethod": "/gitaly.HookService/PreReceiveHook",
"grpc.request.glProjectPath": "test/bitbucket-java-ex",
"grpc.request.glRepository": "project-12",
"grpc.request.repoPath": "@hashed/6b/51/6b51d431df5d7f141cbececcf79edf3dd861c3b4069f0b11661a3eefacbba918.git",
"grpc.request.repoStorage": "default",
"grpc.service": "gitaly.HookService",
"grpc.start_time": "2024-10-17T00:06:15.239",
"level": "warning",
"msg": "stopping transaction because pre-receive hook failed", // 메시지: pre-receive hook 실패로 트랜잭션 중단
"pid": 286,
"span.kind": "server",
"system": "grpc",
"time": "2024-10-17T00:06:15.354Z",
"user_id": "user-1",
"username": "root"
}
Bitbucket 로그
2024-10-17 09:07:56,053 ERROR [MirrorRepositoryHook:thread-3] c.a.s.i.c.HazelcastBucketedExecutor Attempt 5 of 5 at processing bucket 'MirrorRepositoryHook@73:https://gitlab-playground.curvc.com/test/bitbucket-java-ex.git' for executor 'MirrorRepositoryHook' failed: com.atlassian.bitbucket.ServerException: An error occurred while executing an external process: Wrapping ProcessFailedException: 'git push --prune https://root:*****@gitlab-playground.curvc.com/test/bitbucket-java-ex.git --force --atomic +refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/* +refs/notes/*:refs/notes/*' exited with code 1 at com.atlassian.bitbucket.scm.git.command.GitCommandExitHandler.evaluateThrowable(GitCommandExitHandler.java:163) at com.atlassian.bitbucket.scm.git.command.GitCommandExitHandler.onError(GitCommandExitHandler.java:297) at com.atlassian.bitbucket.scm.DefaultCommandExitHandler.onExit(DefaultCommandExitHandler.java:32) at com.englishtown.bitbucket.hook.PasswordHandler.onExit(PasswordHandler.java:50) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.callExitHandler(AbstractFragmentResponseObserver.java:180) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.asResult(AbstractFragmentResponseObserver.java:150) at com.atlassian.stash.internal.scm.git.mesh.BidirectionalFragmentResponseObserver.asResult(BidirectionalFragmentResponseObserver.java:66) at com.atlassian.stash.internal.scm.git.mesh.GrpcPlumbingClient.call(GrpcPlumbingClient.java:71) at jdk.internal.reflect.GeneratedMethodAccessor794.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at jdk.internal.reflect.GeneratedMethodAccessor187.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at jdk.proxy3/jdk.proxy3.$Proxy477.call(Unknown Source) at com.atlassian.stash.internal.scm.git.mesh.MeshGitFreeFormCommandBuilder$1.call(MeshGitFreeFormCommandBuilder.java:105) at com.englishtown.bitbucket.hook.MirrorBucketProcessor.runMirrorCommand(MirrorBucketProcessor.java:130) at com.englishtown.bitbucket.hook.MirrorBucketProcessor.lambda$process$0(MirrorBucketProcessor.java:80) at com.atlassian.stash.internal.user.DefaultEscalatedSecurityContext.call(DefaultEscalatedSecurityContext.java:59) at com.englishtown.bitbucket.hook.MirrorBucketProcessor.process(MirrorBucketProcessor.java:70) at com.atlassian.stash.internal.concurrent.HazelcastBucketedExecutor$BucketProcessingBootstrapper.run(HazelcastBucketedExecutor.java:146) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.lang.Thread.run(Thread.java:840) ... 15 frames trimmed Caused by: com.englishtown.bitbucket.hook.PasswordHandler$PasswordSafeException: Wrapping ProcessFailedException: 'git push --prune https://root:*****@gitlab-playground.curvc.com/test/bitbucket-java-ex.git --force --atomic +refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/* +refs/notes/*:refs/notes/*' exited with code 1 at com.atlassian.stash.internal.scm.git.command.BioStdioHandler.onComplete(BioStdioHandler.java:92) at com.atlassian.stash.internal.scm.git.mesh.LatchedNioStdioHandler.onComplete(LatchedNioStdioHandler.java:61) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.maybeSummarize(AbstractFragmentResponseObserver.java:278) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.finish(AbstractFragmentResponseObserver.java:228) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.callOnExit(AbstractFragmentResponseObserver.java:200) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.onNext(AbstractFragmentResponseObserver.java:120) at com.atlassian.stash.internal.scm.git.mesh.AbstractFragmentResponseObserver.onNext(AbstractFragmentResponseObserver.java:41) at io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onMessage(ClientCalls.java:468) at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) at com.atlassian.stash.internal.scm.git.mesh.StatefulClientCallListener.onMessage(StatefulClientCallListener.java:45) at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) at com.atlassian.stash.internal.scm.git.mesh.DeadlinePropagatingClientInterceptor$DeadlinePropagatingListener.onMessage(DeadlinePropagatingClientInterceptor.java:169) at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInternal(ClientCallImpl.java:667) at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInContext(ClientCallImpl.java:654) at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37) at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133) ... 3 common frames omitted








