mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-11 16:05:44 +01:00
Fix messages for post commit hooks in git (#1647)
Fixes the transmission of messages from post commit hooks in Git repositories. We therefore use a new method patched in jGit for SCM-Manager. This simplifies the trigger logic a lot.
This commit is contained in:
2
gradle/changelog/post_commit_hook_messages_git.yaml
Normal file
2
gradle/changelog/post_commit_hook_messages_git.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
- type: fixed
|
||||||
|
description: Messages from post commit hooks for git ([#1647](https://github.com/scm-manager/scm-manager/pull/1647))
|
||||||
@@ -27,7 +27,7 @@ plugins {
|
|||||||
id 'org.scm-manager.smp' version '0.7.5'
|
id 'org.scm-manager.smp' version '0.7.5'
|
||||||
}
|
}
|
||||||
|
|
||||||
def jgitVersion = '5.10.0.202012080955-r-scm1'
|
def jgitVersion = '5.10.0.202012080955-r-scm2'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// required by scm-it
|
// required by scm-it
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ import org.eclipse.jgit.lib.Repository;
|
|||||||
import org.eclipse.jgit.lib.RepositoryCache;
|
import org.eclipse.jgit.lib.RepositoryCache;
|
||||||
import sonia.scm.repository.GitChangesetConverterFactory;
|
import sonia.scm.repository.GitChangesetConverterFactory;
|
||||||
import sonia.scm.repository.GitRepositoryHandler;
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
|
import sonia.scm.repository.spi.HookEventFacade;
|
||||||
import sonia.scm.web.CollectingPackParserListener;
|
import sonia.scm.web.CollectingPackParserListener;
|
||||||
import sonia.scm.web.GitHookEventFacade;
|
|
||||||
import sonia.scm.web.GitReceiveHook;
|
import sonia.scm.web.GitReceiveHook;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -54,7 +54,7 @@ public class ScmTransportProtocol extends TransportProtocol {
|
|||||||
private static final Set<String> SCHEMES = ImmutableSet.of(NAME);
|
private static final Set<String> SCHEMES = ImmutableSet.of(NAME);
|
||||||
|
|
||||||
private Provider<GitChangesetConverterFactory> converterFactory;
|
private Provider<GitChangesetConverterFactory> converterFactory;
|
||||||
private Provider<GitHookEventFacade> hookEventFacadeProvider;
|
private Provider<HookEventFacade> hookEventFacadeProvider;
|
||||||
private Provider<GitRepositoryHandler> repositoryHandlerProvider;
|
private Provider<GitRepositoryHandler> repositoryHandlerProvider;
|
||||||
|
|
||||||
public ScmTransportProtocol() {
|
public ScmTransportProtocol() {
|
||||||
@@ -63,7 +63,7 @@ public class ScmTransportProtocol extends TransportProtocol {
|
|||||||
@Inject
|
@Inject
|
||||||
public ScmTransportProtocol(
|
public ScmTransportProtocol(
|
||||||
Provider<GitChangesetConverterFactory> converterFactory,
|
Provider<GitChangesetConverterFactory> converterFactory,
|
||||||
Provider<GitHookEventFacade> hookEventFacadeProvider,
|
Provider<HookEventFacade> hookEventFacadeProvider,
|
||||||
Provider<GitRepositoryHandler> repositoryHandlerProvider) {
|
Provider<GitRepositoryHandler> repositoryHandlerProvider) {
|
||||||
this.converterFactory = converterFactory;
|
this.converterFactory = converterFactory;
|
||||||
this.hookEventFacadeProvider = hookEventFacadeProvider;
|
this.hookEventFacadeProvider = hookEventFacadeProvider;
|
||||||
@@ -110,11 +110,11 @@ public class ScmTransportProtocol extends TransportProtocol {
|
|||||||
|
|
||||||
private final GitChangesetConverterFactory converterFactory;
|
private final GitChangesetConverterFactory converterFactory;
|
||||||
private final GitRepositoryHandler handler;
|
private final GitRepositoryHandler handler;
|
||||||
private final GitHookEventFacade hookEventFacade;
|
private final HookEventFacade hookEventFacade;
|
||||||
|
|
||||||
public TransportLocalWithHooks(
|
public TransportLocalWithHooks(
|
||||||
GitChangesetConverterFactory converterFactory,
|
GitChangesetConverterFactory converterFactory,
|
||||||
GitHookEventFacade hookEventFacade,
|
HookEventFacade hookEventFacade,
|
||||||
GitRepositoryHandler handler,
|
GitRepositoryHandler handler,
|
||||||
Repository local, URIish uri, File gitDir) {
|
Repository local, URIish uri, File gitDir) {
|
||||||
super(local, uri, gitDir);
|
super(local, uri, gitDir);
|
||||||
@@ -133,7 +133,7 @@ public class ScmTransportProtocol extends TransportProtocol {
|
|||||||
pack.setPreReceiveHook(hook);
|
pack.setPreReceiveHook(hook);
|
||||||
pack.setPostReceiveHook(hook);
|
pack.setPostReceiveHook(hook);
|
||||||
|
|
||||||
CollectingPackParserListener.set(pack);
|
CollectingPackParserListener.set(pack, hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pack;
|
return pack;
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
|||||||
import sonia.scm.repository.GitChangesetConverterFactory;
|
import sonia.scm.repository.GitChangesetConverterFactory;
|
||||||
import sonia.scm.repository.GitRepositoryConfig;
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
import sonia.scm.repository.GitRepositoryHandler;
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
|
import sonia.scm.repository.spi.HookEventFacade;
|
||||||
import sonia.scm.web.CollectingPackParserListener;
|
import sonia.scm.web.CollectingPackParserListener;
|
||||||
import sonia.scm.web.GitHookEventFacade;
|
|
||||||
import sonia.scm.web.GitReceiveHook;
|
import sonia.scm.web.GitReceiveHook;
|
||||||
|
|
||||||
public abstract class BaseReceivePackFactory<T> implements ReceivePackFactory<T> {
|
public abstract class BaseReceivePackFactory<T> implements ReceivePackFactory<T> {
|
||||||
@@ -45,7 +45,7 @@ public abstract class BaseReceivePackFactory<T> implements ReceivePackFactory<T>
|
|||||||
|
|
||||||
protected BaseReceivePackFactory(GitChangesetConverterFactory converterFactory,
|
protected BaseReceivePackFactory(GitChangesetConverterFactory converterFactory,
|
||||||
GitRepositoryHandler handler,
|
GitRepositoryHandler handler,
|
||||||
GitHookEventFacade hookEventFacade,
|
HookEventFacade hookEventFacade,
|
||||||
GitRepositoryConfigStoreProvider storeProvider) {
|
GitRepositoryConfigStoreProvider storeProvider) {
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.storeProvider = storeProvider;
|
this.storeProvider = storeProvider;
|
||||||
@@ -60,7 +60,7 @@ public abstract class BaseReceivePackFactory<T> implements ReceivePackFactory<T>
|
|||||||
receivePack.setPreReceiveHook(hook);
|
receivePack.setPreReceiveHook(hook);
|
||||||
receivePack.setPostReceiveHook(hook);
|
receivePack.setPostReceiveHook(hook);
|
||||||
// apply collecting listener, to be able to check which commits are new
|
// apply collecting listener, to be able to check which commits are new
|
||||||
CollectingPackParserListener.set(receivePack);
|
CollectingPackParserListener.set(receivePack, hook);
|
||||||
|
|
||||||
return receivePack;
|
return receivePack;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import sonia.scm.protocolcommand.CommandContext;
|
|||||||
import sonia.scm.protocolcommand.RepositoryContext;
|
import sonia.scm.protocolcommand.RepositoryContext;
|
||||||
import sonia.scm.protocolcommand.ScmCommandProtocol;
|
import sonia.scm.protocolcommand.ScmCommandProtocol;
|
||||||
import sonia.scm.repository.RepositoryPermissions;
|
import sonia.scm.repository.RepositoryPermissions;
|
||||||
import sonia.scm.web.GitHookEventFacade;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -51,13 +50,11 @@ public class GitCommandProtocol implements ScmCommandProtocol {
|
|||||||
|
|
||||||
private final ScmUploadPackFactory uploadPackFactory;
|
private final ScmUploadPackFactory uploadPackFactory;
|
||||||
private final ScmReceivePackFactory receivePackFactory;
|
private final ScmReceivePackFactory receivePackFactory;
|
||||||
private final GitHookEventFacade gitHookEventFacade;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public GitCommandProtocol(ScmUploadPackFactory uploadPackFactory, ScmReceivePackFactory receivePackFactory, GitHookEventFacade gitHookEventFacade) {
|
public GitCommandProtocol(ScmUploadPackFactory uploadPackFactory, ScmReceivePackFactory receivePackFactory) {
|
||||||
this.uploadPackFactory = uploadPackFactory;
|
this.uploadPackFactory = uploadPackFactory;
|
||||||
this.receivePackFactory = receivePackFactory;
|
this.receivePackFactory = receivePackFactory;
|
||||||
this.gitHookEventFacade = gitHookEventFacade;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -81,8 +78,6 @@ public class GitCommandProtocol implements ScmCommandProtocol {
|
|||||||
receivePack.receive(commandContext.getInputStream(), commandContext.getOutputStream(), commandContext.getErrorStream());
|
receivePack.receive(commandContext.getInputStream(), commandContext.getOutputStream(), commandContext.getErrorStream());
|
||||||
} catch (ServiceNotEnabledException | ServiceNotAuthorizedException e) {
|
} catch (ServiceNotEnabledException | ServiceNotAuthorizedException e) {
|
||||||
throw new IOException("error creating receive pack for ssh", e);
|
throw new IOException("error creating receive pack for ssh", e);
|
||||||
} finally {
|
|
||||||
gitHookEventFacade.firePending();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,14 +31,14 @@ import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
|||||||
import sonia.scm.protocolcommand.RepositoryContext;
|
import sonia.scm.protocolcommand.RepositoryContext;
|
||||||
import sonia.scm.repository.GitChangesetConverterFactory;
|
import sonia.scm.repository.GitChangesetConverterFactory;
|
||||||
import sonia.scm.repository.GitRepositoryHandler;
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
import sonia.scm.web.GitHookEventFacade;
|
import sonia.scm.repository.spi.HookEventFacade;
|
||||||
|
|
||||||
public class ScmReceivePackFactory extends BaseReceivePackFactory<RepositoryContext> {
|
public class ScmReceivePackFactory extends BaseReceivePackFactory<RepositoryContext> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ScmReceivePackFactory(GitChangesetConverterFactory converterFactory,
|
public ScmReceivePackFactory(GitChangesetConverterFactory converterFactory,
|
||||||
GitRepositoryHandler handler,
|
GitRepositoryHandler handler,
|
||||||
GitHookEventFacade hookEventFacade,
|
HookEventFacade hookEventFacade,
|
||||||
GitRepositoryConfigStoreProvider gitRepositoryConfigStoreProvider) {
|
GitRepositoryConfigStoreProvider gitRepositoryConfigStoreProvider) {
|
||||||
super(converterFactory, handler, hookEventFacade, gitRepositoryConfigStoreProvider);
|
super(converterFactory, handler, hookEventFacade, gitRepositoryConfigStoreProvider);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ public class CollectingPackParserListener implements PackParserListener
|
|||||||
*/
|
*/
|
||||||
private static final Logger logger =
|
private static final Logger logger =
|
||||||
LoggerFactory.getLogger(CollectingPackParserListener.class);
|
LoggerFactory.getLogger(CollectingPackParserListener.class);
|
||||||
|
private final GitReceiveHook hook;
|
||||||
|
|
||||||
|
public CollectingPackParserListener(GitReceiveHook hook) {
|
||||||
|
this.hook = hook;
|
||||||
|
}
|
||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
//~--- get methods ----------------------------------------------------------
|
||||||
|
|
||||||
@@ -94,10 +99,10 @@ public class CollectingPackParserListener implements PackParserListener
|
|||||||
*
|
*
|
||||||
* @param pack receive pack
|
* @param pack receive pack
|
||||||
*/
|
*/
|
||||||
public static void set(ReceivePack pack)
|
public static void set(ReceivePack pack, GitReceiveHook hook)
|
||||||
{
|
{
|
||||||
logger.trace("apply collecting listener to receive pack");
|
logger.trace("apply collecting listener to receive pack");
|
||||||
pack.setPackParserListener(new CollectingPackParserListener());
|
pack.setPackParserListener(new CollectingPackParserListener(hook));
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
@@ -148,6 +153,11 @@ public class CollectingPackParserListener implements PackParserListener
|
|||||||
parser.setNeedNewObjectIds(true);
|
parser.setNeedNewObjectIds(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release() {
|
||||||
|
hook.afterReceive();
|
||||||
|
}
|
||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
//~--- get methods ----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package sonia.scm.web;
|
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
||||||
import io.micrometer.core.instrument.MeterRegistry;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import sonia.scm.metrics.Metrics;
|
|
||||||
import sonia.scm.repository.RepositoryHookType;
|
|
||||||
import sonia.scm.repository.spi.GitHookContextProvider;
|
|
||||||
import sonia.scm.repository.spi.HookEventFacade;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is used to delay the firing of post commit hooks, so that they are not
|
|
||||||
* executed while the internal git processing has not finished. Without this, this can
|
|
||||||
* lead to conflicting access to pack files which results in 'Short compressed stream'
|
|
||||||
* errors (see https://github.com/scm-manager/scm-manager/pull/1518).
|
|
||||||
* <br>
|
|
||||||
* The delay is handled either by "caching" the hook data in a thread local, where it
|
|
||||||
* is fetched from when {@link #firePending()} is called, or in case the trigger is fired
|
|
||||||
* due to changes made with internal git file push (from a workdir to the central
|
|
||||||
* repository) by detecting the internal thread used by JGit and joining another thread
|
|
||||||
* where the pending push is triggered.
|
|
||||||
*/
|
|
||||||
public class GitHookEventFacade implements Closeable {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(GitHookEventFacade.class);
|
|
||||||
|
|
||||||
private static final ThreadLocal<GitHookContextProvider> PENDING_POST_HOOK = new ThreadLocal<>();
|
|
||||||
|
|
||||||
private final HookEventFacade hookEventFacade;
|
|
||||||
private final ExecutorService internalThreadHookHandler;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public GitHookEventFacade(HookEventFacade hookEventFacade, MeterRegistry registry) {
|
|
||||||
this.hookEventFacade = hookEventFacade;
|
|
||||||
this.internalThreadHookHandler = createInternalThreadHookHandlerPool(registry);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void fire(RepositoryHookType type, GitHookContextProvider context) {
|
|
||||||
switch (type) {
|
|
||||||
case PRE_RECEIVE:
|
|
||||||
doFire(type, context);
|
|
||||||
break;
|
|
||||||
case POST_RECEIVE:
|
|
||||||
Thread thread = Thread.currentThread();
|
|
||||||
if ("JGit-Receive-Pack".equals(thread.getName())) {
|
|
||||||
// this thread name is used in the JGit class org.eclipse.jgit.transport.InternalPushConnection
|
|
||||||
LOG.debug("handling internal git thread for post receive hook");
|
|
||||||
handleGitInternalThread(context, thread);
|
|
||||||
} else {
|
|
||||||
LOG.debug("register post receive hook for repository id {} in thread {}", context.getRepositoryId(), thread);
|
|
||||||
PENDING_POST_HOOK.set(context);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown hook type: " + type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void firePending() {
|
|
||||||
try {
|
|
||||||
LOG.debug("fire pending post receive hooks in thread {}", Thread.currentThread());
|
|
||||||
doFire(RepositoryHookType.POST_RECEIVE, PENDING_POST_HOOK.get());
|
|
||||||
} finally {
|
|
||||||
clean();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleGitInternalThread(GitHookContextProvider context, Thread internalJGitThread) {
|
|
||||||
internalThreadHookHandler.submit(() -> {
|
|
||||||
try {
|
|
||||||
internalJGitThread.join();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.debug("got interrupted in internal git thread for repository id {}", context.getRepositoryId(), e);
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
} finally {
|
|
||||||
LOG.debug("internal git thread ended for repository id {}", context.getRepositoryId());
|
|
||||||
doFire(RepositoryHookType.POST_RECEIVE, context);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clean() {
|
|
||||||
PENDING_POST_HOOK.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doFire(RepositoryHookType type, GitHookContextProvider context) {
|
|
||||||
if (context != null) {
|
|
||||||
LOG.debug("firing {} hook for repository {} in Thread {}", type, context.getRepositoryId(), Thread.currentThread());
|
|
||||||
hookEventFacade.handle(context.getRepositoryId()).fireHookEvent(type, context);
|
|
||||||
} else {
|
|
||||||
LOG.debug("No context found for event type {} in Thread {}", type, Thread.currentThread());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private ExecutorService createInternalThreadHookHandlerPool(MeterRegistry registry) {
|
|
||||||
ExecutorService executorService = Executors.newCachedThreadPool(
|
|
||||||
new ThreadFactoryBuilder()
|
|
||||||
.setNameFormat("GitInternalThreadHookHandler-%d")
|
|
||||||
.build()
|
|
||||||
);
|
|
||||||
Metrics.executor(registry, executorService, "GitInternalHookHandler", "cached");
|
|
||||||
return executorService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
internalThreadHookHandler.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -38,6 +38,7 @@ import sonia.scm.repository.GitChangesetConverterFactory;
|
|||||||
import sonia.scm.repository.GitRepositoryHandler;
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
import sonia.scm.repository.RepositoryHookType;
|
import sonia.scm.repository.RepositoryHookType;
|
||||||
import sonia.scm.repository.spi.GitHookContextProvider;
|
import sonia.scm.repository.spi.GitHookContextProvider;
|
||||||
|
import sonia.scm.repository.spi.HookEventFacade;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -49,18 +50,18 @@ import java.util.List;
|
|||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
public class GitReceiveHook implements PreReceiveHook, PostReceiveHook
|
public class GitReceiveHook implements PreReceiveHook, PostReceiveHook {
|
||||||
{
|
|
||||||
|
|
||||||
/** the logger for GitReceiveHook */
|
private static final Logger LOG = LoggerFactory.getLogger(GitReceiveHook.class);
|
||||||
private static final Logger logger =
|
|
||||||
LoggerFactory.getLogger(GitReceiveHook.class);
|
|
||||||
|
|
||||||
private final GitRepositoryHandler handler;
|
private final GitRepositoryHandler handler;
|
||||||
private final GitChangesetConverterFactory converterFactory;
|
private final GitChangesetConverterFactory converterFactory;
|
||||||
private final GitHookEventFacade hookEventFacade;
|
private final HookEventFacade hookEventFacade;
|
||||||
|
|
||||||
public GitReceiveHook(GitChangesetConverterFactory converterFactory, GitHookEventFacade hookEventFacade,
|
private GitHookContextProvider postReceiveContext;
|
||||||
|
|
||||||
|
public GitReceiveHook(GitChangesetConverterFactory converterFactory,
|
||||||
|
HookEventFacade hookEventFacade,
|
||||||
GitRepositoryHandler handler)
|
GitRepositoryHandler handler)
|
||||||
{
|
{
|
||||||
this.converterFactory = converterFactory;
|
this.converterFactory = converterFactory;
|
||||||
@@ -69,69 +70,60 @@ public class GitReceiveHook implements PreReceiveHook, PostReceiveHook
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPostReceive(ReceivePack rpack,
|
public void onPostReceive(ReceivePack rpack, Collection<ReceiveCommand> receiveCommands) {
|
||||||
Collection<ReceiveCommand> receiveCommands)
|
|
||||||
{
|
|
||||||
onReceive(rpack, receiveCommands, RepositoryHookType.POST_RECEIVE);
|
onReceive(rpack, receiveCommands, RepositoryHookType.POST_RECEIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPreReceive(ReceivePack rpack,
|
public void onPreReceive(ReceivePack rpack, Collection<ReceiveCommand> receiveCommands) {
|
||||||
Collection<ReceiveCommand> receiveCommands)
|
|
||||||
{
|
|
||||||
onReceive(rpack, receiveCommands, RepositoryHookType.PRE_RECEIVE);
|
onReceive(rpack, receiveCommands, RepositoryHookType.PRE_RECEIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleReceiveCommands(ReceivePack rpack,
|
public void afterReceive() {
|
||||||
List<ReceiveCommand> receiveCommands, RepositoryHookType type)
|
if (postReceiveContext != null) {
|
||||||
{
|
LOG.debug("firing {} hook for repository {}", RepositoryHookType.POST_RECEIVE, postReceiveContext.getRepositoryId());
|
||||||
try
|
try {
|
||||||
{
|
hookEventFacade.handle(postReceiveContext.getRepositoryId()).fireHookEvent(RepositoryHookType.POST_RECEIVE, postReceiveContext);
|
||||||
|
} finally {
|
||||||
|
postReceiveContext = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.debug("No context found for event type {}", RepositoryHookType.POST_RECEIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleReceiveCommands(ReceivePack rpack, List<ReceiveCommand> receiveCommands, RepositoryHookType type) {
|
||||||
|
try {
|
||||||
Repository repository = rpack.getRepository();
|
Repository repository = rpack.getRepository();
|
||||||
String repositoryId = resolveRepositoryId(repository);
|
String repositoryId = resolveRepositoryId(repository);
|
||||||
|
|
||||||
logger.trace("resolved repository to {}", repositoryId);
|
LOG.trace("resolved repository to {}", repositoryId);
|
||||||
|
|
||||||
GitHookContextProvider context = new GitHookContextProvider(converterFactory, rpack, receiveCommands, repository, repositoryId);
|
GitHookContextProvider context = new GitHookContextProvider(converterFactory, rpack, receiveCommands, repository, repositoryId);
|
||||||
|
|
||||||
hookEventFacade.fire(type, context);
|
if (type == RepositoryHookType.POST_RECEIVE) {
|
||||||
|
postReceiveContext = context;
|
||||||
|
} else {
|
||||||
|
hookEventFacade.handle(repositoryId).fireHookEvent(type, context);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
} catch (Exception ex) {
|
||||||
{
|
LOG.error("could not handle receive commands", ex);
|
||||||
logger.error("could not handle receive commands", ex);
|
|
||||||
|
|
||||||
GitHooks.abortIfPossible(type, rpack, receiveCommands, ex.getMessage());
|
GitHooks.abortIfPossible(type, rpack, receiveCommands, ex.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void onReceive(ReceivePack rpack, Collection<ReceiveCommand> commands, RepositoryHookType type) {
|
||||||
* Method description
|
LOG.trace("received git hook, type={}", type);
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param rpack
|
|
||||||
* @param commands
|
|
||||||
* @param type
|
|
||||||
*/
|
|
||||||
private void onReceive(ReceivePack rpack,
|
|
||||||
Collection<ReceiveCommand> commands, RepositoryHookType type)
|
|
||||||
{
|
|
||||||
if (logger.isTraceEnabled())
|
|
||||||
{
|
|
||||||
logger.trace("received git hook, type={}", type);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ReceiveCommand> receiveCommands = GitHooks.filterReceiveable(type,
|
List<ReceiveCommand> receiveCommands = GitHooks.filterReceiveable(type, commands);
|
||||||
commands);
|
|
||||||
|
|
||||||
GitFileHook.execute(type, rpack, commands);
|
GitFileHook.execute(type, rpack, commands);
|
||||||
|
|
||||||
if (!receiveCommands.isEmpty())
|
if (!receiveCommands.isEmpty()) {
|
||||||
{
|
|
||||||
handleReceiveCommands(rpack, receiveCommands, type);
|
handleReceiveCommands(rpack, receiveCommands, type);
|
||||||
}
|
} else {
|
||||||
else if (logger.isDebugEnabled())
|
LOG.debug("no receive commands found to process");
|
||||||
{
|
|
||||||
logger.debug("no receive commands found to process");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,8 +137,7 @@ public class GitReceiveHook implements PreReceiveHook, PostReceiveHook
|
|||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private String resolveRepositoryId(Repository repository)
|
private String resolveRepositoryId(Repository repository) {
|
||||||
{
|
|
||||||
StoredConfig gitConfig = repository.getConfig();
|
StoredConfig gitConfig = repository.getConfig();
|
||||||
return handler.getRepositoryId(gitConfig);
|
return handler.getRepositoryId(gitConfig);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
|||||||
import sonia.scm.protocolcommand.git.BaseReceivePackFactory;
|
import sonia.scm.protocolcommand.git.BaseReceivePackFactory;
|
||||||
import sonia.scm.repository.GitChangesetConverterFactory;
|
import sonia.scm.repository.GitChangesetConverterFactory;
|
||||||
import sonia.scm.repository.GitRepositoryHandler;
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
|
import sonia.scm.repository.spi.HookEventFacade;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@ public class GitReceivePackFactory extends BaseReceivePackFactory<HttpServletReq
|
|||||||
@Inject
|
@Inject
|
||||||
public GitReceivePackFactory(GitChangesetConverterFactory converterFactory,
|
public GitReceivePackFactory(GitChangesetConverterFactory converterFactory,
|
||||||
GitRepositoryHandler handler,
|
GitRepositoryHandler handler,
|
||||||
GitHookEventFacade hookEventFacade,
|
HookEventFacade hookEventFacade,
|
||||||
GitRepositoryConfigStoreProvider gitRepositoryConfigStoreProvider) {
|
GitRepositoryConfigStoreProvider gitRepositoryConfigStoreProvider) {
|
||||||
super(converterFactory, handler, hookEventFacade, gitRepositoryConfigStoreProvider);
|
super(converterFactory, handler, hookEventFacade, gitRepositoryConfigStoreProvider);
|
||||||
this.wrapped = new DefaultReceivePackFactory();
|
this.wrapped = new DefaultReceivePackFactory();
|
||||||
|
|||||||
@@ -73,12 +73,11 @@ public class ScmGitServlet extends GitServlet implements ScmProviderHttpServlet
|
|||||||
GitReceivePackFactory receivePackFactory,
|
GitReceivePackFactory receivePackFactory,
|
||||||
GitRepositoryViewer repositoryViewer,
|
GitRepositoryViewer repositoryViewer,
|
||||||
RepositoryRequestListenerUtil repositoryRequestListenerUtil,
|
RepositoryRequestListenerUtil repositoryRequestListenerUtil,
|
||||||
LfsServletFactory lfsServletFactory, GitHookEventFacade gitHookEventFacade)
|
LfsServletFactory lfsServletFactory)
|
||||||
{
|
{
|
||||||
this.repositoryViewer = repositoryViewer;
|
this.repositoryViewer = repositoryViewer;
|
||||||
this.repositoryRequestListenerUtil = repositoryRequestListenerUtil;
|
this.repositoryRequestListenerUtil = repositoryRequestListenerUtil;
|
||||||
this.lfsServletFactory = lfsServletFactory;
|
this.lfsServletFactory = lfsServletFactory;
|
||||||
this.gitHookEventFacade = gitHookEventFacade;
|
|
||||||
|
|
||||||
setRepositoryResolver(repositoryResolver);
|
setRepositoryResolver(repositoryResolver);
|
||||||
setReceivePackFactory(receivePackFactory);
|
setReceivePackFactory(receivePackFactory);
|
||||||
@@ -113,12 +112,7 @@ public class ScmGitServlet extends GitServlet implements ScmProviderHttpServlet
|
|||||||
} else if (isRegularGitAPIRequest(request)) {
|
} else if (isRegularGitAPIRequest(request)) {
|
||||||
logger.trace("handle regular git request");
|
logger.trace("handle regular git request");
|
||||||
// continue with the regular git Backend
|
// continue with the regular git Backend
|
||||||
try {
|
|
||||||
handleRegularGitRequest(request, response, repository);
|
handleRegularGitRequest(request, response, repository);
|
||||||
gitHookEventFacade.firePending();
|
|
||||||
} finally {
|
|
||||||
gitHookEventFacade.clean();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
logger.trace("handle browser request");
|
logger.trace("handle browser request");
|
||||||
handleBrowserRequest(request, response, repository);
|
handleBrowserRequest(request, response, repository);
|
||||||
@@ -240,6 +234,4 @@ public class ScmGitServlet extends GitServlet implements ScmProviderHttpServlet
|
|||||||
private final GitRepositoryViewer repositoryViewer;
|
private final GitRepositoryViewer repositoryViewer;
|
||||||
|
|
||||||
private final LfsServletFactory lfsServletFactory;
|
private final LfsServletFactory lfsServletFactory;
|
||||||
|
|
||||||
private final GitHookEventFacade gitHookEventFacade;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
package sonia.scm.repository.spi;
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
|
||||||
import org.eclipse.jgit.transport.ScmTransportProtocol;
|
import org.eclipse.jgit.transport.ScmTransportProtocol;
|
||||||
import org.eclipse.jgit.transport.Transport;
|
import org.eclipse.jgit.transport.Transport;
|
||||||
import org.junit.rules.ExternalResource;
|
import org.junit.rules.ExternalResource;
|
||||||
@@ -33,7 +32,6 @@ import sonia.scm.repository.GitTestHelper;
|
|||||||
import sonia.scm.repository.PreProcessorUtil;
|
import sonia.scm.repository.PreProcessorUtil;
|
||||||
import sonia.scm.repository.RepositoryManager;
|
import sonia.scm.repository.RepositoryManager;
|
||||||
import sonia.scm.repository.api.HookContextFactory;
|
import sonia.scm.repository.api.HookContextFactory;
|
||||||
import sonia.scm.web.GitHookEventFacade;
|
|
||||||
|
|
||||||
import static com.google.inject.util.Providers.of;
|
import static com.google.inject.util.Providers.of;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
@@ -45,12 +43,12 @@ class BindTransportProtocolRule extends ExternalResource {
|
|||||||
private ScmTransportProtocol scmTransportProtocol;
|
private ScmTransportProtocol scmTransportProtocol;
|
||||||
|
|
||||||
RepositoryManager repositoryManager = mock(RepositoryManager.class);
|
RepositoryManager repositoryManager = mock(RepositoryManager.class);
|
||||||
GitHookEventFacade hookEventFacade;
|
HookEventFacade hookEventFacade;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void before() {
|
protected void before() {
|
||||||
HookContextFactory hookContextFactory = new HookContextFactory(mock(PreProcessorUtil.class));
|
HookContextFactory hookContextFactory = new HookContextFactory(mock(PreProcessorUtil.class));
|
||||||
hookEventFacade = new GitHookEventFacade(new HookEventFacade(of(repositoryManager), hookContextFactory), new SimpleMeterRegistry());
|
hookEventFacade = new HookEventFacade(of(repositoryManager), hookContextFactory);
|
||||||
GitRepositoryHandler gitRepositoryHandler = mock(GitRepositoryHandler.class);
|
GitRepositoryHandler gitRepositoryHandler = mock(GitRepositoryHandler.class);
|
||||||
scmTransportProtocol = new ScmTransportProtocol(of(GitTestHelper.createConverterFactory()), of(hookEventFacade), of(gitRepositoryHandler));
|
scmTransportProtocol = new ScmTransportProtocol(of(GitTestHelper.createConverterFactory()), of(hookEventFacade), of(gitRepositoryHandler));
|
||||||
|
|
||||||
@@ -63,6 +61,5 @@ class BindTransportProtocolRule extends ExternalResource {
|
|||||||
@Override
|
@Override
|
||||||
protected void after() {
|
protected void after() {
|
||||||
Transport.unregister(scmTransportProtocol);
|
Transport.unregister(scmTransportProtocol);
|
||||||
hookEventFacade.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import org.eclipse.jgit.lib.Repository;
|
|||||||
import org.eclipse.jgit.transport.ScmTransportProtocol;
|
import org.eclipse.jgit.transport.ScmTransportProtocol;
|
||||||
import org.eclipse.jgit.transport.Transport;
|
import org.eclipse.jgit.transport.Transport;
|
||||||
import org.eclipse.jgit.transport.URIish;
|
import org.eclipse.jgit.transport.URIish;
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -44,7 +43,6 @@ import sonia.scm.repository.api.HookContextFactory;
|
|||||||
import sonia.scm.repository.work.NoneCachingWorkingCopyPool;
|
import sonia.scm.repository.work.NoneCachingWorkingCopyPool;
|
||||||
import sonia.scm.repository.work.WorkdirProvider;
|
import sonia.scm.repository.work.WorkdirProvider;
|
||||||
import sonia.scm.repository.work.WorkingCopy;
|
import sonia.scm.repository.work.WorkingCopy;
|
||||||
import sonia.scm.web.GitHookEventFacade;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -63,23 +61,17 @@ public class SimpleGitWorkingCopyFactoryTest extends AbstractGitCommandTestBase
|
|||||||
// keep this so that it will not be garbage collected (Transport keeps this in a week reference)
|
// keep this so that it will not be garbage collected (Transport keeps this in a week reference)
|
||||||
private ScmTransportProtocol proto;
|
private ScmTransportProtocol proto;
|
||||||
private WorkdirProvider workdirProvider;
|
private WorkdirProvider workdirProvider;
|
||||||
GitHookEventFacade hookEventFacade;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void bindScmProtocol() throws IOException {
|
public void bindScmProtocol() throws IOException {
|
||||||
HookContextFactory hookContextFactory = new HookContextFactory(mock(PreProcessorUtil.class));
|
HookContextFactory hookContextFactory = new HookContextFactory(mock(PreProcessorUtil.class));
|
||||||
hookEventFacade = new GitHookEventFacade(new HookEventFacade(of(mock(RepositoryManager.class)), hookContextFactory), new SimpleMeterRegistry());
|
HookEventFacade hookEventFacade = new HookEventFacade(of(mock(RepositoryManager.class)), hookContextFactory);
|
||||||
GitRepositoryHandler gitRepositoryHandler = mock(GitRepositoryHandler.class);
|
GitRepositoryHandler gitRepositoryHandler = mock(GitRepositoryHandler.class);
|
||||||
proto = new ScmTransportProtocol(of(GitTestHelper.createConverterFactory()), of(hookEventFacade), of(gitRepositoryHandler));
|
proto = new ScmTransportProtocol(of(GitTestHelper.createConverterFactory()), of(hookEventFacade), of(gitRepositoryHandler));
|
||||||
Transport.register(proto);
|
Transport.register(proto);
|
||||||
workdirProvider = new WorkdirProvider(temporaryFolder.newFolder(), repositoryLocationResolver, false);
|
workdirProvider = new WorkdirProvider(temporaryFolder.newFolder(), repositoryLocationResolver, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
|
||||||
public void close() {
|
|
||||||
hookEventFacade.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyPoolShouldCreateNewWorkdir() {
|
public void emptyPoolShouldCreateNewWorkdir() {
|
||||||
SimpleGitWorkingCopyFactory factory = new SimpleGitWorkingCopyFactory(new NoneCachingWorkingCopyPool(workdirProvider), new SimpleMeterRegistry());
|
SimpleGitWorkingCopyFactory factory = new SimpleGitWorkingCopyFactory(new NoneCachingWorkingCopyPool(workdirProvider), new SimpleMeterRegistry());
|
||||||
|
|||||||
Reference in New Issue
Block a user