Skip to content

Commit c907b0a

Browse files
Integrated code lifecycle: Add auxiliary repositories in exercise export and import (#9612)
1 parent 7876792 commit c907b0a

File tree

3 files changed

+57
-18
lines changed

3 files changed

+57
-18
lines changed

src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ protected void exportProblemStatementAndEmbeddedFilesAndExerciseDetails(Exercise
186186
if (exercise instanceof ProgrammingExercise programmingExercise) {
187187
// Used for a save typecast, this should always be true since this class only works with programming exercises.
188188
programmingExerciseTaskService.replaceTestIdsWithNames(programmingExercise);
189+
programmingExercise.setAuxiliaryRepositories(auxiliaryRepositoryRepository.findByExerciseId(exercise.getId()));
189190
}
190191
super.exportProblemStatementAndEmbeddedFilesAndExerciseDetails(exercise, exportErrors, exportDir, pathsToBeZipped);
191192
}

src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import java.nio.charset.StandardCharsets;
99
import java.nio.file.Files;
1010
import java.nio.file.Path;
11+
import java.util.ArrayList;
12+
import java.util.Arrays;
1113
import java.util.List;
1214
import java.util.Map;
1315
import java.util.stream.Stream;
@@ -33,6 +35,7 @@
3335
import de.tum.cit.aet.artemis.core.service.FileService;
3436
import de.tum.cit.aet.artemis.core.service.ProfileService;
3537
import de.tum.cit.aet.artemis.core.service.ZipFileService;
38+
import de.tum.cit.aet.artemis.programming.domain.AuxiliaryRepository;
3639
import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise;
3740
import de.tum.cit.aet.artemis.programming.domain.Repository;
3841
import de.tum.cit.aet.artemis.programming.domain.RepositoryType;
@@ -170,44 +173,72 @@ private void importRepositoriesFromFile(ProgrammingExercise newExercise, Path ba
170173
Repository templateRepo = gitService.getOrCheckoutRepository(new VcsRepositoryUri(newExercise.getTemplateRepositoryUri()), false);
171174
Repository solutionRepo = gitService.getOrCheckoutRepository(new VcsRepositoryUri(newExercise.getSolutionRepositoryUri()), false);
172175
Repository testRepo = gitService.getOrCheckoutRepository(new VcsRepositoryUri(newExercise.getTestRepositoryUri()), false);
176+
List<Repository> auxiliaryRepositories = new ArrayList<>();
177+
for (AuxiliaryRepository auxiliaryRepository : newExercise.getAuxiliaryRepositories()) {
178+
auxiliaryRepositories.add(gitService.getOrCheckoutRepository(auxiliaryRepository.getVcsRepositoryUri(), false));
179+
}
173180

174-
copyImportedExerciseContentToRepositories(templateRepo, solutionRepo, testRepo, basePath);
175-
replaceImportedExerciseShortName(Map.of(oldExerciseShortName, newExercise.getShortName()), templateRepo, solutionRepo, testRepo);
181+
copyImportedExerciseContentToRepositories(templateRepo, solutionRepo, testRepo, auxiliaryRepositories, basePath);
182+
replaceImportedExerciseShortName(Map.of(oldExerciseShortName, newExercise.getShortName()), List.of(solutionRepo, templateRepo, testRepo));
183+
replaceImportedExerciseShortName(Map.of(oldExerciseShortName, newExercise.getShortName()), auxiliaryRepositories);
176184

177185
gitService.stageAllChanges(templateRepo);
178186
gitService.stageAllChanges(solutionRepo);
179187
gitService.stageAllChanges(testRepo);
188+
for (Repository auxRepo : auxiliaryRepositories) {
189+
gitService.stageAllChanges(auxRepo);
190+
}
180191

181192
gitService.commitAndPush(templateRepo, "Import template from file", true, user);
182193
gitService.commitAndPush(solutionRepo, "Import solution from file", true, user);
183194
gitService.commitAndPush(testRepo, "Import tests from file", true, user);
195+
for (Repository auxRepo : auxiliaryRepositories) {
196+
gitService.commitAndPush(auxRepo, "Import auxiliary repo from file", true, user);
197+
}
198+
184199
}
185200

186-
private void replaceImportedExerciseShortName(Map<String, String> replacements, Repository... repositories) {
201+
private void replaceImportedExerciseShortName(Map<String, String> replacements, List<Repository> repositories) {
187202
for (Repository repository : repositories) {
188203
fileService.replaceVariablesInFileRecursive(repository.getLocalPath(), replacements, SHORT_NAME_REPLACEMENT_EXCLUSIONS);
189204
}
190205
}
191206

192-
private void copyImportedExerciseContentToRepositories(Repository templateRepo, Repository solutionRepo, Repository testRepo, Path basePath) throws IOException {
207+
private void copyImportedExerciseContentToRepositories(Repository templateRepo, Repository solutionRepo, Repository testRepo, List<Repository> auxiliaryRepositories,
208+
Path basePath) throws IOException {
193209
repositoryService.deleteAllContentInRepository(templateRepo);
194210
repositoryService.deleteAllContentInRepository(solutionRepo);
195211
repositoryService.deleteAllContentInRepository(testRepo);
196-
copyExerciseContentToRepository(templateRepo, RepositoryType.TEMPLATE, basePath);
197-
copyExerciseContentToRepository(solutionRepo, RepositoryType.SOLUTION, basePath);
198-
copyExerciseContentToRepository(testRepo, RepositoryType.TESTS, basePath);
212+
for (Repository auxRepo : auxiliaryRepositories) {
213+
repositoryService.deleteAllContentInRepository(auxRepo);
214+
}
215+
216+
copyExerciseContentToRepository(templateRepo, RepositoryType.TEMPLATE.getName(), basePath);
217+
copyExerciseContentToRepository(solutionRepo, RepositoryType.SOLUTION.getName(), basePath);
218+
copyExerciseContentToRepository(testRepo, RepositoryType.TESTS.getName(), basePath);
219+
for (Repository auxRepo : auxiliaryRepositories) {
220+
String[] parts = auxRepo.getLocalPath().toString().split("-");
221+
var auxRepoName = String.join("-", Arrays.copyOfRange(parts, 1, parts.length));
222+
copyExerciseContentToRepository(auxRepo, auxRepoName, basePath);
223+
}
199224
}
200225

201226
/**
202227
* Copies everything from the extracted zip file to the repository, except the .git folder
203228
*
204-
* @param repository the repository to which the content should be copied
205-
* @param repositoryType the type of the repository
206-
* @param basePath the path to the extracted zip file
229+
* @param repository the repository to which the content should be copied
230+
* @param repoName the name of the repository
231+
* @param basePath the path to the extracted zip file
207232
**/
208-
private void copyExerciseContentToRepository(Repository repository, RepositoryType repositoryType, Path basePath) throws IOException {
209-
FileUtils.copyDirectory(retrieveRepositoryDirectoryPath(basePath, repositoryType.getName()).toFile(), repository.getLocalPath().toFile(),
210-
new NotFileFilter(new NameFileFilter(".git")));
233+
private void copyExerciseContentToRepository(Repository repository, String repoName, Path basePath) throws IOException {
234+
// @formatter:off
235+
FileUtils.copyDirectory(
236+
retrieveRepositoryDirectoryPath(basePath, repoName).toFile(),
237+
repository.getLocalPath().toFile(),
238+
new NotFileFilter(new NameFileFilter(".git"))
239+
);
240+
// @formatter:on
241+
211242
try (var files = Files.walk(repository.getLocalPath())) {
212243
files.filter(file -> "gradlew".equals(file.getFileName().toString())).forEach(file -> file.toFile().setExecutable(true));
213244
}
@@ -242,17 +273,17 @@ private void checkRepositoryForTypeExists(Path path, RepositoryType repoType) th
242273
}
243274
}
244275

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

258289
return result.getFirst();

src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,17 @@ export class ExerciseImportFromFileComponent implements OnInit {
5050
switch (this.exerciseType) {
5151
case ExerciseType.PROGRAMMING:
5252
this.exercise = JSON.parse(exerciseDetails as string) as ProgrammingExercise;
53+
const progEx = this.exercise as ProgrammingExercise;
5354
// This is needed to make sure that old exported programming exercises can be imported
54-
if (!(this.exercise as ProgrammingExercise).buildConfig) {
55-
(this.exercise as ProgrammingExercise).buildConfig = copyBuildConfigFromExerciseJson(exerciseJson as ProgrammingExerciseBuildConfig);
55+
if (!progEx.buildConfig) {
56+
progEx.buildConfig = copyBuildConfigFromExerciseJson(exerciseJson as ProgrammingExerciseBuildConfig);
5657
}
58+
if (progEx.auxiliaryRepositories) {
59+
progEx.auxiliaryRepositories!.forEach((repo, index) => {
60+
progEx.auxiliaryRepositories![index].id = undefined;
61+
});
62+
}
63+
this.exercise = progEx;
5764
break;
5865
default:
5966
this.alertService.error('artemisApp.exercise.importFromFile.notSupportedExerciseType', {

0 commit comments

Comments
 (0)