diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0837a57891cf..8d07af38982d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -115,9 +115,9 @@ jobs: if: ${{ github.event_name == 'release' }} uses: actions/checkout@v4 - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 # Build and Push to GitHub Container Registry - name: Login to GitHub Container Registry uses: docker/login-action@v2 diff --git a/build.gradle b/build.gradle index 783f8b2e412a..be4f411ef905 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ plugins { } group = "de.tum.in.www1.artemis" -version = "6.4.3" +version = "6.5.0" description = "Interactive Learning with Individual Feedback" sourceCompatibility=17 diff --git a/package-lock.json b/package-lock.json index 426607e0d086..6ce1f04ecd2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "artemis", - "version": "6.4.3", + "version": "6.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "artemis", - "version": "6.4.3", + "version": "6.5.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c741d72fd202..33c99708233e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "artemis", - "version": "6.4.3", + "version": "6.5.0", "description": "Interactive Learning with Individual Feedback", "private": true, "license": "MIT", diff --git a/src/main/java/de/tum/in/www1/artemis/aop/logging/LoggingAspect.java b/src/main/java/de/tum/in/www1/artemis/aop/logging/LoggingAspect.java index 1fe5d3b127fb..b78cc86e65cb 100644 --- a/src/main/java/de/tum/in/www1/artemis/aop/logging/LoggingAspect.java +++ b/src/main/java/de/tum/in/www1/artemis/aop/logging/LoggingAspect.java @@ -10,6 +10,7 @@ import org.springframework.core.env.Environment; import org.springframework.core.env.Profiles; +import de.tum.in.www1.artemis.service.connectors.vcs.AbstractVersionControlService; import tech.jhipster.config.JHipsterConstants; /** @@ -53,10 +54,14 @@ public void applicationPackagePointcut() { */ @AfterThrowing(pointcut = "applicationPackagePointcut() && springBeanPointcut()", throwing = "e") public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { + if (AbstractVersionControlService.isReadFullyShortReadOfBlockException(e)) { + // ignore + return; + } + if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT))) { log.error("Exception in {}.{}() with cause = \'{}\' and exception = \'{}\'", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), e.getCause() != null ? e.getCause() : "NULL", e.getMessage(), e); - } else { log.error("Exception in {}.{}() with cause = {}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), @@ -86,7 +91,6 @@ public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { } catch (IllegalArgumentException e) { log.error("Illegal argument: {} in {}.{}()", Arrays.toString(joinPoint.getArgs()), joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); - throw e; } } diff --git a/src/main/java/de/tum/in/www1/artemis/domain/UserGroup.java b/src/main/java/de/tum/in/www1/artemis/domain/UserGroup.java index 091be72ed18e..ebb09576e010 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/UserGroup.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/UserGroup.java @@ -1,6 +1,7 @@ package de.tum.in.www1.artemis.domain; import java.io.Serializable; +import java.util.Objects; import javax.persistence.*; @@ -18,12 +19,36 @@ public class UserGroup { private String group; @Embeddable - public class UserGroupKey implements Serializable { + public static class UserGroupKey implements Serializable { @Column(name = "user_id") private Long userId; @Column(name = "`groups`") private String group; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + UserGroupKey that = (UserGroupKey) o; + + if (!Objects.equals(userId, that.userId)) { + return false; + } + return Objects.equals(group, that.group); + } + + @Override + public int hashCode() { + int result = userId != null ? userId.hashCode() : 0; + result = 31 * result + (group != null ? group.hashCode() : 0); + return result; + } } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/FileService.java b/src/main/java/de/tum/in/www1/artemis/service/FileService.java index 9e7ff41a1af9..84877534deb6 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/FileService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/FileService.java @@ -1,11 +1,11 @@ package de.tum.in.www1.artemis.service; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import java.io.*; import java.net.URLDecoder; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -59,6 +59,13 @@ public class FileService implements DisposableBean { private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors()); + /** + * A list of common binary file extensions. + * Extensions must be lower-case without leading dots. + */ + private static final Set binaryFileExtensions = Set.of("png", "jpg", "jpeg", "heic", "gif", "tiff", "psd", "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "pages", + "numbers", "key", "odt", "zip", "rar", "7z", "tar", "iso", "mdb", "sqlite", "exe", "jar"); + /** * The list of file extensions that are allowed to be uploaded in a Markdown editor. * Extensions must be lower-case without leading dots. @@ -631,7 +638,7 @@ private Path getTargetPath(final Resource resource, final Path prefix, final Pat filePath = resource.getFile().toPath(); } else { - final String url = URLDecoder.decode(resource.getURL().toString(), StandardCharsets.UTF_8); + final String url = URLDecoder.decode(resource.getURL().toString(), UTF_8); filePath = Path.of(url); } @@ -738,7 +745,7 @@ public void replacePlaceholderSections(String filePath, Map sec throw new FilePathParsingException("File " + filePath + " should be updated but does not exist."); } - try (var reader = new BufferedReader(new FileReader(file, StandardCharsets.UTF_8)); var writer = new BufferedWriter(new FileWriter(tempFile, StandardCharsets.UTF_8))) { + try (var reader = new BufferedReader(new FileReader(file, UTF_8)); var writer = new BufferedWriter(new FileWriter(tempFile, UTF_8))) { Map.Entry matchingStartPattern = null; String line = reader.readLine(); while (line != null) { @@ -856,29 +863,27 @@ public void replaceVariablesInFileName(String startPath, String targetString, St /** * This replaces all occurrences of the target Strings with the replacement Strings in the given file and saves the file *

