From c505eb35e503aad3e2a1956ae2d06f872f0f027d Mon Sep 17 00:00:00 2001 From: Mohamed Bilel Besrour Date: Thu, 21 Nov 2024 03:05:28 +0100 Subject: [PATCH 1/9] wip --- .../service/BuildLogEntryService.java | 32 +++++++++++++++++ .../web/localci/BuildLogResource.java | 18 ++++++++++ .../build-queue/build-queue.component.html | 36 +++++++++++++++++-- .../build-queue/build-queue.component.scss | 18 ++++++++++ .../build-queue/build-queue.component.ts | 35 ++++++++++++++---- .../build-queue/build-queue.service.ts | 13 +++++++ src/main/webapp/i18n/de/buildQueue.json | 4 +++ src/main/webapp/i18n/en/buildQueue.json | 4 +++ 8 files changed, 151 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryService.java index f6143a43561c..e23fac91a80a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryService.java @@ -2,12 +2,15 @@ import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -490,4 +493,33 @@ public boolean buildJobHasLogFile(String buildJobId, ProgrammingExercise program return Files.exists(logPath); } + public List parseBuildLogEntries(FileSystemResource buildLog) { + try { + List buildLogEntries = new ArrayList<>(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(buildLog.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split("\t", 2); + if (parts.length == 2) { + try { + ZonedDateTime time = ZonedDateTime.parse(parts[0]); + buildLogEntries.add(new BuildLogEntry(time, parts[1])); + } + catch (DateTimeParseException e) { + log.warn("Failed to parse build log entry time: {}", parts[0]); + } + } + else if (parts.length == 1 && !parts[0].isBlank()) { + buildLogEntries.add(new BuildLogEntry(null, parts[0])); + } + } + } + return buildLogEntries; + } + catch (IOException e) { + log.error("Error occurred while trying to parse build log entries", e); + return new ArrayList<>(); + } + } + } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/localci/BuildLogResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/localci/BuildLogResource.java index c8fd18e69fca..8e8d3e43cc2e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/localci/BuildLogResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/localci/BuildLogResource.java @@ -2,6 +2,8 @@ import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_LOCALCI; +import java.util.List; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Profile; @@ -17,6 +19,7 @@ import org.springframework.web.bind.annotation.RestController; import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastEditor; +import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; @Profile(PROFILE_LOCALCI) @@ -52,4 +55,19 @@ public ResponseEntity getBuildLogForBuildJob(@PathVariable String buil responseHeaders.setContentDispositionFormData("attachment", "build-" + buildJobId + ".log"); return new ResponseEntity<>(buildLog, responseHeaders, HttpStatus.OK); } + + @GetMapping("build-log/{buildJobId}/entries") + @EnforceAtLeastEditor + public ResponseEntity> getBuildLogEntriesForBuildJob(@PathVariable String buildJobId) { + FileSystemResource buildLog = buildLogEntryService.retrieveBuildLogsFromFileForBuildJob(buildJobId); + if (buildLog == null) { + return ResponseEntity.notFound().build(); + } + + var buildLogEntries = buildLogEntryService.parseBuildLogEntries(buildLog); + if (buildLogEntries == null || buildLogEntries.isEmpty()) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(buildLogEntries); + } } diff --git a/src/main/webapp/app/localci/build-queue/build-queue.component.html b/src/main/webapp/app/localci/build-queue/build-queue.component.html index a8f9a582cbf3..7d7145737dc3 100644 --- a/src/main/webapp/app/localci/build-queue/build-queue.component.html +++ b/src/main/webapp/app/localci/build-queue/build-queue.component.html @@ -504,7 +504,7 @@

@@ -617,7 +617,7 @@

- + {{ finishedBuildJob.buildAgentAddress }} @@ -742,7 +742,7 @@

