Allow to configure hideRefs for extra refs prefix to hide/show Introduce the 'git-refs-filter' configuration settings in gerrit.config which allows to specify `hideRefs' configs similarly to what Git does, see [1] [1] https://git-scm.com/docs/git-config/2.17.0#Documentation/git-config.txt-receivehideRefs Change-Id: Ib6a703ecf2c977b49689743826b95b8b985cf734
diff --git a/README.md b/README.md index debe9c9..c172399 100644 --- a/README.md +++ b/README.md
@@ -69,6 +69,21 @@ - All '/meta' refs of all changes - All non-published edits of any changes +It is also possible to define additional refs prefixes to be hidden or explicitly shown, +using a similar syntax to the [hideRefs](https://git-scm.com/docs/git-config/2.17.0#Documentation/git-config.txt-receivehideRefs) +setting, adding a set of `git-refs-filter.hideRefs` configuration settings in +`gerrit.config`. + +Example of how to hide all `refs/backup/*` and `refs/sandbox/*` from being advertised +but still show `refs/sandbox/mines/`: + +```` +[git-refs-filter] + hideRefs = refs/backup/ + hideRefs = refs/sandbox/ + hideRefs = !refs/sandbox/mine/ +``` + To enable a group of users of getting a "filtered list" of refs (e.g. CI jobs): - Define a new group of users (e.g. Builders) - Add a user to that group (e.g. Add 'jenkins' to the Builders group)
diff --git a/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/FilterRefsConfig.java b/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/FilterRefsConfig.java new file mode 100644 index 0000000..2524f5e --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/FilterRefsConfig.java
@@ -0,0 +1,71 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.modules.gitrefsfilter; + +import com.google.common.collect.ImmutableList; +import com.google.gerrit.server.config.GerritServerConfig; +import com.google.inject.Inject; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.Ref; + +public class FilterRefsConfig { + public static final String SECTION_GIT_REFS_FILTER = "git-refs-filter"; + public static final String KEY_HIDE_REFS = "hideRefs"; + + private final List<String> hideRefs; + private final List<String> showRefs; + + @Inject + public FilterRefsConfig(@GerritServerConfig Config gerritConfig) { + + List<String> hideRefsConfig = + Arrays.asList(gerritConfig.getStringList(SECTION_GIT_REFS_FILTER, null, KEY_HIDE_REFS)); + + hideRefs = + ImmutableList.copyOf( + hideRefsConfig.stream() + .filter(s -> !s.startsWith("!")) + .map(String::trim) + .collect(Collectors.toList())); + showRefs = + ImmutableList.copyOf( + hideRefsConfig.stream() + .filter(s -> s.startsWith("!")) + .map(s -> s.substring(1)) + .map(String::trim) + .collect(Collectors.toList())); + } + + public boolean isRefToShow(Ref ref) { + String refName = ref.getName(); + + for (String refToShow : showRefs) { + if (refName.startsWith(refToShow)) { + return true; + } + } + + for (String refToHide : hideRefs) { + if (refName.startsWith(refToHide)) { + return false; + } + } + + return true; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/ForProjectWrapper.java b/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/ForProjectWrapper.java index b477670..0809840 100644 --- a/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/ForProjectWrapper.java +++ b/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/ForProjectWrapper.java
@@ -41,6 +41,7 @@ private final ForProject defaultForProject; private final Project.NameKey project; private final ChangeNotes.Factory changeNotesFactory; + private final FilterRefsConfig config; public interface Factory { ForProjectWrapper get(ForProject defaultForProject, Project.NameKey project); @@ -49,11 +50,13 @@ @Inject public ForProjectWrapper( ChangeNotes.Factory changeNotesFactory, + FilterRefsConfig config, @Assisted ForProject defaultForProject, @Assisted Project.NameKey project) { this.defaultForProject = defaultForProject; this.project = project; this.changeNotesFactory = changeNotesFactory; + this.config = config; } @Override @@ -79,8 +82,9 @@ return defaultForProject .filter(refs, repo, opts) .parallelStream() - .filter((ref) -> !ref.getName().startsWith(RefNames.REFS_USERS)) - .filter((ref) -> !ref.getName().startsWith(RefNames.REFS_CACHE_AUTOMERGE)) + .filter(ref -> !ref.getName().startsWith(RefNames.REFS_USERS)) + .filter(ref -> !ref.getName().startsWith(RefNames.REFS_CACHE_AUTOMERGE)) + .filter(config::isRefToShow) .filter( (ref) -> { String refName = ref.getName();
diff --git a/src/test/java/com/googlesource/gerrit/libmodule/plugins/test/AbstractGitDaemonTest.java b/src/test/java/com/googlesource/gerrit/libmodule/plugins/test/AbstractGitDaemonTest.java index 8b1bd0b..ea1d61e 100644 --- a/src/test/java/com/googlesource/gerrit/libmodule/plugins/test/AbstractGitDaemonTest.java +++ b/src/test/java/com/googlesource/gerrit/libmodule/plugins/test/AbstractGitDaemonTest.java
@@ -76,6 +76,11 @@ protected TestRepository<InMemoryRepository> cloneProjectChangesRefs(TestAccount testAccount) throws Exception { + return cloneProjectRefs(testAccount, REFS_CHANGES); + } + + protected TestRepository<InMemoryRepository> cloneProjectRefs( + TestAccount testAccount, String refsSpec) throws Exception { DfsRepositoryDescription desc = new DfsRepositoryDescription("clone of " + project.get()); FS fs = FS.detect(); @@ -94,7 +99,7 @@ Config cfg = dest.getConfig(); String uri = registerRepoConnection(project, testAccount); cfg.setString("remote", "origin", "url", uri); - cfg.setString("remote", "origin", "fetch", REFS_CHANGES); + cfg.setString("remote", "origin", "fetch", refsSpec); TestRepository<InMemoryRepository> testRepo = GitUtil.newTestRepository(dest); FetchResult result = testRepo.git().fetch().setRemote("origin").call(); String originMaster = "refs/remotes/origin/master";
diff --git a/src/test/java/com/googlesource/gerrit/libmodule/plugins/test/GitRefsFilterTest.java b/src/test/java/com/googlesource/gerrit/libmodule/plugins/test/GitRefsFilterTest.java index d9d458b..957d4f7 100644 --- a/src/test/java/com/googlesource/gerrit/libmodule/plugins/test/GitRefsFilterTest.java +++ b/src/test/java/com/googlesource/gerrit/libmodule/plugins/test/GitRefsFilterTest.java
@@ -20,9 +20,15 @@ import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.Sandboxed; import com.google.gerrit.acceptance.TestAccount; +import com.google.gerrit.acceptance.config.GerritConfig; +import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; +import com.google.gerrit.entities.BranchNameKey; import com.google.gerrit.entities.RefNames; +import com.google.inject.Inject; import com.google.inject.Module; import com.googlesource.gerrit.modules.gitrefsfilter.RefsFilterModule; +import java.io.IOException; +import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; @@ -38,6 +44,7 @@ @NoHttpd @Sandboxed public class GitRefsFilterTest extends AbstractGitDaemonTest { + @Inject private RequestScopeOperations requestScopeOperations; @Override public Module createModule() { @@ -83,6 +90,32 @@ assertThat(getRefs(cloneProjectChangesRefs(admin))).isNotEmpty(); } + @Test + @GerritConfig(name = "git-refs-filter.hideRefs", value = "refs/heads/sandbox/") + public void testUserWithHideRefsShouldNotSeeSandboxBranches() throws Exception { + String sandboxPrefix = "refs/heads/sandbox/"; + requestScopeOperations.setApiUser(admin.id()); + createBranch(BranchNameKey.create(project, "sandbox/foo")); + + assertThat(getRefs(cloneProjectRefs(admin, "+refs/heads/*:refs/heads/*"), sandboxPrefix)) + .isNotEmpty(); + assertThat(getRefs(cloneProjectRefs(user, "+refs/heads/*:refs/heads/*"), sandboxPrefix)) + .isEmpty(); + } + + @Test + @GerritConfig( + name = "git-refs-filter.hideRefs", + values = {"refs/heads/sandbox/", "!refs/heads/sandbox/mine"}) + public void testUserWithHideRefsShouldSeeItsOwnSandboxBranch() throws Exception { + String sandboxPrefix = "refs/heads/sandbox/"; + requestScopeOperations.setApiUser(admin.id()); + createBranch(BranchNameKey.create(project, "sandbox/mine")); + + assertThat(getRefs(cloneProjectRefs(user, "+refs/heads/*:refs/heads/*"), sandboxPrefix)) + .isNotEmpty(); + } + protected Stream<String> fetchAllRefs(TestAccount testAccount) throws Exception { DfsRepositoryDescription desc = new DfsRepositoryDescription("clone of " + project.get()); @@ -99,4 +132,9 @@ FetchResult result = testRepo.git().fetch().setRemote("origin").call(); return result.getAdvertisedRefs().stream().map(Ref::getName); } + + protected List<Ref> getRefs(TestRepository<InMemoryRepository> repo, String prefix) + throws IOException { + return repo.getRepository().getRefDatabase().getRefsByPrefix(prefix); + } }