- * {@link #replaceVariablesInFile(String, Map) replaceVariablesInFile} + * {@link #replaceVariablesInFile(Path, Map) replaceVariablesInFile} * * @param startPath the path where the start directory is located * @param replacements the replacements that should be applied - * @throws IOException if an issue occurs on file access for the replacement of the variables. */ - public void replaceVariablesInFileRecursive(String startPath, Map replacements) throws IOException { + public void replaceVariablesInFileRecursive(Path startPath, Map replacements) { replaceVariablesInFileRecursive(startPath, replacements, Collections.emptyList()); } /** * This replaces all occurrences of the target Strings with the replacement Strings in the given file and saves the file *

- * {@link #replaceVariablesInFile(String, Map) replaceVariablesInFile} + * {@link #replaceVariablesInFile(Path, Map) replaceVariablesInFile} * * @param startPath the path where the start directory is located * @param replacements the replacements that should be applied * @param filesToIgnore the name of files for which no replacement should be done - * @throws IOException if an issue occurs on file access for the replacement of the variables. */ - public void replaceVariablesInFileRecursive(String startPath, Map replacements, List filesToIgnore) throws IOException { + public void replaceVariablesInFileRecursive(Path startPath, Map replacements, List filesToIgnore) { log.debug("Replacing {} in files in directory {}", replacements, startPath); - File directory = new File(startPath); + File directory = startPath.toFile(); if (!directory.exists() || !directory.isDirectory()) { throw new RuntimeException("Files in directory " + startPath + " should be replaced but the directory does not exist."); } @@ -889,7 +894,7 @@ public void replaceVariablesInFileRecursive(String startPath, Map replacements) throws IOException { + public void replaceVariablesInFile(Path filePath, Map replacements) { log.debug("Replacing {} in file {}", replacements, filePath); - // https://stackoverflow.com/questions/3935791/find-and-replace-words-lines-in-a-file - Path replaceFilePath = Path.of(filePath); - Charset charset = StandardCharsets.UTF_8; - String fileContent = Files.readString(replaceFilePath, charset); - for (Map.Entry replacement : replacements.entrySet()) { - fileContent = fileContent.replace(replacement.getKey(), replacement.getValue()); + if (isBinaryFile(filePath)) { + // do not try to read binary files with 'readString' + return; + } + try { + // Note: Java does not offer a good way to check if a file is binary or not. If the basic check above fails (e.g. due to a custom binary file from an instructor), + // but the file is still binary, we try to read it. In case the method readString fails, we only log this below, but continue, because the exception should NOT + // interrupt the ongoing process + String fileContent = Files.readString(filePath, UTF_8); + for (Map.Entry replacement : replacements.entrySet()) { + fileContent = fileContent.replace(replacement.getKey(), replacement.getValue()); + } + Files.writeString(filePath, fileContent, UTF_8); } - Files.writeString(replaceFilePath, fileContent, charset); + catch (IOException ex) { + log.warn("Exception {} occurred when trying to replace {} in (binary) file {}", ex.getMessage(), replacements, filePath); + // continue + } + } + + /** + * very simple and non-exhaustive check for the most common binary files such as images + * Unfortunately, Java cannot determine this correctly, so we need to provide typical file endings here + * + * @param filePath the path of the file + * @return whether the simple check for file endings determines the underlying file to be binary (true) or not (false) + */ + private static boolean isBinaryFile(Path filePath) { + final String fileExtension = FilenameUtils.getExtension(filePath.getFileName().toString()); + return binaryFileExtensions.stream().anyMatch(fileExtension::equalsIgnoreCase); } /** * This normalizes all line endings to UNIX-line-endings recursively from the startPath. *

- * {@link #normalizeLineEndings(String) normalizeLineEndings} + * {@link #normalizeLineEndings(Path) normalizeLineEndings} * * @param startPath the path where the start directory is located * @throws IOException if an issue occurs on file access for the normalizing of the line endings. */ - public void normalizeLineEndingsDirectory(String startPath) throws IOException { + public void normalizeLineEndingsDirectory(Path startPath) throws IOException { log.debug("Normalizing file endings in directory {}", startPath); - File directory = new File(startPath); + File directory = startPath.toFile(); if (!directory.exists() || !directory.isDirectory()) { throw new RuntimeException("File endings in directory " + startPath + " should be normalized but the directory does not exist."); } @@ -948,7 +974,7 @@ public void normalizeLineEndingsDirectory(String startPath) throws IOException { Collection files = FileUtils.listFiles(directory, FileFilterUtils.trueFileFilter(), directoryFileFilter); for (File file : files) { - normalizeLineEndings(file.getAbsolutePath()); + normalizeLineEndings(file.toPath()); } } @@ -960,28 +986,29 @@ public void normalizeLineEndingsDirectory(String startPath) throws IOException { * @param filePath the path where the file is located * @throws IOException if an issue occurs on file access for the normalizing of the line endings. */ - public void normalizeLineEndings(String filePath) throws IOException { + public void normalizeLineEndings(Path filePath) throws IOException { log.debug("Normalizing line endings in file {}", filePath); + if (isBinaryFile(filePath)) { + // do not try to read binary files with 'readString' + return; + } // https://stackoverflow.com/questions/3776923/how-can-i-normalize-the-eol-character-in-java - Path replaceFilePath = Path.of(filePath); - Charset charset = StandardCharsets.UTF_8; - - String fileContent = Files.readString(replaceFilePath, charset); + String fileContent = Files.readString(filePath, UTF_8); fileContent = fileContent.replaceAll("\\r\\n?", "\n"); - Files.writeString(replaceFilePath, fileContent, charset); + Files.writeString(filePath, fileContent, UTF_8); } /** * This converts all files to the UTF-8 encoding recursively from the startPath. *

- * {@link #convertToUTF8(String) convertToUTF8} + * {@link #convertToUTF8(Path) convertToUTF8} * * @param startPath the path where the start directory is located * @throws IOException if an issue occurs on file access when converting to UTF-8. */ - public void convertToUTF8Directory(String startPath) throws IOException { + public void convertToUTF8Directory(Path startPath) throws IOException { log.debug("Converting files in directory {} to UTF-8", startPath); - File directory = new File(startPath); + File directory = startPath.toFile(); if (!directory.exists() || !directory.isDirectory()) { throw new RuntimeException("Files in directory " + startPath + " should be converted to UTF-8 but the directory does not exist."); } @@ -992,7 +1019,7 @@ public void convertToUTF8Directory(String startPath) throws IOException { Collection files = FileUtils.listFiles(directory, FileFilterUtils.trueFileFilter(), directoryFileFilter); for (File file : files) { - convertToUTF8(file.getAbsolutePath()); + convertToUTF8(file.toPath()); } } @@ -1003,17 +1030,16 @@ public void convertToUTF8Directory(String startPath) throws IOException { * @param filePath the path where the file is located * @throws IOException if an issue occurs on file access when converting to UTF-8. */ - public void convertToUTF8(String filePath) throws IOException { + public void convertToUTF8(Path filePath) throws IOException { log.debug("Converting file {} to UTF-8", filePath); - Path replaceFilePath = Path.of(filePath); - byte[] contentArray = Files.readAllBytes(replaceFilePath); + byte[] contentArray = Files.readAllBytes(filePath); Charset charset = detectCharset(contentArray); log.debug("Detected charset for file {} is {}", filePath, charset.name()); String fileContent = new String(contentArray, charset); - Files.writeString(replaceFilePath, fileContent, StandardCharsets.UTF_8); + Files.writeString(filePath, fileContent, UTF_8); } /** @@ -1132,7 +1158,7 @@ public void createDirectory(Path path) { * @return Path to the written file */ public Path writeStringToFile(String stringToWrite, Path path) { - try (var outStream = new OutputStreamWriter(new FileOutputStream(path.toString()), StandardCharsets.UTF_8)) { + try (var outStream = new OutputStreamWriter(new FileOutputStream(path.toString()), UTF_8)) { outStream.write(stringToWrite); } catch (IOException e) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/vcs/AbstractVersionControlService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/vcs/AbstractVersionControlService.java index e4810c43451d..8523a53018e6 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/vcs/AbstractVersionControlService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/vcs/AbstractVersionControlService.java @@ -1,12 +1,12 @@ package de.tum.in.www1.artemis.service.connectors.vcs; -import static de.tum.in.www1.artemis.config.Constants.*; - import java.io.IOException; import java.nio.file.Path; +import java.util.Objects; import org.apache.commons.io.FileUtils; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.internal.JGitText; import org.hibernate.Hibernate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -123,8 +123,15 @@ public VcsRepositoryUrl copyRepository(String sourceProjectKey, String sourceRep // copy by pushing the source's content to the target's repo gitService.pushSourceToTargetRepo(targetRepo, targetRepoUrl, sourceBranch); } - catch (GitAPIException | VersionControlException e) { + catch (GitAPIException | VersionControlException ex) { + if (isReadFullyShortReadOfBlockException(ex)) { + // NOTE: we ignore this particular error: it sometimes happens when pushing code that includes binary files, however the push operation typically worked correctly + // TODO: verify that the push operation actually worked correctly, e.g. by comparing the number of commits in the source and target repo + log.warn("TransportException/EOFException with 'Short read of block' when copying repository {} to {}. Will ignore it", sourceRepoUrl, targetRepoUrl); + return targetRepoUrl; + } Path localPath = gitService.getDefaultLocalPathOfRepo(targetRepoUrl); + // clean up in case of an error try { if (targetRepo != null) { // delete the target repo if an error occurs @@ -135,16 +142,30 @@ public VcsRepositoryUrl copyRepository(String sourceProjectKey, String sourceRep FileUtils.deleteDirectory(localPath.toFile()); } } - catch (IOException ex) { + catch (IOException ioException) { // ignore log.error("Could not delete directory of the failed cloned repository in: {}", localPath); } - throw new VersionControlException("Could not copy repository " + sourceRepositoryName + " to the target repository " + targetRepositoryName, e); + throw new VersionControlException("Could not copy repository " + sourceRepositoryName + " to the target repository " + targetRepositoryName, ex); } return targetRepoUrl; } + /** + * checks for a specific exception that we would like to ignore + * + * @param ex the exception + * @return whether we found the specific one or not + */ + public static boolean isReadFullyShortReadOfBlockException(Throwable ex) { + return ex instanceof org.eclipse.jgit.api.errors.TransportException transportException + && transportException.getCause() instanceof org.eclipse.jgit.errors.TransportException innerTransportException + && innerTransportException.getCause() instanceof java.io.EOFException eofException && eofException.getMessage().equals(JGitText.get().shortReadOfBlock) + && Objects.equals(eofException.getStackTrace()[0].getClassName(), "org.eclipse.jgit.util.IO") + && Objects.equals(eofException.getStackTrace()[0].getMethodName(), "readFully"); + } + @Override public String getOrRetrieveBranchOfParticipation(ProgrammingExerciseParticipation participation) { if (participation instanceof ProgrammingExerciseStudentParticipation studentParticipation) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseExportService.java b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseExportService.java index 74326f490bc0..ac7bed156470 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseExportService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseExportService.java @@ -670,8 +670,8 @@ private Path createZipForRepositoryWithParticipation(final ProgrammingExercise p if (repositoryExportOptions.isNormalizeCodeStyle()) { try { log.debug("Normalizing code style for participation {}", participation); - fileService.normalizeLineEndingsDirectory(repository.getLocalPath().toString()); - fileService.convertToUTF8Directory(repository.getLocalPath().toString()); + fileService.normalizeLineEndingsDirectory(repository.getLocalPath()); + fileService.convertToUTF8Directory(repository.getLocalPath()); } catch (IOException ex) { log.warn("Cannot normalize code style in the repository {} due to the following exception: {}", repository.getLocalPath(), ex.getMessage()); diff --git a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportFromFileService.java b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportFromFileService.java index 2151f85aff20..61dce26dc239 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportFromFileService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportFromFileService.java @@ -78,6 +78,7 @@ public ProgrammingExercise importProgrammingExerciseFromFile(ProgrammingExercise checkRepositoriesExist(importExerciseDir); var oldShortName = getProgrammingExerciseFromDetailsFile(importExerciseDir).getShortName(); programmingExerciseService.validateNewProgrammingExerciseSettings(programmingExerciseForImport, course); + // TODO: creating the whole exercise (from template) is a bad solution in this case, we do not want the template content, instead we want the file content of the zip importedProgrammingExercise = programmingExerciseService.createProgrammingExercise(programmingExerciseForImport); if (Boolean.TRUE.equals(programmingExerciseForImport.isStaticCodeAnalysisEnabled())) { staticCodeAnalysisService.createDefaultCategories(importedProgrammingExercise); @@ -131,9 +132,9 @@ private void importRepositoriesFromFile(ProgrammingExercise newExercise, Path ba gitService.commitAndPush(testRepo, "Import tests from file", true, null); } - private void replaceImportedExerciseShortName(Map replacements, Repository... repositories) throws IOException { + private void replaceImportedExerciseShortName(Map replacements, Repository... repositories) { for (Repository repository : repositories) { - fileService.replaceVariablesInFileRecursive(repository.getLocalPath().toString(), replacements, SHORT_NAME_REPLACEMENT_EXCLUSIONS); + fileService.replaceVariablesInFileRecursive(repository.getLocalPath(), replacements, SHORT_NAME_REPLACEMENT_EXCLUSIONS); } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportService.java b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportService.java index 18484590eded..f3a347493af8 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportService.java @@ -95,18 +95,18 @@ public void importRepositories(final ProgrammingExercise templateExercise, final String sourceBranch = versionControl.getOrRetrieveBranchOfExercise(templateExercise); + // TODO: in case one of those operations fail, we should do error handling and revert all previous operations versionControl.copyRepository(sourceProjectKey, templateRepoName, sourceBranch, targetProjectKey, RepositoryType.TEMPLATE.getName()); versionControl.copyRepository(sourceProjectKey, solutionRepoName, sourceBranch, targetProjectKey, RepositoryType.SOLUTION.getName()); versionControl.copyRepository(sourceProjectKey, testRepoName, sourceBranch, targetProjectKey, RepositoryType.TESTS.getName()); - List auxiliaryRepositories = templateExercise.getAuxiliaryRepositories(); - for (int i = 0; i < auxiliaryRepositories.size(); i++) { - AuxiliaryRepository auxiliaryRepository = auxiliaryRepositories.get(i); - String repositoryUrl = versionControl - .copyRepository(sourceProjectKey, auxiliaryRepository.getRepositoryName(), sourceBranch, targetProjectKey, auxiliaryRepository.getName()).toString(); - AuxiliaryRepository newAuxiliaryRepository = newExercise.getAuxiliaryRepositories().get(i); - newAuxiliaryRepository.setRepositoryUrl(repositoryUrl); - auxiliaryRepositoryRepository.save(newAuxiliaryRepository); + List auxRepos = templateExercise.getAuxiliaryRepositories(); + for (int i = 0; i < auxRepos.size(); i++) { + AuxiliaryRepository auxRepo = auxRepos.get(i); + var repoUrl = versionControl.copyRepository(sourceProjectKey, auxRepo.getRepositoryName(), sourceBranch, targetProjectKey, auxRepo.getName()).toString(); + AuxiliaryRepository newAuxRepo = newExercise.getAuxiliaryRepositories().get(i); + newAuxRepo.setRepositoryUrl(repoUrl); + auxiliaryRepositoryRepository.save(newAuxRepo); } // Unprotect the default branch of the template exercise repo. @@ -255,12 +255,11 @@ private void adjustProjectNames(ProgrammingExercise templateExercise, Programmin * @param repositoryName the name of the repository that should be adjusted * @param user the user which performed the action (used as Git author) * @throws GitAPIException If the checkout/push of one repository fails - * @throws IOException If the values in the files could not be replaced */ - private void adjustProjectName(Map replacements, String projectKey, String repositoryName, User user) throws GitAPIException, IOException { + private void adjustProjectName(Map replacements, String projectKey, String repositoryName, User user) throws GitAPIException { final var repositoryUrl = versionControlService.orElseThrow().getCloneRepositoryUrl(projectKey, repositoryName); Repository repository = gitService.getOrCheckoutRepository(repositoryUrl, true); - fileService.replaceVariablesInFileRecursive(repository.getLocalPath().toAbsolutePath().toString(), replacements, List.of("gradle-wrapper.jar")); + fileService.replaceVariablesInFileRecursive(repository.getLocalPath().toAbsolutePath(), replacements, List.of("gradle-wrapper.jar")); gitService.stageAllChanges(repository); gitService.commitAndPush(repository, "Template adjusted by Artemis", true, user); repository.setFiles(null); // Clear cache to avoid multiple commits when Artemis server is not restarted between attempts diff --git a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseRepositoryService.java b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseRepositoryService.java index 4bc551968cbc..688cacd177a0 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseRepositoryService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseRepositoryService.java @@ -580,7 +580,7 @@ void replacePlaceholders(final ProgrammingExercise programmingExercise, final Re replacements.put("${exerciseName}", programmingExercise.getTitle()); replacements.put("${studentWorkingDirectory}", Constants.STUDENT_WORKING_DIRECTORY); replacements.put("${packaging}", programmingExercise.hasSequentialTestRuns() ? "pom" : "jar"); - fileService.replaceVariablesInFileRecursive(repository.getLocalPath().toAbsolutePath().toString(), replacements, List.of("gradle-wrapper.jar")); + fileService.replaceVariablesInFileRecursive(repository.getLocalPath().toAbsolutePath(), replacements, List.of("gradle-wrapper.jar")); } /** diff --git a/src/test/java/de/tum/in/www1/artemis/service/FileServiceTest.java b/src/test/java/de/tum/in/www1/artemis/service/FileServiceTest.java index c49477ca5012..30a3dc719c1d 100644 --- a/src/test/java/de/tum/in/www1/artemis/service/FileServiceTest.java +++ b/src/test/java/de/tum/in/www1/artemis/service/FileServiceTest.java @@ -104,7 +104,7 @@ void normalizeFileEndingsUnix_normalized() throws IOException { int size = Files.readAllBytes(Path.of(".", "exportTest", "LineEndingsUnix.java")).length; assertThat(size).isEqualTo(129); - fileService.normalizeLineEndings(Path.of(".", "exportTest", "LineEndingsUnix.java").toString()); + fileService.normalizeLineEndings(Path.of(".", "exportTest", "LineEndingsUnix.java")); size = Files.readAllBytes(Path.of(".", "exportTest", "LineEndingsUnix.java")).length; assertThat(size).isEqualTo(129); } @@ -122,7 +122,7 @@ void normalizeFileEndingsWindows_normalized() throws IOException { int size = Files.readAllBytes(Path.of(".", "exportTest", "LineEndingsWindows.java")).length; assertThat(size).isEqualTo(136); - fileService.normalizeLineEndings(Path.of(".", "exportTest", "LineEndingsWindows.java").toString()); + fileService.normalizeLineEndings(Path.of(".", "exportTest", "LineEndingsWindows.java")); size = Files.readAllBytes(Path.of(".", "exportTest", "LineEndingsWindows.java")).length; assertThat(size).isEqualTo(129); } @@ -140,7 +140,7 @@ void normalizeEncodingISO_8559_1() throws IOException { Charset charset = fileService.detectCharset(Files.readAllBytes(Path.of(".", "exportTest", "EncodingISO_8559_1.java"))); assertThat(charset).isEqualTo(StandardCharsets.ISO_8859_1); - fileService.convertToUTF8(Path.of(".", "exportTest", "EncodingISO_8559_1.java").toString()); + fileService.convertToUTF8(Path.of(".", "exportTest", "EncodingISO_8559_1.java")); charset = fileService.detectCharset(Files.readAllBytes(Path.of(".", "exportTest", "EncodingISO_8559_1.java"))); assertThat(charset).isEqualTo(StandardCharsets.UTF_8); } @@ -156,7 +156,7 @@ void replacePlaceHolder() throws IOException { Map replacements = new HashMap<>(); replacements.put("${exerciseName}", "SomeCoolExerciseName"); - fileService.replaceVariablesInFileRecursive(pomXml.getParent(), replacements); + fileService.replaceVariablesInFileRecursive(pomXml.getParentFile().toPath(), replacements); fileContent = FileUtils.readFileToString(pomXml, Charset.defaultCharset()); assertThat(fileContent).doesNotContain("${exerciseName}").contains("SomeCoolExerciseName"); @@ -173,7 +173,7 @@ void replacePlaceHolderIgnoreNames() throws IOException { Map replacements = new HashMap<>(); replacements.put("${exerciseName}", "SomeCoolExerciseName"); - fileService.replaceVariablesInFileRecursive(pomXml.getParent(), replacements, List.of("pom.xml")); + fileService.replaceVariablesInFileRecursive(pomXml.getParentFile().toPath(), replacements, List.of("pom.xml")); fileContent = FileUtils.readFileToString(pomXml, Charset.defaultCharset()); assertThat(fileContent).contains("${exerciseName}").doesNotContain("SomeCoolExerciseName"); @@ -288,19 +288,19 @@ void testPublicPathForActualPathOrThrow_shouldThrowException() { @Test void testReplaceVariablesInFileRecursive_shouldThrowException() { - assertThatRuntimeException().isThrownBy(() -> fileService.replaceVariablesInFileRecursive("some-path", new HashMap<>())) + assertThatRuntimeException().isThrownBy(() -> fileService.replaceVariablesInFileRecursive(Path.of("some-path"), new HashMap<>())) .withMessageEndingWith("should be replaced but the directory does not exist."); } @Test void testNormalizeLineEndingsDirectory_shouldThrowException() { - assertThatRuntimeException().isThrownBy(() -> fileService.normalizeLineEndingsDirectory("some-path")) + assertThatRuntimeException().isThrownBy(() -> fileService.normalizeLineEndingsDirectory(Path.of("some-path"))) .withMessageEndingWith("should be normalized but the directory does not exist."); } @Test void testConvertToUTF8Directory_shouldThrowException() { - assertThatRuntimeException().isThrownBy(() -> fileService.convertToUTF8Directory("some-path")) + assertThatRuntimeException().isThrownBy(() -> fileService.convertToUTF8Directory(Path.of("some-path"))) .withMessageEndingWith("should be converted to UTF-8 but the directory does not exist."); }