From 0e8b8472fcca6434e39629f793b59cac9fdf88ce Mon Sep 17 00:00:00 2001 From: djamaile Date: Wed, 30 Mar 2022 17:34:33 +0200 Subject: [PATCH] feat: add trigger reason to give a better overview to the user Signed-off-by: djamaile --- .../src/api/utils.test.ts | 89 +++++++++++++++ .../src/api/utils.ts | 107 ++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 plugins/cicd-statistics-module-gitlab/src/api/utils.test.ts create mode 100644 plugins/cicd-statistics-module-gitlab/src/api/utils.ts diff --git a/plugins/cicd-statistics-module-gitlab/src/api/utils.test.ts b/plugins/cicd-statistics-module-gitlab/src/api/utils.test.ts new file mode 100644 index 0000000000..338b92bf4f --- /dev/null +++ b/plugins/cicd-statistics-module-gitlab/src/api/utils.test.ts @@ -0,0 +1,89 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { pipelinesToBuilds, jobsToStages } from './utils'; +import { Types } from '@gitbeaker/core'; + +const pipelineMock: Types.PipelineSchema[] = [ + { + id: 1000, + iid: 1, + project_id: 1, + sha: 'd40', + ref: 'main', + status: 'success', + source: 'schedule', + created_at: '2022-03-30T13:03:09.846Z', + updated_at: '2022-03-30T13:07:49.248Z', + web_url: 'https://gitlab.com/backstage/app/-/pipelines/1000', + user: { + name: 'user', + avatar_url: 'avatar_user', + }, + }, +]; + +// cast to unknown so we can omit a lot unused vars but also keep the type +const jobMock = [ + { + id: 6962883, + status: 'success', + stage: 'build', + name: 'docker', + ref: 'refs/merge-requests/209/train', + tag: false, + allow_failure: false, + created_at: new Date('2022-03-30T08:35:15.394Z'), + started_at: new Date('2022-03-30T08:35:16.532Z'), + finished_at: new Date('2022-03-30T08:35:37.731Z'), + duration: 21.199465, + queued_duration: 0.976313, + }, +] as unknown as Array; + +describe('util functionality', () => { + it('transforms the pipeline object to the build object', () => { + const builds = pipelinesToBuilds(pipelineMock); + expect(builds).toEqual([ + { + id: '1000', + status: 'succeeded', + branchType: 'master', + duration: 0, + requestedAt: new Date('2022-03-30T13:03:09.846Z'), + stages: [], + }, + ]); + }); + + it('transforms the job object to the stage object', () => { + const stages = jobsToStages(jobMock); + expect(stages).toEqual([ + { + name: 'build', + status: 'succeeded', + duration: 21199.465, + stages: [ + { + name: 'docker', + status: 'succeeded', + duration: 21199.465, + }, + ], + }, + ]); + }); +}); diff --git a/plugins/cicd-statistics-module-gitlab/src/api/utils.ts b/plugins/cicd-statistics-module-gitlab/src/api/utils.ts new file mode 100644 index 0000000000..acf2910704 --- /dev/null +++ b/plugins/cicd-statistics-module-gitlab/src/api/utils.ts @@ -0,0 +1,107 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Build, + FilterStatusType, + TriggerReason, + Stage, +} from '@backstage/plugin-cicd-statistics'; +import { Types } from '@gitbeaker/core'; + +const statusMap: Record = { + manual: 'unknown', + created: 'enqueued', + waiting_for_resource: 'stalled', + preparing: 'unknown', + pending: 'scheduled', + running: 'running', + success: 'succeeded', + failed: 'failed', + canceled: 'aborted', + skipped: 'aborted', + scheduled: 'scheduled', +}; + +// all gitlab trigger reasons can be found here: https://docs.gitlab.com/ee/api/pipelines.html#list-project-pipelines +const triggerReasonMap: Record = { + push: 'scm', + trigger: 'manual', + merge_request_event: 'scm', + schedule: 'internal', +}; + +/** + * Takes the Pipeline object from Gitlab and transforms it to the Build object + * + * @param pipelines - Pipeline object that gets returned from Gitlab + * + * @public + */ +export function pipelinesToBuilds( + pipelines: Array, +): Build[] { + return pipelines.map(pipeline => { + return { + id: pipeline.id.toString(), + status: statusMap[pipeline.status], + branchType: 'master', + duration: 0, // will get filled in later in a seperate API call + requestedAt: new Date(pipeline.created_at), + triggeredBy: triggerReasonMap[pipeline.source as string] ?? 'other', + stages: [], + }; + }); +} + +/** + * Takes the Job object from Gitlab and transforms it to the Stage object + * + * @param jobs - Job object that gets returned from Gitlab + * + * @public + * + * @remarks + * + * The Gitlab API can only return the job (sub-stage) of a pipeline and not a whole stage a pipeline + * The job does return from which stage it is + * So, for the stage name we use the parent stage name and in the sub-stages we add the current job + * In the end the cicd-statistics plugin will calculate the right durations for each stage + * + * Furthermore, we don't add the job to the sub-stage if it is has the same name as the parent stage + * We then assume that the stage has no sub-stages + */ +export function jobsToStages(jobs: Array): Stage[] { + return jobs.map(job => { + const status = statusMap[job.status] ? statusMap[job.status] : 'unknown'; + const duration = job.duration ? ((job.duration * 1000) as number) : 0; + return { + name: job.stage, + status, + duration, + stages: + job.name !== job.stage + ? [ + { + name: job.name, + status, + duration, + }, + ] + : [], + }; + }); +}