Allow filter for Git repositories

The line `allowfilter = true` is inserted both in new Git repositories
and existing ones (via an UpdateStep). This enables clones with
`--filter` parameters.

Co-authored-by: René Pfeuffer<rene.pfeuffer@cloudogu.com>
This commit is contained in:
Till-André Diegeler
2025-01-09 15:27:22 +01:00
parent a4e30b94a2
commit 08e57b9a19
17 changed files with 438 additions and 35 deletions

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2020 - present Cloudogu GmbH
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
package sonia.scm.repository;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
@ExtendWith(MockitoExtension.class)
class GitConfigHelperTest {
@Mock
Repository gitRepository;
StoredConfig gitConfig;
sonia.scm.repository.Repository scmmRepository;
@BeforeEach
void setUp() {
gitConfig = new StoredConfig() {
@Override
public void load() {
// not needed
}
@Override
public void save() {
// not needed
}
};
doReturn(gitConfig).when(gitRepository).getConfig();
scmmRepository = RepositoryTestData.createHeartOfGold();
}
@Test
void shouldSetCorrectScmmRepositoryId() throws IOException {
GitConfigHelper target = new GitConfigHelper();
target.createScmmConfig(scmmRepository, gitRepository);
assertThat(gitConfig.getString("scmm", null, "repositoryid")).isEqualTo(scmmRepository.getId());
}
@Test
void shouldAllowUploadpackFilter() throws IOException {
GitConfigHelper target = new GitConfigHelper();
target.createScmmConfig(scmmRepository, gitRepository);
assertThat(gitConfig.getBoolean("uploadpack", "allowFilter", false)).isTrue();
}
}

View File

@@ -23,8 +23,12 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.schedule.Scheduler;
import sonia.scm.store.ConfigurationStoreFactory;
import sonia.scm.store.InMemoryByteConfigurationStoreFactory;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
@@ -79,7 +83,6 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
GitConfig config = new GitConfig();
// TODO fix event bus exception
repositoryHandler.setConfig(config);
return repositoryHandler;
@@ -116,4 +119,28 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
assertThat(new File(nativeRepoDirectory, "HEAD")).hasContent("ref: refs/heads/other");
}
@Test
public void shouldSetAllowFilterConfigByDefault() throws Exception{
ConfigurationStoreFactory configurationStoreFactory = new InMemoryByteConfigurationStoreFactory();
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(configurationStoreFactory,
scheduler, repositoryLocationResolver, gitWorkingCopyFactory,null);
GitConfig config = new GitConfig();
repositoryHandler.setConfig(config);
repositoryHandler.create(RepositoryTestData.createHeartOfGold("git"));
Path repositoryPath = repositoryLocationResolver.forClass(Path.class).getLocation("");
File configFile = repositoryPath.resolve("data/config").toFile();
boolean containsAllowFilter = false;
try (BufferedReader br = new BufferedReader(new FileReader(configFile.getAbsolutePath()))) {
do {
String line = br.readLine();
containsAllowFilter |= line.contains("allowFilter") && line.contains("true");
} while (br.readLine() != null);
}
assertTrue(containsAllowFilter);
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 2020 - present Cloudogu GmbH
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
package sonia.scm.repository.update;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.TempDirRepositoryLocationResolver;
import sonia.scm.migration.RepositoryUpdateContext;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.repository.RepositoryTestData;
import sonia.scm.update.UpdateStepRepositoryMetadataAccess;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.io.CleanupMode.ALWAYS;
@ExtendWith(MockitoExtension.class)
class UpdatePackFilterUpdateStepTest {
RepositoryLocationResolver repositoryLocationResolver;
UpdateStepRepositoryMetadataAccess<Path> updateStepRepositoryMetadataAccess;
@Nested
class DoUpdate {
@TempDir(cleanup = ALWAYS)
Path tempDir;
UpdatePackFilterUpdateStep target;
private static final String EXAMPLE_REPOSITORY_ID = "3ZUZMNJn3E";
@BeforeEach
void setUp() throws IOException {
String sourcePath = "src/test/resources/scm-home/repositories/exampleGitRepoWithoutFilterUpdate";
loadFilesIntoTempDir(tempDir, sourcePath);
repositoryLocationResolver = new TempDirRepositoryLocationResolver(tempDir.toFile());
updateStepRepositoryMetadataAccess = location -> {
Repository repository = RepositoryTestData.createRestaurantAtTheEndOfTheUniverse("git");
repository.setId(EXAMPLE_REPOSITORY_ID);
return repository;
};
}
@Test
void shouldWriteAllowFilterLineWithinConfig() throws Exception {
target = new UpdatePackFilterUpdateStep(repositoryLocationResolver, updateStepRepositoryMetadataAccess);
target.doUpdate(new RepositoryUpdateContext(EXAMPLE_REPOSITORY_ID));
File configFile = tempDir.resolve("data/config").toFile();
boolean containsAllowFilter = false;
try (BufferedReader br = new BufferedReader(new FileReader(configFile.getAbsolutePath()))) {
do {
String line = br.readLine();
containsAllowFilter |= line.contains("allowFilter") && line.contains("true");
} while (br.readLine() != null);
}
assertTrue(containsAllowFilter);
}
}
private void loadFilesIntoTempDir(Path tempDir, String sourcePath) throws IOException {
File repositorySourcePath = new File(sourcePath);
try (Stream<Path> sources = Files.walk(repositorySourcePath.toPath())) {
sources.forEach(source -> {
Path destination = Paths.get(tempDir.toString(), source.toString().substring(repositorySourcePath.toString().length()));
if (!destination.toFile().exists()) {
try {
Files.copy(source, destination);
} catch (IOException e) {
fail("An exception occurred during temporary repository file setup.", e);
}
}
});
}
}
}

View File

@@ -0,0 +1,7 @@
[core]
repositoryformatversion = 0
filemode = true
bare = true
logallrefupdates = false
[scmm]
repositoryid = 3ZUZMNJn3E

View File

@@ -0,0 +1 @@
43932c31dda188da38d96682d50df14a9407579e

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
Copyright (c) 2020 - present Cloudogu GmbH
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation, version 3.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see https://www.gnu.org/licenses/.
-->
<repositories>
<properties/>
<id>3ZUZMNJn3E</id>
<namespace>test</namespace>
<name>exampleGitRepoWithoutFilterUpdate</name>
<type>git</type>
<description></description>
<contact></contact>
<creationDate>1736402353654</creationDate>
<lastModified>1736402353753</lastModified>
<permission>
<groupPermission>false</groupPermission>
<name>scmadmin</name>
<role>OWNER</role>
</permission>
<archived>false</archived>
</repositories>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" ?>
<!--
Copyright (c) 2020 - present Cloudogu GmbH
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation, version 3.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see https://www.gnu.org/licenses/.
-->
<configuration type="config-entry">
<entry>
<key>sonia.scm.dao.xml</key>
<value>
<latestVersion>2.0.0</latestVersion>
</value>
</entry>
</configuration>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" ?>
<!--
Copyright (c) 2020 - present Cloudogu GmbH
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation, version 3.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see https://www.gnu.org/licenses/.
-->
<config>
<defaultBranch>main</defaultBranch>
<nonFastForwardDisallowed>false</nonFastForwardDisallowed>
</config>