package de.hftstuttgart.dtabackend.utils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.springframework.stereotype.Component; import org.springframework.util.FileSystemUtils; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.wc.SVNWCUtil; import org.tmatesoft.svn.core.wc2.SvnCheckout; import org.tmatesoft.svn.core.wc2.SvnOperationFactory; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; import java.io.IOException; import java.util.regex.Matcher; @Component public class RepoUtil { private static final Logger LOG = LogManager.getLogger(RepoUtil.class); public RepoUtil() {} public void cloneRepository(Matcher config, String targetPath, String subDir) { if (!config.matches()) { LOG.error("Invalid repository URL format."); return; } String repoUrl = config.group(1); String username = config.group(2); String password = config.group(3); // Not used for SSH authentication LOG.debug("Cloning repository: {}", repoUrl); File targetDirectory = new File(targetPath); if (targetDirectory.exists()) { LOG.debug("Target directory exists, deleting it."); FileSystemUtils.deleteRecursively(targetDirectory); } File checkoutDirectory = targetDirectory; if (!subDir.isEmpty()) { checkoutDirectory = new File(targetPath + "_checkout"); if (checkoutDirectory.exists()) { LOG.debug("Checkout directory exists, deleting it."); FileSystemUtils.deleteRecursively(checkoutDirectory); } } try { LOG.debug("Preparing clone..."); if (repoUrl.endsWith(".git")) { // GIT Repository Clone CloneCommand cloneCommand = Git.cloneRepository() .setDirectory(checkoutDirectory) .setURI(repoUrl); if (!"none".equals(username) && !"none".equals(password)) { LOG.debug("Setting Git credentials."); cloneCommand.setCredentialsProvider(new UsernamePasswordCredentialsProvider(username, password)); } LOG.debug("Cloning Git repository..."); cloneCommand.call().close(); } else { // SVN Repository Clone (including svn+ssh support) SvnOperationFactory operationFactory = new SvnOperationFactory(); try { SVNURL svnUrl = SVNURL.parseURIEncoded(repoUrl); SvnCheckout checkout = operationFactory.createCheckout(); checkout.setSingleTarget(SvnTarget.fromFile(checkoutDirectory)); checkout.setSource(SvnTarget.fromURL(svnUrl)); checkout.setDepth(SVNDepth.INFINITY); // **Use SSH authentication if URL starts with svn+ssh://** if (repoUrl.startsWith("svn+ssh://")) { LOG.debug("Setting up SSH authentication for SVN..."); String sshPrivateKeyPath = "/home/appuser/.ssh/id_rsa"; File privateKeyFile = new File(sshPrivateKeyPath); if (!privateKeyFile.exists()) { LOG.error("SSH private key not found at: {}", sshPrivateKeyPath); throw new SVNException(SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, "SSH key not found")); } ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager( new File("/home/appuser/.ssh/id_rsa"), // Explicit SSH Key username, null, new File("/home/appuser/.ssh/known_hosts"), // Explicit Known Hosts false ); operationFactory.setAuthenticationManager(authManager); } LOG.debug("Performing SVN checkout..."); checkout.run(); } finally { operationFactory.dispose(); } } // Handle subdirectory case if (!subDir.isEmpty()) { File sourceSubDir = new File(checkoutDirectory, subDir); if (sourceSubDir.exists()) { FileSystemUtils.copyRecursively(sourceSubDir, targetDirectory); } else { LOG.error("Specified subdirectory does not exist."); } } LOG.debug("Repository successfully cloned from {} to {}", repoUrl, targetDirectory); } catch (IOException | GitAPIException | SVNException e) { LOG.error("Error while cloning repository: " + repoUrl, e); } } }