paginate common app pipeline history by repository
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-07-09 10:53:53 +09:00
parent b35c486ae9
commit 76cc887371
2 changed files with 68 additions and 9 deletions
+13 -1
View File
@@ -1,4 +1,4 @@
import { Controller, Get } from '@nestjs/common';
import { Controller, Get, Param, Query } from '@nestjs/common';
import { WoodpeckerService } from './woodpecker.service';
@@ -10,4 +10,16 @@ export class WoodpeckerController {
listRepos() {
return this.woodpeckerService.listRepos();
}
@Get('repos/:repoId/pipelines')
listRepoPipelines(
@Param('repoId') repoId: string,
@Query('page') page?: string,
@Query('limit') limit?: string,
) {
return this.woodpeckerService.listRepoPipelines(repoId, {
page: Number(page) || 1,
limit: Number(limit) || 5,
});
}
}
+55 -8
View File
@@ -41,6 +41,32 @@ export class WoodpeckerService {
};
}
async listRepoPipelines(repoId: string, input: { page?: number; limit?: number }) {
this.assertToken();
const page = Math.max(1, input.page || 1);
const limit = Math.min(50, Math.max(1, input.limit || 5));
const body = await this.request<unknown>(
`/api/repos/${encodeURIComponent(repoId)}/pipelines?page=${page}&limit=${limit + 1}`,
);
const rows = Array.isArray(body)
? body
: isRecord(body) && Array.isArray(body.pipelines)
? body.pipelines
: [];
const pipelines = rows
.filter(isRecord)
.slice(0, limit)
.map((pipeline) => normalizePipeline(pipeline));
return {
page,
pageSize: limit,
hasNext: rows.length > limit,
pipelines,
};
}
private async getLatestPipeline(repo: WoodpeckerRepo): Promise<WoodpeckerPipeline> {
const embeddedPipeline = getPipeline(repo);
if (getPipelineTime(embeddedPipeline) > 0) return embeddedPipeline;
@@ -91,23 +117,39 @@ export class WoodpeckerService {
function normalizeRepo(repo: WoodpeckerRepo, pipeline = getPipeline(repo), baseUrl = '') {
const createdAt = getPipelineTime(pipeline);
const repository = getString(repo.full_name) || getString(repo.fullName) || joinRepoName(repo) || getString(repo.name) || '-';
const commitSha = getCommitSha(pipeline);
const durationSeconds = getPipelineDurationSeconds(pipeline);
const normalizedPipeline = normalizePipeline(pipeline);
return {
id: `${getString(repo.id) || repository}-${getString(pipeline.id) || getString(pipeline.number) || createdAt}`,
id: `${getString(repo.id) || repository}-${normalizedPipeline.id || normalizedPipeline.number || createdAt}`,
repoId: getString(repo.id) || null,
repository,
message: getString(pipeline.message) || getString(pipeline.title) || getString(pipeline.event) || '-',
branch: getString(pipeline.branch) || '-',
commitSha,
durationSeconds,
status: getString(pipeline.status) || '-',
number: normalizedPipeline.number,
message: normalizedPipeline.message,
branch: normalizedPipeline.branch,
commitSha: normalizedPipeline.commitSha,
durationSeconds: normalizedPipeline.durationSeconds,
status: normalizedPipeline.status,
createdAt,
ciUrl: baseUrl || null,
url: getString(repo.link_url) || getString(repo.forge_url) || getString(repo.url) || null,
};
}
function normalizePipeline(pipeline: WoodpeckerPipeline) {
const createdAt = getPipelineTime(pipeline);
return {
id: getString(pipeline.id) || getString(pipeline.number) || String(createdAt || ''),
number: getNumber(pipeline.number),
message: getString(pipeline.message) || getString(pipeline.title) || getString(pipeline.event) || '-',
branch: getString(pipeline.branch) || '-',
commitSha: getCommitSha(pipeline),
durationSeconds: getPipelineDurationSeconds(pipeline),
status: getString(pipeline.status) || '-',
createdAt,
};
}
function getPipeline(repo: WoodpeckerRepo): WoodpeckerPipeline {
const pipeline = repo.last_pipeline ?? repo.pipeline;
return isRecord(pipeline) ? pipeline : {};
@@ -161,6 +203,11 @@ function getString(value: unknown) {
return typeof value === 'string' || typeof value === 'number' ? String(value) : '';
}
function getNumber(value: unknown) {
const number = typeof value === 'number' ? value : typeof value === 'string' ? Number(value) : NaN;
return Number.isFinite(number) ? number : null;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}