Auto-fill repository name from URL when left blank
Derive the repository name from the clone URL by extracting the last path segment and stripping the .git suffix. Applied to the web controller, GitService, and telnet interface. The telnet prompt now shows the default name in brackets. Also fix flaky contextLoads test by using RANDOM_PORT. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -52,8 +52,10 @@ public class HomeController
|
||||
}
|
||||
|
||||
@PostMapping("/clone")
|
||||
public String cloneRepo(@RequestParam String url, @RequestParam String name) throws GitAPIException, IOException
|
||||
public String cloneRepo(@RequestParam String url, @RequestParam(required = false) String name) throws GitAPIException, IOException
|
||||
{
|
||||
if (name == null || name.isBlank())
|
||||
name = gitService.deriveRepositoryName(url);
|
||||
gitService.cloneRepository(url, name);
|
||||
return "redirect:/?repo=" + name;
|
||||
}
|
||||
|
||||
@@ -22,8 +22,20 @@ public class GitService
|
||||
{
|
||||
private final WebgitProperties properties;
|
||||
|
||||
public String deriveRepositoryName(String url)
|
||||
{
|
||||
String path = url.replaceAll("[/\\\\]+$", "");
|
||||
int lastSep = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
|
||||
String base = lastSep >= 0 ? path.substring(lastSep + 1) : path;
|
||||
if (base.endsWith(".git"))
|
||||
base = base.substring(0, base.length() - 4);
|
||||
return base;
|
||||
}
|
||||
|
||||
public void cloneRepository(String url, String name) throws GitAPIException, IOException
|
||||
{
|
||||
if (name == null || name.isBlank())
|
||||
name = deriveRepositoryName(url);
|
||||
Path worktree = properties.getWorktreePath().resolve(name);
|
||||
Path gitDir = properties.getGitDirPath().resolve(name);
|
||||
|
||||
|
||||
@@ -88,11 +88,12 @@ public class TelnetSession implements Runnable
|
||||
if (url == null || url.isBlank())
|
||||
return;
|
||||
|
||||
out.print("Name: ");
|
||||
String defaultName = gitService.deriveRepositoryName(url.trim());
|
||||
out.print("Name [" + defaultName + "]: ");
|
||||
out.flush();
|
||||
String name = in.readLine();
|
||||
if (name == null || name.isBlank())
|
||||
return;
|
||||
name = defaultName;
|
||||
|
||||
gitService.cloneRepository(url.trim(), name.trim());
|
||||
out.println("Cloned successfully.");
|
||||
|
||||
@@ -2,8 +2,10 @@ package be.seeseepuff.webgit;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
@SpringBootTest
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@TestPropertySource(properties = "webgit.telnet.enabled=false")
|
||||
class WebgitApplicationTests {
|
||||
|
||||
@Test
|
||||
|
||||
@@ -115,4 +115,31 @@ class HomeControllerTest
|
||||
|
||||
verify(gitService).cloneRepository("https://example.com/repo.git", "myrepo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void cloneWithEmptyNameDerivesFromUrl() throws Exception
|
||||
{
|
||||
when(gitService.deriveRepositoryName("https://example.com/repo.git")).thenReturn("repo");
|
||||
|
||||
mockMvc.perform(post("/clone")
|
||||
.param("url", "https://example.com/repo.git")
|
||||
.param("name", ""))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("/?repo=repo"));
|
||||
|
||||
verify(gitService).cloneRepository("https://example.com/repo.git", "repo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void cloneWithNoNameParamDerivesFromUrl() throws Exception
|
||||
{
|
||||
when(gitService.deriveRepositoryName("https://example.com/project.git")).thenReturn("project");
|
||||
|
||||
mockMvc.perform(post("/clone")
|
||||
.param("url", "https://example.com/project.git"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("/?repo=project"));
|
||||
|
||||
verify(gitService).cloneRepository("https://example.com/project.git", "project");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,4 +250,53 @@ class GitServiceTest
|
||||
|
||||
assertDoesNotThrow(() -> gitService.pull("myrepo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deriveNameFromHttpsUrl()
|
||||
{
|
||||
assertEquals("my-project", gitService.deriveRepositoryName("https://github.com/user/my-project.git"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deriveNameFromUrlWithoutGitSuffix()
|
||||
{
|
||||
assertEquals("repo", gitService.deriveRepositoryName("https://github.com/user/repo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deriveNameFromUrlWithTrailingSlash()
|
||||
{
|
||||
assertEquals("repo", gitService.deriveRepositoryName("https://github.com/user/repo/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deriveNameFromSshUrl()
|
||||
{
|
||||
assertEquals("project", gitService.deriveRepositoryName("git@github.com:user/project.git"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deriveNameFromBareName()
|
||||
{
|
||||
assertEquals("foo", gitService.deriveRepositoryName("foo.git"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void cloneWithBlankNameDerivesFromUrl() throws GitAPIException, IOException
|
||||
{
|
||||
gitService.cloneRepository(bareRemote.toUri().toString(), "");
|
||||
|
||||
String expectedName = gitService.deriveRepositoryName(bareRemote.toUri().toString());
|
||||
assertTrue(Files.exists(worktreePath.resolve(expectedName)));
|
||||
assertTrue(Files.exists(gitDirPath.resolve(expectedName)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void cloneWithNullNameDerivesFromUrl() throws GitAPIException, IOException
|
||||
{
|
||||
gitService.cloneRepository(bareRemote.toUri().toString(), null);
|
||||
|
||||
String expectedName = gitService.deriveRepositoryName(bareRemote.toUri().toString());
|
||||
assertTrue(Files.exists(worktreePath.resolve(expectedName)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,9 @@ class TelnetSessionTest
|
||||
@Test
|
||||
void cloneRepository() throws IOException, GitAPIException
|
||||
{
|
||||
when(gitService.deriveRepositoryName("https://example.com/repo.git")).thenReturn("repo");
|
||||
String output = runSession("2\nhttps://example.com/repo.git\nmyrepo\nq\n");
|
||||
assertTrue(output.contains("Name [repo]:"));
|
||||
assertTrue(output.contains("Cloned successfully."));
|
||||
verify(gitService).cloneRepository("https://example.com/repo.git", "myrepo");
|
||||
}
|
||||
@@ -85,10 +87,13 @@ class TelnetSessionTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void cloneRepositoryEmptyName()
|
||||
void cloneRepositoryEmptyNameUsesDefault() throws IOException, GitAPIException
|
||||
{
|
||||
when(gitService.deriveRepositoryName("https://example.com/repo.git")).thenReturn("repo");
|
||||
String output = runSession("2\nhttps://example.com/repo.git\n\nq\n");
|
||||
assertFalse(output.contains("Cloned successfully."));
|
||||
assertTrue(output.contains("Name [repo]:"));
|
||||
assertTrue(output.contains("Cloned successfully."));
|
||||
verify(gitService).cloneRepository("https://example.com/repo.git", "repo");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -521,10 +526,12 @@ class TelnetSessionTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void cloneNullName()
|
||||
void cloneNullName() throws IOException, GitAPIException
|
||||
{
|
||||
// URL is provided but name input stream ends
|
||||
// URL is provided but name input stream ends — uses default
|
||||
when(gitService.deriveRepositoryName("https://example.com/repo.git")).thenReturn("repo");
|
||||
String output = runSession("2\nhttps://example.com/repo.git\n");
|
||||
assertTrue(output.contains("Goodbye!"));
|
||||
assertTrue(output.contains("Cloned successfully."));
|
||||
verify(gitService).cloneRepository("https://example.com/repo.git", "repo");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user