Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrated code lifecycle: Add auxiliary repositories in exercise export and import #9612

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ private Path exportProgrammingExerciseMaterialWithStudentReposOptional(Programmi
exportErrors.add("Failed to export programming exercise repositories: " + e.getMessage());
}

exercise.setAuxiliaryRepositories(auxiliaryRepositoryRepository.findByExerciseId(exercise.getId()));
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

// Add problem statement as .md file
exportProblemStatementAndEmbeddedFilesAndExerciseDetails(exercise, exportErrors, exportDir.orElseThrow(), pathsToBeZipped);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
Expand All @@ -33,6 +34,7 @@
import de.tum.cit.aet.artemis.core.service.FileService;
import de.tum.cit.aet.artemis.core.service.ProfileService;
import de.tum.cit.aet.artemis.core.service.ZipFileService;
import de.tum.cit.aet.artemis.programming.domain.AuxiliaryRepository;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise;
import de.tum.cit.aet.artemis.programming.domain.Repository;
import de.tum.cit.aet.artemis.programming.domain.RepositoryType;
Expand Down Expand Up @@ -170,44 +172,71 @@ private void importRepositoriesFromFile(ProgrammingExercise newExercise, Path ba
Repository templateRepo = gitService.getOrCheckoutRepository(new VcsRepositoryUri(newExercise.getTemplateRepositoryUri()), false);
Repository solutionRepo = gitService.getOrCheckoutRepository(new VcsRepositoryUri(newExercise.getSolutionRepositoryUri()), false);
Repository testRepo = gitService.getOrCheckoutRepository(new VcsRepositoryUri(newExercise.getTestRepositoryUri()), false);
List<Repository> auxiliaryRepositories = new ArrayList<>();
for (AuxiliaryRepository auxiliaryRepository : newExercise.getAuxiliaryRepositories()) {
auxiliaryRepositories.add(gitService.getOrCheckoutRepository(new VcsRepositoryUri(auxiliaryRepository.getRepositoryUri()), false));
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
}

copyImportedExerciseContentToRepositories(templateRepo, solutionRepo, testRepo, basePath);
replaceImportedExerciseShortName(Map.of(oldExerciseShortName, newExercise.getShortName()), templateRepo, solutionRepo, testRepo);
copyImportedExerciseContentToRepositories(templateRepo, solutionRepo, testRepo, auxiliaryRepositories, basePath);
replaceImportedExerciseShortName(Map.of(oldExerciseShortName, newExercise.getShortName()), List.of(solutionRepo, templateRepo, testRepo));
replaceImportedExerciseShortName(Map.of(oldExerciseShortName, newExercise.getShortName()), auxiliaryRepositories);

gitService.stageAllChanges(templateRepo);
gitService.stageAllChanges(solutionRepo);
gitService.stageAllChanges(testRepo);
for (Repository auxRepo : auxiliaryRepositories) {
gitService.stageAllChanges(auxRepo);
}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

gitService.commitAndPush(templateRepo, "Import template from file", true, user);
gitService.commitAndPush(solutionRepo, "Import solution from file", true, user);
gitService.commitAndPush(testRepo, "Import tests from file", true, user);
for (Repository auxRepo : auxiliaryRepositories) {
gitService.commitAndPush(auxRepo, "Import auxiliary repo from file", true, user);
}

}

