diff --git a/gradle/changelog/mirror_post_receive_hook_git.yaml b/gradle/changelog/mirror_post_receive_hook_git.yaml new file mode 100644 index 0000000000..2d068106ba --- /dev/null +++ b/gradle/changelog/mirror_post_receive_hook_git.yaml @@ -0,0 +1,2 @@ +- type: fixed + description: Post receive hook events after mirror update for git ([#1703](https://github.com/scm-manager/scm-manager/pulls/1703)) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/MirrorRefFilter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/MirrorRefFilter.java deleted file mode 100644 index 13baa14106..0000000000 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/MirrorRefFilter.java +++ /dev/null @@ -1,44 +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.protocolcommand.git; - -import org.eclipse.jgit.lib.Ref; -import sonia.scm.repository.spi.GitMirrorCommand; - -import java.util.Map; -import java.util.stream.Collectors; - -final class MirrorRefFilter { - - private MirrorRefFilter() { - } - - static Map filterMirrors(Map refs) { - return refs.entrySet() - .stream() - .filter(entry -> !entry.getKey().startsWith(GitMirrorCommand.MIRROR_REF_PREFIX)) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } -} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/ScmUploadPackFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/ScmUploadPackFactory.java index f547ae69d8..9ed0b44076 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/ScmUploadPackFactory.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/ScmUploadPackFactory.java @@ -32,8 +32,6 @@ import sonia.scm.protocolcommand.RepositoryContext; public class ScmUploadPackFactory implements UploadPackFactory { @Override public UploadPack create(RepositoryContext repositoryContext, Repository repository) { - UploadPack uploadPack = new UploadPack(repository); - uploadPack.setRefFilter(MirrorRefFilter::filterMirrors); - return uploadPack; + return new UploadPack(repository); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/ScmUploadPackFactoryForHttpServletRequest.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/ScmUploadPackFactoryForHttpServletRequest.java deleted file mode 100644 index cec28340b9..0000000000 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/ScmUploadPackFactoryForHttpServletRequest.java +++ /dev/null @@ -1,40 +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.protocolcommand.git; - -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.transport.UploadPack; -import org.eclipse.jgit.transport.resolver.UploadPackFactory; - -import javax.servlet.http.HttpServletRequest; - -public class ScmUploadPackFactoryForHttpServletRequest implements UploadPackFactory { - @Override - public UploadPack create(HttpServletRequest repositoryContext, Repository repository) { - UploadPack uploadPack = new UploadPack(repository); - uploadPack.setRefFilter(MirrorRefFilter::filterMirrors); - return uploadPack; - } -} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java index 8ad6e3ddf4..c765ae93d2 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java @@ -38,6 +38,7 @@ import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.transport.PushResult; +import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.RemoteRefUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,8 +55,10 @@ import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; +import static java.util.Arrays.stream; import static java.util.Optional.empty; import static java.util.Optional.of; +import static java.util.stream.Collectors.toList; import static sonia.scm.ContextEntry.ContextBuilder.entity; import static sonia.scm.NotFoundException.notFound; import static sonia.scm.repository.GitUtil.getBranchIdOrCurrentHead; @@ -179,7 +182,7 @@ class AbstractGitCommand { logger.trace("could not checkout branch {} directly; trying to create local branch", branchName, e); checkOutTargetAsNewLocalBranch(branchName); } catch (GitAPIException e) { - throw new InternalRepositoryException(context.getRepository(), "could not checkout branch: " + branchName, e); + throw new InternalRepositoryException(repository, "could not checkout branch: " + branchName, e); } } @@ -189,9 +192,9 @@ class AbstractGitCommand { clone.checkout().setStartPoint(targetRevision.getName()).setName(branchName).setCreateBranch(true).call(); } catch (RefNotFoundException e) { logger.debug("could not checkout branch {} as local branch", branchName, e); - throw notFound(entity("Revision", branchName).in(context.getRepository())); + throw notFound(entity("Revision", branchName).in(repository)); } catch (GitAPIException e) { - throw new InternalRepositoryException(context.getRepository(), "could not checkout branch as local branch: " + branchName, e); + throw new InternalRepositoryException(repository, "could not checkout branch as local branch: " + branchName, e); } } @@ -210,7 +213,7 @@ class AbstractGitCommand { throw doThrow.get(); } } catch (GitAPIException e) { - throw new InternalRepositoryException(context.getRepository(), "could not read status of repository", e); + throw new InternalRepositoryException(repository, "could not read status of repository", e); } } @@ -230,7 +233,7 @@ class AbstractGitCommand { return empty(); } } catch (GitAPIException | IOException e) { - throw new InternalRepositoryException(context.getRepository(), "could not commit changes", e); + throw new InternalRepositoryException(repository, "could not commit changes", e); } } @@ -238,9 +241,14 @@ class AbstractGitCommand { return clone.getRepository().readMergeHeads() != null && !clone.getRepository().readMergeHeads().isEmpty(); } - void push() { + void push(String... refSpecs) { try { - Iterable pushResults = clone.push().call(); + Iterable pushResults = + clone + .push() + .setRefSpecs(stream(refSpecs).map(RefSpec::new).collect(toList())) + .setForce(true) + .call(); Iterator pushResultIterator = pushResults.iterator(); if (!pushResultIterator.hasNext()) { throw new InternalRepositoryException(repository, "got no result from push"); @@ -252,7 +260,7 @@ class AbstractGitCommand { } remoteUpdates .stream() - .filter(remoteRefUpdate -> remoteRefUpdate.getStatus() != RemoteRefUpdate.Status.OK) + .filter(remoteRefUpdate -> remoteRefUpdate.getStatus() != RemoteRefUpdate.Status.OK && remoteRefUpdate.getStatus() != RemoteRefUpdate.Status.UP_TO_DATE) .findAny() .ifPresent(remoteRefUpdate -> { logger.info("message for failed push: {}", pushResult.getMessages()); diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMirrorCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMirrorCommand.java index de6cd264ac..c785d72c23 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMirrorCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMirrorCommand.java @@ -32,8 +32,8 @@ import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefDatabase; import org.eclipse.jgit.lib.RefUpdate; -import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevTag; @@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory; import sonia.scm.repository.Changeset; import sonia.scm.repository.GitChangesetConverter; import sonia.scm.repository.GitChangesetConverterFactory; +import sonia.scm.repository.GitWorkingCopyFactory; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Tag; import sonia.scm.repository.api.MirrorCommandResult; @@ -92,22 +93,20 @@ import static sonia.scm.repository.api.MirrorCommandResult.ResultType.REJECTED_U */ public class GitMirrorCommand extends AbstractGitCommand implements MirrorCommand { - public static final String MIRROR_REF_PREFIX = "refs/mirror/"; - private static final Logger LOG = LoggerFactory.getLogger(GitMirrorCommand.class); - private final PostReceiveRepositoryHookEventFactory postReceiveRepositoryHookEventFactory; private final MirrorHttpConnectionProvider mirrorHttpConnectionProvider; private final GitChangesetConverterFactory converterFactory; private final GitTagConverter gitTagConverter; + private final GitWorkingCopyFactory workingCopyFactory; @Inject - GitMirrorCommand(GitContext context, PostReceiveRepositoryHookEventFactory postReceiveRepositoryHookEventFactory, MirrorHttpConnectionProvider mirrorHttpConnectionProvider, GitChangesetConverterFactory converterFactory, GitTagConverter gitTagConverter) { + GitMirrorCommand(GitContext context, MirrorHttpConnectionProvider mirrorHttpConnectionProvider, GitChangesetConverterFactory converterFactory, GitTagConverter gitTagConverter, GitWorkingCopyFactory workingCopyFactory) { super(context); this.mirrorHttpConnectionProvider = mirrorHttpConnectionProvider; - this.postReceiveRepositoryHookEventFactory = postReceiveRepositoryHookEventFactory; this.converterFactory = converterFactory; this.gitTagConverter = gitTagConverter; + this.workingCopyFactory = workingCopyFactory; } @Override @@ -117,36 +116,33 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman @Override public MirrorCommandResult update(MirrorCommandRequest mirrorCommandRequest) { - try (Repository repository = context.open(); Git git = Git.wrap(repository)) { - return new Worker(mirrorCommandRequest, repository, git).update(); - } catch (IOException e) { - throw new InternalRepositoryException(context.getRepository(), "error during git fetch", e); - } + return inClone(git -> new Worker(context, mirrorCommandRequest, repository, git), workingCopyFactory, null); } - private class Worker { + private class Worker extends GitCloneWorker { private final MirrorCommandRequest mirrorCommandRequest; private final List mirrorLog = new ArrayList<>(); private final Stopwatch stopwatch; - private final Repository repository; private final Git git; + private final Collection deletedRefs = new ArrayList<>(); + private FetchResult fetchResult; private GitFilterContext filterContext; private MirrorFilter.Filter filter; private ResultType result = OK; - private Worker(MirrorCommandRequest mirrorCommandRequest, Repository repository, Git git) { + private Worker(GitContext context, MirrorCommandRequest mirrorCommandRequest, sonia.scm.repository.Repository repository, Git git) { + super(git, context, repository); this.mirrorCommandRequest = mirrorCommandRequest; - this.repository = repository; this.git = git; stopwatch = Stopwatch.createStarted(); } - MirrorCommandResult update() { + MirrorCommandResult run() { try { return doUpdate(); } catch (GitAPIException e) { @@ -158,6 +154,7 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman } private MirrorCommandResult doUpdate() throws GitAPIException { + copyRemoteRefsToMain(); fetchResult = createFetchCommand().call(); filterContext = new GitFilterContext(); filter = mirrorCommandRequest.getFilter().getFilter(filterContext); @@ -169,13 +166,44 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman handleTags(); } - postReceiveRepositoryHookEventFactory.fireForFetch(git, fetchResult); + push(generatePushRefSpecs().toArray(new String[0])); return new MirrorCommandResult(result, mirrorLog, stopwatch.stop().elapsed()); } + private Collection generatePushRefSpecs() { + Collection refSpecs = new ArrayList<>(); + refSpecs.add("refs/heads/*:refs/heads/*"); + refSpecs.add("refs/tags/*:refs/tags/*"); + deletedRefs.forEach(deletedRef -> refSpecs.add(":" + deletedRef)); + return refSpecs; + } + + private void copyRemoteRefsToMain() { + try { + RefDatabase refDatabase = git.getRepository().getRefDatabase(); + refDatabase.getRefs() + .stream() + .filter(ref -> ref.getName().startsWith("refs/remotes/origin/")) + .forEach( + ref -> { + try { + String baseName = ref.getName().substring("refs/remotes/origin/".length()); + RefUpdate refUpdate = refDatabase.newUpdate("refs/heads/" + baseName, true); + refUpdate.setNewObjectId(ref.getObjectId()); + refUpdate.forceUpdate(); + } catch (IOException e) { + throw new InternalRepositoryException(context.getRepository(), "Failed to copy origin remote refs " + ref.getName(), e); + } + } + ); + } catch (IOException e) { + throw new InternalRepositoryException(context.getRepository(), "Failed to copy remote refs", e); + } + } + private void handleBranches() { LoggerWithHeader logger = new LoggerWithHeader("Branches:"); - doForEachRefStartingWith(MIRROR_REF_PREFIX + "heads", ref -> handleBranch(logger, ref)); + doForEachRefStartingWith("refs/heads", ref -> handleBranch(logger, ref)); } private void handleBranch(LoggerWithHeader logger, TrackingRefUpdate ref) { @@ -185,7 +213,7 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman private void handleTags() { LoggerWithHeader logger = new LoggerWithHeader("Tags:"); - doForEachRefStartingWith(MIRROR_REF_PREFIX + "tags", ref -> handleTag(logger, ref)); + doForEachRefStartingWith("refs/tags", ref -> handleTag(logger, ref)); } private void handleTag(LoggerWithHeader logger, TrackingRefUpdate ref) { @@ -209,7 +237,7 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman private void handleRef(Function filter) { Result filterResult = filter.apply(ref); try { - String referenceName = ref.getLocalName().substring(MIRROR_REF_PREFIX.length() + refType.length()); + String referenceName = ref.getLocalName().substring("refs/".length() + refType.length()); if (filterResult.isAccepted()) { handleAcceptedReference(referenceName); } else { @@ -245,15 +273,14 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman logger.logChange(ref, referenceName, filterResult.getRejectReason().orElse("rejected due to filter")); } - private void handleAcceptedReference(String referenceName) throws IOException { + private void handleAcceptedReference(String referenceName) { String targetRef = "refs/" + refType + referenceName; if (isDeletedReference(ref)) { LOG.trace("deleting {} ref in {}: {}", typeForLog, GitMirrorCommand.this.repository, targetRef); - deleteReference(targetRef); logger.logChange(ref, referenceName, "deleted"); + deletedRefs.add(targetRef); } else { LOG.trace("updating {} ref in {}: {}", typeForLog, GitMirrorCommand.this.repository, targetRef); - updateReference(targetRef, ref.getNewObjectId()); logger.logChange(ref, referenceName, getUpdateType(ref)); } } @@ -273,7 +300,7 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman } private void deleteReference(String targetRef) throws IOException { - RefUpdate deleteUpdate = repository.getRefDatabase().newUpdate(targetRef, true); + RefUpdate deleteUpdate = git.getRepository().getRefDatabase().newUpdate(targetRef, true); deleteUpdate.setForceUpdate(true); deleteUpdate.delete(); } @@ -284,7 +311,7 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman private void updateReference(String reference, ObjectId objectId) throws IOException { LOG.trace("updating ref in {}: {} -> {}", GitMirrorCommand.this.repository, reference, objectId); - RefUpdate refUpdate = repository.getRefDatabase().newUpdate(reference, true); + RefUpdate refUpdate = git.getRepository().getRefDatabase().newUpdate(reference, true); refUpdate.setNewObjectId(objectId); refUpdate.forceUpdate(); } @@ -335,8 +362,8 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman } private FetchCommand createFetchCommand() { - FetchCommand fetchCommand = Git.wrap(repository).fetch() - .setRefSpecs("refs/heads/*:" + MIRROR_REF_PREFIX + "heads/*", "refs/tags/*:" + MIRROR_REF_PREFIX + "tags/*") + FetchCommand fetchCommand = Git.wrap(git.getRepository()).fetch() + .setRefSpecs("refs/heads/*:refs/heads/*", "refs/tags/*:refs/tags/*") .setForceUpdate(true) .setRemoveDeletedRefs(true) .setRemote(mirrorCommandRequest.getSourceUrl()); @@ -370,10 +397,10 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman Map extractedTagUpdates = new HashMap<>(); fetchResult.getTrackingRefUpdates().forEach(refUpdate -> { - if (refUpdate.getLocalName().startsWith(MIRROR_REF_PREFIX + "heads")) { + if (refUpdate.getLocalName().startsWith("refs/heads")) { extractedBranchUpdates.put(refUpdate.getLocalName(), new GitBranchUpdate(refUpdate)); } - if (refUpdate.getLocalName().startsWith(MIRROR_REF_PREFIX + "tags")) { + if (refUpdate.getLocalName().startsWith("refs/tags")) { extractedTagUpdates.put(refUpdate.getLocalName(), new GitTagUpdate(refUpdate)); } }); @@ -411,7 +438,7 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman public GitBranchUpdate(TrackingRefUpdate refUpdate) { this.refUpdate = refUpdate; - this.branchName = refUpdate.getLocalName().substring(MIRROR_REF_PREFIX.length() + "heads/".length()); + this.branchName = refUpdate.getLocalName().substring("refs/heads/".length()); } @Override @@ -457,18 +484,16 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman } private Changeset computeChangeset() { - try (RevWalk revWalk = new RevWalk(repository); GitChangesetConverter gitChangesetConverter = converter(revWalk)) { - try { - RevCommit revCommit = revWalk.parseCommit(refUpdate.getNewObjectId()); - return gitChangesetConverter.createChangeset(revCommit, refUpdate.getLocalName()); - } catch (Exception e) { - throw new InternalRepositoryException(context.getRepository(), "got exception while validating branch", e); - } + try (RevWalk revWalk = new RevWalk(git.getRepository()); GitChangesetConverter gitChangesetConverter = converter(revWalk)) { + RevCommit revCommit = revWalk.parseCommit(refUpdate.getNewObjectId()); + return gitChangesetConverter.createChangeset(revCommit, refUpdate.getLocalName()); + } catch (Exception e) { + throw new InternalRepositoryException(context.getRepository(), "got exception while validating branch", e); } } private GitChangesetConverter converter(RevWalk revWalk) { - return converterFactory.builder(repository) + return converterFactory.builder(git.getRepository()) .withRevWalk(revWalk) .withAdditionalPublicKeys(mirrorCommandRequest.getPublicKeys()) .create(); @@ -485,7 +510,7 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman public GitTagUpdate(TrackingRefUpdate refUpdate) { this.refUpdate = refUpdate; - this.tagName = refUpdate.getLocalName().substring(MIRROR_REF_PREFIX.length() + "tags/".length()); + this.tagName = refUpdate.getLocalName().substring("refs/tags/".length()); } @Override @@ -523,22 +548,20 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman } private Tag computeTag() { - try (RevWalk revWalk = new RevWalk(repository)) { - try { - RevObject revObject = revWalk.parseAny(refUpdate.getNewObjectId()); - if (revObject.getType() == Constants.OBJ_TAG) { - RevTag revTag = revWalk.parseTag(revObject.getId()); - return gitTagConverter.buildTag(revTag, revWalk); - } else if (revObject.getType() == Constants.OBJ_COMMIT) { - Ref ref = repository.getRefDatabase().findRef(refUpdate.getLocalName()); - Tag t = gitTagConverter.buildTag(repository, revWalk, ref); - return new Tag(tagName, t.getRevision(), t.getDate().orElse(null), t.getDeletable()); - } else { - throw new InternalRepositoryException(context.getRepository(), "invalid object type for tag"); - } - } catch (Exception e) { - throw new InternalRepositoryException(context.getRepository(), "got exception while validating tag", e); + try (RevWalk revWalk = new RevWalk(git.getRepository())) { + RevObject revObject = revWalk.parseAny(refUpdate.getNewObjectId()); + if (revObject.getType() == Constants.OBJ_TAG) { + RevTag revTag = revWalk.parseTag(revObject.getId()); + return gitTagConverter.buildTag(revTag, revWalk); + } else if (revObject.getType() == Constants.OBJ_COMMIT) { + Ref ref = git.getRepository().getRefDatabase().findRef(refUpdate.getLocalName()); + Tag t = gitTagConverter.buildTag(git.getRepository(), revWalk, ref); + return new Tag(tagName, t.getRevision(), t.getDate().orElse(null), t.getDeletable()); + } else { + throw new InternalRepositoryException(context.getRepository(), "invalid object type for tag"); } + } catch (Exception e) { + throw new InternalRepositoryException(context.getRepository(), "got exception while validating tag", e); } } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java index 0fb4ec9196..034e8fa84d 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java @@ -30,7 +30,6 @@ import com.google.inject.Singleton; import org.eclipse.jgit.http.server.GitServlet; import org.eclipse.jgit.lfs.lib.Constants; import org.slf4j.Logger; -import sonia.scm.protocolcommand.git.ScmUploadPackFactoryForHttpServletRequest; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryRequestListenerUtil; import sonia.scm.repository.spi.ScmProviderHttpServlet; @@ -72,7 +71,6 @@ public class ScmGitServlet extends GitServlet implements ScmProviderHttpServlet @Inject public ScmGitServlet(GitRepositoryResolver repositoryResolver, GitReceivePackFactory receivePackFactory, - ScmUploadPackFactoryForHttpServletRequest scmUploadPackFactory, GitRepositoryViewer repositoryViewer, RepositoryRequestListenerUtil repositoryRequestListenerUtil, LfsServletFactory lfsServletFactory) @@ -83,7 +81,6 @@ public class ScmGitServlet extends GitServlet implements ScmProviderHttpServlet setRepositoryResolver(repositoryResolver); setReceivePackFactory(receivePackFactory); - setUploadPackFactory(scmUploadPackFactory); } //~--- methods -------------------------------------------------------------- diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/protocolcommand/git/MirrorRefFilterTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/protocolcommand/git/MirrorRefFilterTest.java deleted file mode 100644 index 1105ebbb83..0000000000 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/protocolcommand/git/MirrorRefFilterTest.java +++ /dev/null @@ -1,51 +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.protocolcommand.git; - -import org.eclipse.jgit.lib.Ref; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; -import static org.mockito.Mockito.mock; - -class MirrorRefFilterTest { - - @Test - void shouldRemoveMirrorRefs() { - Map refs = new HashMap<>(); - Ref master = mock(Ref.class); - Ref mirror = mock(Ref.class); - refs.put("refs/heads/master", master); - refs.put("refs/mirror/some/other", mirror); - - Map filteredRefs = MirrorRefFilter.filterMirrors(refs); - - assertThat(filteredRefs).containsOnly(entry("refs/heads/master", master)); - } -} diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMirrorCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMirrorCommandTest.java index bc2c2cb447..a5474add54 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMirrorCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMirrorCommandTest.java @@ -24,6 +24,7 @@ package sonia.scm.repository.spi; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.junit.http.AppServer; @@ -33,6 +34,7 @@ import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevWalk; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.repository.GitChangesetConverterFactory; @@ -40,6 +42,8 @@ import sonia.scm.repository.GitConfig; import sonia.scm.repository.api.MirrorCommandResult; import sonia.scm.repository.api.MirrorFilter; import sonia.scm.repository.api.SimpleUsernamePasswordCredential; +import sonia.scm.repository.work.NoneCachingWorkingCopyPool; +import sonia.scm.repository.work.WorkdirProvider; import sonia.scm.security.GPG; import sonia.scm.store.InMemoryConfigurationStoreFactory; @@ -52,10 +56,7 @@ import java.util.function.Consumer; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static sonia.scm.repository.api.MirrorCommandResult.ResultType.FAILED; import static sonia.scm.repository.api.MirrorCommandResult.ResultType.OK; @@ -63,10 +64,12 @@ import static sonia.scm.repository.api.MirrorCommandResult.ResultType.REJECTED_U public class GitMirrorCommandTest extends AbstractGitCommandTestBase { + @Rule + public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule(); + public static final Consumer ACCEPT_ALL = r -> { }; public static final Consumer REJECT_ALL = r -> r.setFilter(new DenyAllMirrorFilter()); - private final PostReceiveRepositoryHookEventFactory postReceiveRepositoryHookEventFactory = mock(PostReceiveRepositoryHookEventFactory.class); private final MirrorHttpConnectionProvider mirrorHttpConnectionProvider = mock(MirrorHttpConnectionProvider.class); private final GPG gpg = mock(GPG.class); private final GitChangesetConverterFactory gitChangesetConverterFactory = new GitChangesetConverterFactory(gpg); @@ -81,7 +84,15 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { Git.init().setBare(true).setDirectory(clone).call(); GitContext emptyContext = createMirrorContext(clone); - command = new GitMirrorCommand(emptyContext, postReceiveRepositoryHookEventFactory, mirrorHttpConnectionProvider, gitChangesetConverterFactory, gitTagConverter); + SimpleGitWorkingCopyFactory workingCopyFactory = + new SimpleGitWorkingCopyFactory( + new NoneCachingWorkingCopyPool(new WorkdirProvider(repositoryLocationResolver)), new SimpleMeterRegistry()); + command = new GitMirrorCommand( + emptyContext, + mirrorHttpConnectionProvider, + gitChangesetConverterFactory, + gitTagConverter, + workingCopyFactory); } @Test @@ -99,8 +110,6 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { assertThat(createdMirror.branchList().call()).isNotEmpty(); assertThat(createdMirror.tagList().call()).isNotEmpty(); } - - verify(postReceiveRepositoryHookEventFactory).fireForFetch(any(), any()); } @Test @@ -133,9 +142,6 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { Optional addedBranch = findBranch(updatedMirror, "added-branch"); assertThat(addedBranch).isPresent(); } - - // event should be thrown two times, once for the initial clone, and once for the update - verify(postReceiveRepositoryHookEventFactory, times(2)).fireForFetch(any(), any()); } @Test @@ -203,9 +209,6 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { Optional addedTag = findTag(updatedMirror, "added-tag"); assertThat(addedTag).hasValueSatisfying(ref -> assertThat(ref.getObjectId().getName()).isEqualTo("9e93d8631675a89615fac56b09209686146ff3c0")); } - - // event should be thrown two times, once for the initial clone, and once for the update - verify(postReceiveRepositoryHookEventFactory, times(2)).fireForFetch(any(), any()); } @Test @@ -506,8 +509,6 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { createCredential("wrong", "credentials")); assertThat(result.getResult()).isEqualTo(FAILED); - - verify(postReceiveRepositoryHookEventFactory, never()).fireForFetch(any(), any()); } finally { simpleHttpServer.stop(); } @@ -669,11 +670,13 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { return new Filter() { @Override public Result acceptTag(TagUpdate tagUpdate) { + tagUpdate.getTag(); updates.tagUpdates.add(tagUpdate); return Result.accept(); } @Override public Result acceptBranch(BranchUpdate branchUpdate) { + branchUpdate.getChangeset(); updates.branchUpdates.add(branchUpdate); return Result.accept(); } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTest.java index 2310be7c68..a83cb29123 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTest.java @@ -55,7 +55,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test public void shouldCreateCommit() throws IOException, GitAPIException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -76,7 +76,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test public void shouldCreateCommitOnSelectedBranch() throws IOException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -97,7 +97,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test public void shouldCreateNewFile() throws IOException, GitAPIException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -115,7 +115,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test public void shouldCreateNewFileWhenPathStartsWithSlash() throws IOException, GitAPIException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -133,7 +133,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test(expected = AlreadyExistsException.class) public void shouldFailIfOverwritingExistingFileWithoutOverwriteFlag() throws IOException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -147,7 +147,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test(expected = AlreadyExistsException.class) public void shouldFailIfPathAlreadyExistsAsAFile() throws IOException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -161,7 +161,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test public void shouldOverwriteExistingFileIfOverwriteFlagSet() throws IOException, GitAPIException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -179,7 +179,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test public void shouldModifyExistingFile() throws IOException, GitAPIException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -197,7 +197,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test(expected = NotFoundException.class) public void shouldFailIfFileToModifyDoesNotExist() throws IOException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -211,7 +211,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test(expected = BadRequestException.class) public void shouldFailIfNoChangesMade() throws IOException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "b\n".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "b\n".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -225,7 +225,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test(expected = ConcurrentModificationException.class) public void shouldFailBranchDoesNotHaveExpectedRevision() throws IOException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "irrelevant\n".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "irrelevant\n".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -281,7 +281,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test(expected = NotFoundException.class) public void shouldFailWithNotFoundExceptionIfBranchIsNoBranch() throws IOException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "irrelevant\n".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "irrelevant\n".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -296,7 +296,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test public void shouldSignCreatedCommit() throws IOException, GitAPIException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -317,7 +317,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test public void shouldNotSignCreatedCommitIfSigningDisabled() throws IOException, GitAPIException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -338,7 +338,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test public void shouldTriggerPostCommitHook() throws IOException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -359,7 +359,7 @@ public class GitModifyCommandTest extends GitModifyCommandTestBase { @Test(expected = ScmConstraintViolationException.class) public void shouldFailIfPathInGitMetadata() throws IOException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "other".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "other".getBytes()).toFile(); GitModifyCommand command = createCommand(); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTestBase.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTestBase.java index b1815b44f2..a485c115b6 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTestBase.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTestBase.java @@ -52,8 +52,6 @@ import static sonia.scm.repository.spi.GitRepositoryConfigStoreProviderTestUtil. @SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini", username = "admin", password = "secret") class GitModifyCommandTestBase extends AbstractGitCommandTestBase { - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Rule public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule(); @Rule diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java index 955d16965b..0246c9b0c6 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java @@ -97,7 +97,7 @@ public class GitModifyCommand_LFSTest extends GitModifyCommandTestBase { when(blob.getOutputStream()).thenReturn(outputStream); when(blob.getSize()).thenReturn((long) content.length()); - File newFile = Files.write(temporaryFolder.newFile().toPath(), content.getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), content.getBytes()).toFile(); GitModifyCommand command = createCommand(); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_withEmptyRepositoryTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_withEmptyRepositoryTest.java index 74989cca8c..e5df94a8ab 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_withEmptyRepositoryTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_withEmptyRepositoryTest.java @@ -43,7 +43,7 @@ public class GitModifyCommand_withEmptyRepositoryTest extends GitModifyCommandTe @Test public void shouldCreateNewFileInEmptyRepository() throws IOException, GitAPIException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); GitModifyCommand command = createCommand(); @@ -124,7 +124,7 @@ public class GitModifyCommand_withEmptyRepositoryTest extends GitModifyCommandTe } private ModifyCommandRequest createRequest() throws IOException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + File newFile = Files.write(tempFolder.newFile().toPath(), "new content".getBytes()).toFile(); ModifyCommandRequest request = new ModifyCommandRequest(); request.setCommitMessage("initial commit");