Skip to content

Commit

Permalink
feat(backend): don't use paginated pipelines
Browse files Browse the repository at this point in the history
  • Loading branch information
dr460nf1r3 committed Jan 2, 2025
1 parent ae4958a commit 70aee20
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 57 deletions.
21 changes: 11 additions & 10 deletions backend/src/gitlab/gitlab.controller.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Body, Controller, Get, Headers, Param, ParseIntPipe, Post, UnauthorizedException } from '@nestjs/common';
import { Controller, Get, Headers, Post, UnauthorizedException } from '@nestjs/common';
import { GitlabService } from './gitlab.service';
import { AllowAnonymous } from '../auth/anonymous.decorator';
import { InMemoryCache } from '../cache.source';
import { MINUTE } from 'nestjs-omacache';
import { PipelineWebhook } from './interfaces';
import { ConfigService } from '@nestjs/config';
import { PipelineWithExternalStatus } from '@./shared-lib';

Expand All @@ -16,24 +15,26 @@ export class GitlabController {
private readonly gitlabService: GitlabService,
) {}

@InMemoryCache({
key: 'pipelines',
kind: 'bust',
})
@AllowAnonymous()
@Post('update')
updateCache(@Body() body: PipelineWebhook, @Headers('X-Gitlab-Token') token: string) {
updateCache(@Headers('X-Gitlab-Token') token: string) {
if (token !== this.WEBHOOK_TOKEN) {
throw new UnauthorizedException('Invalid token');
}
this.gitlabService.updatePipelineCache(body);
}

@InMemoryCache({
key: 'some',
key: 'pipelines',
kind: 'temporal',
ttl: MINUTE,
paramIndex: [0],
ttl: 15 * MINUTE,
})
@AllowAnonymous()
@Get('pipelines/:page')
async getPipelines(@Param('page', ParseIntPipe) page?: number): Promise<PipelineWithExternalStatus[]> {
return await this.gitlabService.getLastPipelines({ page });
@Get('pipelines')
async getPipelines(): Promise<PipelineWithExternalStatus[]> {
return await this.gitlabService.getLastPipelines();
}
}
79 changes: 32 additions & 47 deletions backend/src/gitlab/gitlab.service.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,60 @@
import { HttpService } from '@nestjs/axios';
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { CommitStatusSchema, Gitlab, PipelineSchema } from '@gitbeaker/rest';
import { PipelineWebhook } from './interfaces';
import { PipelineWithExternalStatus } from '@./shared-lib';
import { Mutex } from 'async-mutex';

@Injectable()
export class GitlabService {
api = new Gitlab({
token: this.configService.getOrThrow<string>('CAUR_GITLAB_TOKEN'),
});

updateMutex = new Mutex();

private readonly chaoticId = this.configService.getOrThrow<string>('CAUR_GITLAB_ID_CAUR');
private readonly garudaId = this.configService.getOrThrow<string>('CAUR_GITLAB_ID_GARUDA');
private readonly botEmail = this.configService.getOrThrow<string>('CAUR_AUTO_COMMIT_AUTHOR');

constructor(
private readonly configService: ConfigService,
private readonly httpService: HttpService,
) {}
constructor(private readonly configService: ConfigService) {}

private isExternalStage(name: string): boolean {
return name.startsWith('chaotic-aur:') || name.startsWith('garuda:');
}

updatePipelineCache(body: PipelineWebhook) {
Logger.log('Pipeline webhook received', 'GitlabService');
Logger.log(body, 'GitlabService');

const pages = [1, 2, 3, 4];
pages.map((page) => {
this.httpService.get(`/gitlab/pipelines/${page}`).subscribe({
next: (response) => {
Logger.log(response.data, 'GitlabService');
},
error: (err) => {
Logger.error(err, 'GitlabService');
},
});
});
}
/**
* Get the last GitLab pipelines for the chaotic-aur.
* @returns The last pipelines with their external statuses (aka build logs)
*/
async getLastPipelines(): Promise<PipelineWithExternalStatus[]> {
return await this.updateMutex.runExclusive(async () => {
try {
const fetchPromises: Promise<{ commit: CommitStatusSchema[]; pipeline: PipelineSchema }>[] = [];

async getLastPipelines(options: { page?: number }): Promise<PipelineWithExternalStatus[]> {
try {
const fetchPromises: Promise<{ commit: CommitStatusSchema[]; pipeline: PipelineSchema }>[] = [];
let allPipelines: PipelineSchema[] = await this.api.Pipelines.all(this.chaoticId, {
maxPages: 1,
page: 1,
perPage: 50,
});
allPipelines = allPipelines.filter((pipeline) => pipeline.status !== 'skipped');

let allPipelines: PipelineSchema[] = await this.api.Pipelines.all(this.chaoticId, {
maxPages: 1,
page: options.page,
perPage: 50,
});
allPipelines = allPipelines.filter((pipeline) => pipeline.status !== 'skipped');
for (const pipeline of allPipelines) {
this.getCommitStatus(pipeline, fetchPromises);
}

for (const pipeline of allPipelines) {
this.getCommitStatus(pipeline, fetchPromises);
const promiseResults = await Promise.all(fetchPromises);
return promiseResults.sort((a, b) => b.pipeline.id - a.pipeline.id);
} catch (err) {
Logger.error(err, 'GitlabService');
}

const promiseResults = await Promise.all(fetchPromises);
return promiseResults.sort((a, b) => b.pipeline.id - a.pipeline.id);
} catch (err) {
Logger.error(err, 'GitlabService');
}
});
}

private getCommitStatus(
pipeline: PipelineSchema,
promiseArray: Promise<{
commit: CommitStatusSchema[];
pipeline: PipelineSchema;
}>[],
) {
/**
* Get the commit status for a pipeline, pushing the promise to the array of promises
* @param pipeline The pipeline to get the status for
* @param promiseArray The array of promises to push the new promise to
*/
private getCommitStatus(pipeline: PipelineSchema, promiseArray: Promise<PipelineWithExternalStatus>[]) {
promiseArray.push(
new Promise((resolve) => {
this.api.Commits.allStatuses(this.chaoticId, pipeline.sha).then((statuses) => {
Expand Down

0 comments on commit 70aee20

Please sign in to comment.