use of de.tum.in.www1.artemis.domain.BuildLogEntry in project Artemis by ls1intum.
the class JenkinsService method onBuildCompleted.
@Override
public Result onBuildCompleted(ProgrammingExerciseParticipation participation, Object requestBody) {
final var buildResult = TestResultsDTO.convert(requestBody);
var newResult = createResultFromBuildResult(buildResult, participation);
// Fetch submission or create a fallback
var latestSubmission = super.getSubmissionForBuildResult(participation.getId(), buildResult).orElseGet(() -> createAndSaveFallbackSubmission(participation, buildResult));
latestSubmission.setBuildFailed("No tests found".equals(newResult.getResultString()));
// Parse, filter, and save the build logs if they exist
if (buildResult.getLogs() != null) {
ProgrammingLanguage programmingLanguage = participation.getProgrammingExercise().getProgrammingLanguage();
List<BuildLogEntry> buildLogEntries = JenkinsBuildLogParseUtils.parseBuildLogsFromJenkinsLogs(buildResult.getLogs());
buildLogEntries = filterUnnecessaryLogs(buildLogEntries, programmingLanguage);
buildLogEntries = buildLogService.saveBuildLogs(buildLogEntries, latestSubmission);
// Set the received logs in order to avoid duplicate entries (this removes existing logs)
latestSubmission.setBuildLogEntries(buildLogEntries);
}
// Note: we only set one side of the relationship because we don't know yet whether the result will actually be saved
newResult.setSubmission(latestSubmission);
newResult.setRatedIfNotExceeded(exerciseDateService.getDueDate(participation).orElse(null), latestSubmission);
return newResult;
}
use of de.tum.in.www1.artemis.domain.BuildLogEntry in project ArTEMiS by ls1intum.
the class JenkinsService method onBuildCompleted.
@Override
public Result onBuildCompleted(ProgrammingExerciseParticipation participation, Object requestBody) {
final var buildResult = TestResultsDTO.convert(requestBody);
var newResult = createResultFromBuildResult(buildResult, participation);
// Fetch submission or create a fallback
var latestSubmission = super.getSubmissionForBuildResult(participation.getId(), buildResult).orElseGet(() -> createAndSaveFallbackSubmission(participation, buildResult));
latestSubmission.setBuildFailed("No tests found".equals(newResult.getResultString()));
// Parse, filter, and save the build logs if they exist
if (buildResult.getLogs() != null) {
ProgrammingLanguage programmingLanguage = participation.getProgrammingExercise().getProgrammingLanguage();
List<BuildLogEntry> buildLogEntries = JenkinsBuildLogParseUtils.parseBuildLogsFromJenkinsLogs(buildResult.getLogs());
buildLogEntries = filterUnnecessaryLogs(buildLogEntries, programmingLanguage);
buildLogEntries = buildLogService.saveBuildLogs(buildLogEntries, latestSubmission);
// Set the received logs in order to avoid duplicate entries (this removes existing logs)
latestSubmission.setBuildLogEntries(buildLogEntries);
}
// Note: we only set one side of the relationship because we don't know yet whether the result will actually be saved
newResult.setSubmission(latestSubmission);
newResult.setRatedIfNotExceeded(exerciseDateService.getDueDate(participation).orElse(null), latestSubmission);
return newResult;
}
use of de.tum.in.www1.artemis.domain.BuildLogEntry in project ArTEMiS by ls1intum.
the class JenkinsService method getLatestBuildLogs.
@Override
public List<BuildLogEntry> getLatestBuildLogs(ProgrammingSubmission programmingSubmission) {
ProgrammingExerciseParticipation programmingExerciseParticipation = (ProgrammingExerciseParticipation) programmingSubmission.getParticipation();
String projectKey = programmingExerciseParticipation.getProgrammingExercise().getProjectKey();
String buildPlanId = programmingExerciseParticipation.getBuildPlanId();
ProgrammingLanguage programmingLanguage = programmingExerciseParticipation.getProgrammingExercise().getProgrammingLanguage();
try {
final var build = jenkinsJobService.getJobInFolder(projectKey, buildPlanId).getLastBuild();
List<BuildLogEntry> buildLogEntries;
// Attempt to parse pipeline logs
final String pipelineLogs = build.details().getConsoleOutputText();
if (pipelineLogs != null && pipelineLogs.contains("[Pipeline] Start of Pipeline")) {
buildLogEntries = JenkinsBuildLogParseUtils.parseBuildLogsFromJenkinsLogs(List.of(pipelineLogs.split("\n")));
} else {
// Fallback to legacy logs
final var logHtml = Jsoup.parse(build.details().getConsoleOutputHtml()).body();
buildLogEntries = JenkinsBuildLogParseUtils.parseLogsLegacy(logHtml);
}
// Filter and save build logs
buildLogEntries = filterUnnecessaryLogs(buildLogEntries, programmingLanguage);
buildLogEntries = buildLogService.saveBuildLogs(buildLogEntries, programmingSubmission);
programmingSubmission.setBuildLogEntries(buildLogEntries);
programmingSubmissionRepository.save(programmingSubmission);
return buildLogEntries;
} catch (IOException e) {
log.error(e.getMessage(), e);
throw new JenkinsException(e.getMessage(), e);
}
}
use of de.tum.in.www1.artemis.domain.BuildLogEntry in project ArTEMiS by ls1intum.
the class JenkinsBuildLogParseUtils method parseBuildLogsFromJenkinsLogs.
/**
* Parses build logs from Jenkins into BuildLogEntry objects. The function reads the list
* of log strings and tries to parse lines of the following format:
*
* [2021-05-10T15:19:49.741Z] [INFO] BUILD FAILURE
*
* and extract the timestamp and message.
*
* A small snippet of the log format is:
* [Pipeline] {
* [Pipeline] sh
* [2021-05-10T15:19:37.112Z] + mvn clean test -B
* [2021-05-10T15:19:49.741Z] [INFO] BUILD FAILURE
* ...
* [2021-05-10T15:19:49.741Z] [ERROR] BubbleSort.java:[15,9] not a statement
* [2021-05-10T15:19:49.741Z] [ERROR] BubbleSort.java:[15,10] ';' expected
* [Pipeline] }
* @param logLines The lines of the Jenkins log
* @return a list of BuildLogEntries
*/
public static List<BuildLogEntry> parseBuildLogsFromJenkinsLogs(List<String> logLines) {
final List<BuildLogEntry> buildLogs = new ArrayList<>();
for (final var logLine : logLines) {
// The build logs that we are interested in are the ones that start with a timestamp
// of format [timestamp] ...
final String possibleTimestamp = StringUtils.substringBetween(logLine, "[", "]");
if (possibleTimestamp == null) {
continue;
}
try {
final ZonedDateTime timestamp = ZonedDateTime.parse(possibleTimestamp);
// The 2 is used because the timestamp is surrounded with '[' ']'
final String log = logLine.substring(possibleTimestamp.length() + 2);
BuildLogEntry buildLogEntry = new BuildLogEntry(timestamp, stripLogEndOfLine(log).trim());
buildLogs.add(buildLogEntry);
} catch (DateTimeParseException e) {
// The log line doesn't contain the timestamp so we ignore it
}
}
return buildLogs;
}
use of de.tum.in.www1.artemis.domain.BuildLogEntry in project ArTEMiS by ls1intum.
the class ProgrammingSubmissionAndResultGitlabJenkinsIntegrationTest method shouldReceiveBuildLogsOnNewStudentParticipationResult.
@Test
@WithMockUser(username = "student1", roles = "USER")
void shouldReceiveBuildLogsOnNewStudentParticipationResult() throws Exception {
// Precondition: Database has participation and a programming submission.
String userLogin = "student1";
database.addCourseWithOneProgrammingExercise(false, false, ProgrammingLanguage.JAVA);
ProgrammingExercise exercise = programmingExerciseRepository.findAllWithEagerParticipationsAndLegalSubmissions().get(1);
var participation = database.addStudentParticipationForProgrammingExercise(exercise, userLogin);
var submission = database.createProgrammingSubmission(participation, false);
List<String> logs = new ArrayList<>();
logs.add("[2021-05-10T15:19:49.740Z] [ERROR] BubbleSort.java:[15,9] not a statement");
logs.add("[2021-05-10T15:19:49.740Z] [ERROR] BubbleSort.java:[15,10] ';' expected");
var notification = createJenkinsNewResultNotification(exercise.getProjectKey(), userLogin, ProgrammingLanguage.JAVA, List.of());
notification.setLogs(logs);
postResult(notification, HttpStatus.OK);
var submissionWithLogsOptional = submissionRepository.findWithEagerBuildLogEntriesById(submission.getId());
assertThat(submissionWithLogsOptional).isPresent();
// Assert that the submission contains build log entries
ProgrammingSubmission submissionWithLogs = submissionWithLogsOptional.get();
List<BuildLogEntry> buildLogEntries = submissionWithLogs.getBuildLogEntries();
assertThat(buildLogEntries).hasSize(2);
assertThat(buildLogEntries.get(0).getLog()).isEqualTo("[ERROR] BubbleSort.java:[15,9] not a statement");
assertThat(buildLogEntries.get(1).getLog()).isEqualTo("[ERROR] BubbleSort.java:[15,10] ';' expected");
}
Aggregations