+ + + + + + diff --git a/src/main/webapp/app/localci/build-queue/build-queue.component.scss b/src/main/webapp/app/localci/build-queue/build-queue.component.scss index b67b0520fbb5..1989ed2fb6d9 100644 --- a/src/main/webapp/app/localci/build-queue/build-queue.component.scss +++ b/src/main/webapp/app/localci/build-queue/build-queue.component.scss @@ -34,3 +34,21 @@ .finish-jobs-column-strings { max-width: 180px; } + +.build-output { + height: inherit; + &__entry { + &-date { + width: 200px; + margin-right: 10px; + color: var(--secondary); + font-weight: normal; + float: left; + clear: left; + } + &-text { + margin-bottom: 0; + color: var(--body-color); + } + } +} diff --git a/src/main/webapp/app/localci/build-queue/build-queue.component.ts b/src/main/webapp/app/localci/build-queue/build-queue.component.ts index 79ca4931786c..c610c7e42013 100644 --- a/src/main/webapp/app/localci/build-queue/build-queue.component.ts +++ b/src/main/webapp/app/localci/build-queue/build-queue.component.ts @@ -19,6 +19,7 @@ import { NgbModal, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap'; import { LocalStorageService } from 'ngx-webstorage'; import { Observable, OperatorFunction, Subject, Subscription, merge } from 'rxjs'; import { UI_RELOAD_TIME } from 'app/shared/constants/exercise-exam-constants'; +import { BuildLogEntry } from 'app/entities/programming/build-log.model'; export class FinishedBuildJobFilter { status?: string = undefined; @@ -159,6 +160,9 @@ export class BuildQueueComponent implements OnInit, OnDestroy { searchSubscription: Subscription; searchTerm?: string = undefined; + rawBuildLogs: BuildLogEntry[] = []; + displayedBuildJobId?: string; + constructor( private route: ActivatedRoute, private websocketService: JhiWebsocketService, @@ -386,11 +390,30 @@ export class BuildQueueComponent implements OnInit, OnDestroy { /** * View the build logs of a specific build job - * @param resultId The id of the build job + * @param modal The modal to open + * @param buildJobId The id of the build job + */ + viewBuildLogs(modal: any, buildJobId: string | undefined): void { + if (buildJobId) { + this.openModal(modal, 'xl'); + this.displayedBuildJobId = buildJobId; + this.buildQueueService.getBuildJobLogs(buildJobId).subscribe({ + next: (buildLogs: BuildLogEntry[]) => { + this.rawBuildLogs = buildLogs; + }, + error: (res: HttpErrorResponse) => { + onError(this.alertService, res); + }, + }); + } + } + + /** + * Download the build logs of a specific build job */ - viewBuildLogs(resultId: string | undefined): void { - if (resultId) { - const url = `/api/build-log/${resultId}`; + downloadBuildLogs(): void { + if (this.displayedBuildJobId) { + const url = `/api/build-log/${this.displayedBuildJobId}`; window.open(url, '_blank'); } } @@ -443,8 +466,8 @@ export class BuildQueueComponent implements OnInit, OnDestroy { /** * Opens the modal. */ - open(content: any) { - this.modalService.open(content); + openModal(modal: any, size?: 'sm' | 'lg' | 'xl', scrollable = true, keyboard = true) { + this.modalService.open(modal, { size, keyboard, scrollable }); } /** diff --git a/src/main/webapp/app/localci/build-queue/build-queue.service.ts b/src/main/webapp/app/localci/build-queue/build-queue.service.ts index fdc9bec66e2f..e62d554aacdd 100644 --- a/src/main/webapp/app/localci/build-queue/build-queue.service.ts +++ b/src/main/webapp/app/localci/build-queue/build-queue.service.ts @@ -7,6 +7,7 @@ import { BuildJob, BuildJobStatistics, FinishedBuildJob, SpanType } from 'app/en import { createNestedRequestOption } from 'app/shared/util/request.util'; import { HttpResponse } from '@angular/common/http'; import { FinishedBuildJobFilter } from 'app/localci/build-queue/build-queue.component'; +import { BuildLogEntry } from 'app/entities/programming/build-log.model'; @Injectable({ providedIn: 'root' }) export class BuildQueueService { @@ -188,4 +189,16 @@ export class BuildQueueService { }), ); } + + /** + * Get all build jobs of a course in the queue + * @param buildJobId + */ + getBuildJobLogs(buildJobId: string): Observable { + return this.http.get(`${this.resourceUrl}/build-log/${buildJobId}/entries`).pipe( + catchError((err) => { + return throwError(() => new Error(`Failed to get build logs for build job ${buildJobId}\n${err.message}`)); + }), + ); + } } diff --git a/src/main/webapp/i18n/de/buildQueue.json b/src/main/webapp/i18n/de/buildQueue.json index faa60fc5c108..641774184538 100644 --- a/src/main/webapp/i18n/de/buildQueue.json +++ b/src/main/webapp/i18n/de/buildQueue.json @@ -74,6 +74,10 @@ "daySpan": "1 Tag", "weekSpan": "7 Tage", "monthSpan": "30 Tage" + }, + "logs": { + "title": "Build Logs für Job", + "download": "Herunterladen" } } } diff --git a/src/main/webapp/i18n/en/buildQueue.json b/src/main/webapp/i18n/en/buildQueue.json index aa065f3617df..fe174aea25c2 100644 --- a/src/main/webapp/i18n/en/buildQueue.json +++ b/src/main/webapp/i18n/en/buildQueue.json @@ -74,6 +74,10 @@ "daySpan": "1 day", "weekSpan": "7 days", "monthSpan": "30 days" + }, + "logs": { + "title": "Build Logs for Job", + "download": "Download" } } } From 28132d7b3731abd10548e3d3e552d16901a5f310 Mon Sep 17 00:00:00 2001 From: Mohamed Bilel Besrour Date: Tue, 10 Dec 2024 03:24:42 +0100 Subject: [PATCH 2/9] wip --- .../service/BuildLogEntryService.java | 3 - .../build-queue/build-queue.service.ts | 2 +- .../icl/LocalCIResourceIntegrationTest.java | 25 +++++++++ .../build-queue/build-queue.component.spec.ts | 29 ++++++++++ .../build-queue/build-queue.service.spec.ts | 55 +++++++++++++++++++ 5 files changed, 110 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryService.java index e23fac91a80a..46706b4393dc 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryService.java @@ -509,9 +509,6 @@ public List parseBuildLogEntries(FileSystemResource buildLog) { log.warn("Failed to parse build log entry time: {}", parts[0]); } } - else if (parts.length == 1 && !parts[0].isBlank()) { - buildLogEntries.add(new BuildLogEntry(null, parts[0])); - } } } return buildLogEntries; diff --git a/src/main/webapp/app/localci/build-queue/build-queue.service.ts b/src/main/webapp/app/localci/build-queue/build-queue.service.ts index e62d554aacdd..9f1a2c820191 100644 --- a/src/main/webapp/app/localci/build-queue/build-queue.service.ts +++ b/src/main/webapp/app/localci/build-queue/build-queue.service.ts @@ -197,7 +197,7 @@ export class BuildQueueService { getBuildJobLogs(buildJobId: string): Observable { return this.http.get(`${this.resourceUrl}/build-log/${buildJobId}/entries`).pipe( catchError((err) => { - return throwError(() => new Error(`Failed to get build logs for build job ${buildJobId}\n${err.message}`)); + return throwError(() => new Error(`Failed to get build log entries for build job ${buildJobId}\n${err.message}`)); }), ); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResourceIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResourceIntegrationTest.java index a4d157d1a690..9c02c4c43dc1 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResourceIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResourceIntegrationTest.java @@ -9,6 +9,7 @@ import java.nio.file.Path; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import org.junit.jupiter.api.AfterEach; @@ -344,6 +345,30 @@ void testGetBuildLogsForResult() throws Exception { } } + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testGetBuildLogsEntriesForResult() throws Exception { + try { + buildJobRepository.save(finishedJobForLogs); + BuildLogEntry buildLogEntry = new BuildLogEntry(ZonedDateTime.now(), "Dummy log"); + buildLogEntryService.saveBuildLogsToFile(List.of(buildLogEntry), "6", programmingExercise); + var response = request.get("/api/build-log/6/entries", HttpStatus.OK, List.class); + + LinkedHashMap responseMap = ((LinkedHashMap) response.getFirst()); + String log = responseMap.get("log").toString(); + String time = responseMap.get("time").toString(); + assertThat(response).hasSize(1); + assertThat(buildLogEntry.getLog()).isEqualTo(log); + assertThat(buildLogEntry.getTime().toString()).contains(time); + + } + finally { + Path buildLogFile = Path.of("build-logs").resolve(programmingExercise.getCourseViaExerciseGroupOrCourseMember().getShortName()) + .resolve(programmingExercise.getShortName()).resolve("6.log"); + Files.deleteIfExists(buildLogFile); + } + } + @Test @WithMockUser(username = TEST_PREFIX + "admin", roles = "ADMIN") void testGetBuildJobStatistics() throws Exception { diff --git a/src/test/javascript/spec/component/localci/build-queue/build-queue.component.spec.ts b/src/test/javascript/spec/component/localci/build-queue/build-queue.component.spec.ts index 4c53661e99d4..7d3d0b1e8f0c 100644 --- a/src/test/javascript/spec/component/localci/build-queue/build-queue.component.spec.ts +++ b/src/test/javascript/spec/component/localci/build-queue/build-queue.component.spec.ts @@ -19,6 +19,7 @@ import { LocalStorageService } from 'ngx-webstorage'; import { MockLocalStorageService } from '../../../helpers/mocks/service/mock-local-storage.service'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { PieChartModule } from '@swimlane/ngx-charts'; +import { BuildLogEntry } from '../../../../../../main/webapp/app/entities/programming/build-log.model'; describe('BuildQueueComponent', () => { let component: BuildQueueComponent; @@ -40,6 +41,7 @@ describe('BuildQueueComponent', () => { getFinishedBuildJobs: jest.fn(), getBuildJobStatistics: jest.fn(), getBuildJobStatisticsForCourse: jest.fn(), + getBuildJobLogs: jest.fn(), }; const mockLocalStorageService = new MockLocalStorageService(); @@ -271,6 +273,17 @@ describe('BuildQueueComponent', () => { numberOfAppliedFilters: 0, }; + const buildLogEntries: BuildLogEntry[] = [ + { + time: dayjs('2024-01-01'), + log: 'log1', + }, + { + time: dayjs('2024-01-02'), + log: 'log2', + }, + ]; + beforeEach(waitForAsync(() => { mockActivatedRoute = { params: of({ courseId: testCourseId }) }; @@ -665,4 +678,20 @@ describe('BuildQueueComponent', () => { expect(component.finishedBuildJobFilter.areDatesValid).toBeFalsy(); expect(component.finishedBuildJobFilter.areDurationFiltersValid).toBeFalsy(); }); + + it('should download build logs', () => { + const buildJobId = '1'; + jest.spyOn(window, 'open').mockImplementation(); + + mockBuildQueueService.getBuildJobLogs = jest.fn().mockReturnValue(of(buildLogEntries)); + + component.viewBuildLogs(undefined, buildJobId); + + expect(mockBuildQueueService.getBuildJobLogs).toHaveBeenCalledWith(buildJobId); + expect(component.rawBuildLogs).toEqual(buildLogEntries); + + component.downloadBuildLogs(); + + expect(window.open).toHaveBeenCalledWith(`/api/build-log/${component.displayedBuildJobId}`, '_blank'); + }); }); diff --git a/src/test/javascript/spec/component/localci/build-queue/build-queue.service.spec.ts b/src/test/javascript/spec/component/localci/build-queue/build-queue.service.spec.ts index 73b4ac460f63..8c64559f674f 100644 --- a/src/test/javascript/spec/component/localci/build-queue/build-queue.service.spec.ts +++ b/src/test/javascript/spec/component/localci/build-queue/build-queue.service.spec.ts @@ -15,6 +15,7 @@ import { JobTimingInfo } from 'app/entities/job-timing-info.model'; import { BuildConfig } from 'app/entities/programming/build-config.model'; import { FinishedBuildJobFilter } from 'app/localci/build-queue/build-queue.component'; import { provideHttpClient } from '@angular/common/http'; +import { BuildLogEntry } from '../../../../../../main/webapp/app/entities/programming/build-log.model'; describe('BuildQueueService', () => { let service: BuildQueueService; @@ -32,6 +33,17 @@ describe('BuildQueueService', () => { filterOptions.buildStartDateFilterTo = dayjs('2024-01-02'); filterOptions.status = 'SUCCESSFUL'; + const buildLogEntries: BuildLogEntry[] = [ + { + time: dayjs('2024-01-01'), + log: 'log1', + }, + { + time: dayjs('2024-01-02'), + log: 'log2', + }, + ]; + const expectFilterParams = (req: TestRequest, filterOptions: FinishedBuildJobFilter) => { expect(req.request.params.get('buildAgentAddress')).toBe(filterOptions.buildAgentAddress); expect(req.request.params.get('buildDurationLower')).toBe(filterOptions.buildDurationFilterLowerBound?.toString()); @@ -590,6 +602,49 @@ describe('BuildQueueService', () => { expect(errorOccurred).toBeTrue(); })); + it('should return build log entries for a specific build job', () => { + const buildJobId = '1'; + const expectedResponse = buildLogEntries; + + service.getBuildJobLogs(buildJobId).subscribe((data) => { + expect(data).toEqual(expectedResponse); + }); + + const req = httpMock.expectOne(`${service.resourceUrl}/build-log/${buildJobId}/entries`); + expect(req.request.method).toBe('GET'); + req.flush(expectedResponse); + }); + + it('should handle errors when getting build log entries for a specific build job', fakeAsync(() => { + const buildJobId = '1'; + + let errorOccurred = false; + + service.getBuildJobLogs(buildJobId).subscribe({ + error: (err) => { + expect(err.message).toBe( + 'Failed to get build log entries for build job ' + + buildJobId + + '\nHttp failure response for ' + + service.resourceUrl + + '/build-log/' + + buildJobId + + '/entries: 500 Internal Server Error', + ); + errorOccurred = true; + }, + }); + + const req = httpMock.expectOne(`${service.resourceUrl}/build-log/${buildJobId}/entries`); + expect(req.request.method).toBe('GET'); + + req.flush(null, { status: 500, statusText: 'Internal Server Error' }); + + tick(); + + expect(errorOccurred).toBeTrue(); + })); + afterEach(() => { httpMock.verify(); // Verify that there are no outstanding requests. }); From 97bc0fd71dd4fad75a845ed221a8a69207ae1205 Mon Sep 17 00:00:00 2001 From: Mohamed Bilel Besrour Date: Tue, 10 Dec 2024 18:28:29 +0100 Subject: [PATCH 3/9] wip --- .../service/BuildLogEntryService.java | 17 +++++++++++++---- .../web/localci/BuildLogResource.java | 4 ++-- .../app/entities/programming/build-log.model.ts | 5 +++++ .../build-queue/build-queue.component.html | 9 +++++++-- .../build-queue/build-queue.component.ts | 15 +++++++++------ .../app/shared/pipes/artemis-date.pipe.ts | 17 +++++++++++------ .../icl/LocalCIResourceIntegrationTest.java | 6 +++--- .../build-queue/build-queue.component.spec.ts | 8 ++++++-- 8 files changed, 56 insertions(+), 25 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryService.java index 779e12958a98..48a82fd74778 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryService.java @@ -494,9 +494,9 @@ public boolean buildJobHasLogFile(String buildJobId, ProgrammingExercise program return Files.exists(logPath); } - public List parseBuildLogEntries(FileSystemResource buildLog) { + public List parseBuildLogEntries(FileSystemResource buildLog) { try { - List buildLogEntries = new ArrayList<>(); + List buildLogEntries = new ArrayList<>(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(buildLog.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { @@ -504,12 +504,21 @@ public List parseBuildLogEntries(FileSystemResource buildLog) { if (parts.length == 2) { try { ZonedDateTime time = ZonedDateTime.parse(parts[0]); - buildLogEntries.add(new BuildLogEntry(time, parts[1])); + buildLogEntries.add(new BuildLogDTO(time, parts[1])); } catch (DateTimeParseException e) { - log.warn("Failed to parse build log entry time: {}", parts[0]); + // If the time cannot be parsed, append the line to the last entry + if (!buildLogEntries.isEmpty()) { + BuildLogDTO lastEntry = buildLogEntries.getLast(); + buildLogEntries.set(buildLogEntries.size() - 1, new BuildLogDTO(lastEntry.time(), lastEntry.log() + "\n\t" + line)); + } } } + else { + // If the line does not contain a tab, add it to in a new entry + BuildLogDTO lastEntry = buildLogEntries.getLast(); + buildLogEntries.add(new BuildLogDTO(lastEntry.time(), line)); + } } } return buildLogEntries; diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/localci/BuildLogResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/localci/BuildLogResource.java index 8e8d3e43cc2e..7a2ecf902ef3 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/localci/BuildLogResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/localci/BuildLogResource.java @@ -18,8 +18,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import de.tum.cit.aet.artemis.buildagent.dto.BuildLogDTO; import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastEditor; -import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; @Profile(PROFILE_LOCALCI) @@ -58,7 +58,7 @@ public ResponseEntity getBuildLogForBuildJob(@PathVariable String buil @GetMapping("build-log/{buildJobId}/entries") @EnforceAtLeastEditor - public ResponseEntity> getBuildLogEntriesForBuildJob(@PathVariable String buildJobId) { + public ResponseEntity> getBuildLogEntriesForBuildJob(@PathVariable String buildJobId) { FileSystemResource buildLog = buildLogEntryService.retrieveBuildLogsFromFileForBuildJob(buildJobId); if (buildLog == null) { return ResponseEntity.notFound().build(); diff --git a/src/main/webapp/app/entities/programming/build-log.model.ts b/src/main/webapp/app/entities/programming/build-log.model.ts index fd4135a005cd..18e951c737a6 100644 --- a/src/main/webapp/app/entities/programming/build-log.model.ts +++ b/src/main/webapp/app/entities/programming/build-log.model.ts @@ -8,6 +8,11 @@ export enum BuildLogType { OTHER = 'OTHER', } +export type BuildLogLines = { + time: any; + logLines: string[]; +}; + export type BuildLogEntry = { time: any; log: string; diff --git a/src/main/webapp/app/localci/build-queue/build-queue.component.html b/src/main/webapp/app/localci/build-queue/build-queue.component.html index 7d7145737dc3..7ce5af03fe2e 100644 --- a/src/main/webapp/app/localci/build-queue/build-queue.component.html +++ b/src/main/webapp/app/localci/build-queue/build-queue.component.html @@ -893,8 +893,13 @@