private void replaceImportedExerciseShortName(Map<String, String> replacements, Repository... repositories) {
private void replaceImportedExerciseShortName(Map<String, String> replacements, List<Repository> repositories) {
for (Repository repository : repositories) {
fileService.replaceVariablesInFileRecursive(repository.getLocalPath(), replacements, SHORT_NAME_REPLACEMENT_EXCLUSIONS);
}
}

private void copyImportedExerciseContentToRepositories(Repository templateRepo, Repository solutionRepo, Repository testRepo, Path basePath) throws IOException {
private void copyImportedExerciseContentToRepositories(Repository templateRepo, Repository solutionRepo, Repository testRepo, List<Repository> auxiliaryRepositories,
Path basePath) throws IOException {
repositoryService.deleteAllContentInRepository(templateRepo);
repositoryService.deleteAllContentInRepository(solutionRepo);
repositoryService.deleteAllContentInRepository(testRepo);
copyExerciseContentToRepository(templateRepo, RepositoryType.TEMPLATE, basePath);
copyExerciseContentToRepository(solutionRepo, RepositoryType.SOLUTION, basePath);
copyExerciseContentToRepository(testRepo, RepositoryType.TESTS, basePath);
for (Repository auxRepo : auxiliaryRepositories) {
repositoryService.deleteAllContentInRepository(auxRepo);
}

copyExerciseContentToRepository(templateRepo, RepositoryType.TEMPLATE.getName(), basePath);
copyExerciseContentToRepository(solutionRepo, RepositoryType.SOLUTION.getName(), basePath);
copyExerciseContentToRepository(testRepo, RepositoryType.TESTS.getName(), basePath);
for (Repository auxRepo : auxiliaryRepositories) {
String auxRepoSuffix = auxRepo.getLocalPath().toString().split("-")[1];
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
copyExerciseContentToRepository(auxRepo, auxRepoSuffix, basePath);
}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Copies everything from the extracted zip file to the repository, except the .git folder
*
* @param repository the repository to which the content should be copied
* @param repositoryType the type of the repository
* @param basePath the path to the extracted zip file
* @param repository the repository to which the content should be copied
* @param repoName the name of the repository
* @param basePath the path to the extracted zip file
**/
private void copyExerciseContentToRepository(Repository repository, RepositoryType repositoryType, Path basePath) throws IOException {
FileUtils.copyDirectory(retrieveRepositoryDirectoryPath(basePath, repositoryType.getName()).toFile(), repository.getLocalPath().toFile(),
new NotFileFilter(new NameFileFilter(".git")));
private void copyExerciseContentToRepository(Repository repository, String repoName, Path basePath) throws IOException {
// @formatter:off
FileUtils.copyDirectory(
retrieveRepositoryDirectoryPath(basePath, repoName).toFile(),
repository.getLocalPath().toFile(),
new NotFileFilter(new NameFileFilter(".git"))
);
// @formatter:on

try (var files = Files.walk(repository.getLocalPath())) {
files.filter(file -> "gradlew".equals(file.getFileName().toString())).forEach(file -> file.toFile().setExecutable(true));
}
Expand Down Expand Up @@ -242,17 +271,17 @@ private void checkRepositoryForTypeExists(Path path, RepositoryType repoType) th
}
}

private Path retrieveRepositoryDirectoryPath(Path dirPath, String repoType) {
private Path retrieveRepositoryDirectoryPath(Path dirPath, String repoName) {
List<Path> result;
try (Stream<Path> walk = Files.walk(dirPath)) {
result = walk.filter(Files::isDirectory).filter(file -> file.getFileName().toString().endsWith("-" + repoType)).toList();
result = walk.filter(Files::isDirectory).filter(file -> file.getFileName().toString().endsWith("-" + repoName)).toList();
}
catch (IOException e) {
throw new BadRequestAlertException("Could not read the directory", "programmingExercise", "couldnotreaddirectory");
}
if (result.size() != 1) {
throw new IllegalArgumentException(
"There are either no or more than one sub-directories containing " + repoType + " in their name. Please make sure that there is exactly one.");
"There are either no or more than one sub-directories containing " + repoName + " in their name. Please make sure that there is exactly one.");
}

return result.getFirst();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,17 @@ export class ExerciseImportFromFileComponent implements OnInit {
switch (this.exerciseType) {
case ExerciseType.PROGRAMMING:
this.exercise = JSON.parse(exerciseDetails as string) as ProgrammingExercise;
const progEx = this.exercise as ProgrammingExercise;
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
// This is needed to make sure that old exported programming exercises can be imported
if (!(this.exercise as ProgrammingExercise).buildConfig) {
(this.exercise as ProgrammingExercise).buildConfig = copyBuildConfigFromExerciseJson(exerciseJson as ProgrammingExerciseBuildConfig);
if (!progEx.buildConfig) {
progEx.buildConfig = copyBuildConfigFromExerciseJson(exerciseJson as ProgrammingExerciseBuildConfig);
}
if (progEx.auxiliaryRepositories) {
progEx.auxiliaryRepositories!.forEach((repo, index) => {
progEx.auxiliaryRepositories![index].id = undefined;
});
}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
this.exercise = progEx;
break;
default:
this.alertService.error('artemisApp.exercise.importFromFile.notSupportedExerciseType', {
Expand Down
Loading