gitlab-radiator 4.4.5 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,119 +1,125 @@
1
- import _ from 'lodash'
2
- import {gitlabRequest} from './client.js'
3
-
4
- export async function fetchLatestPipelines(projectId, gitlab) {
5
- const pipelines = await fetchLatestAndMasterPipeline(projectId, gitlab)
6
-
7
- const pipelinesWithStages = []
8
- for (const {id, ref, status} of pipelines) {
9
- const {commit, stages} = await fetchJobs(projectId, id, gitlab)
10
- const downstreamStages = await fetchDownstreamJobs(projectId, id, gitlab)
11
- pipelinesWithStages.push({
12
- id,
13
- ref,
14
- status,
15
- commit,
16
- stages: stages.concat(downstreamStages)
17
- })
18
- }
19
- return pipelinesWithStages
1
+ import { gitlabRequest } from "./client.js";
2
+ export async function fetchLatestPipelines(projectId, gitlab, prioritizeRunningPipelines) {
3
+ const pipelines = await fetchLatestAndMasterPipeline(projectId, gitlab, prioritizeRunningPipelines);
4
+ const pipelinesWithStages = [];
5
+ for (const { id, ref, status } of pipelines) {
6
+ const { commit, stages } = await fetchJobs(projectId, id, gitlab);
7
+ const downstreamStages = await fetchDownstreamJobs(projectId, id, gitlab);
8
+ pipelinesWithStages.push({
9
+ id,
10
+ ref,
11
+ status,
12
+ commit,
13
+ stages: stages.concat(downstreamStages)
14
+ });
15
+ }
16
+ return pipelinesWithStages;
20
17
  }
21
-
22
-
23
- async function fetchLatestAndMasterPipeline(projectId, config) {
24
- const pipelines = await fetchPipelines(projectId, config, {per_page: 100})
25
- if (pipelines.length === 0) {
26
- return []
27
- }
28
- const latestPipeline = _.take(pipelines, 1)
29
- if (latestPipeline[0].ref === 'master') {
30
- return latestPipeline
31
- }
32
- const latestMasterPipeline = _(pipelines).filter({ref: 'master'}).take(1).value()
33
- if (latestMasterPipeline.length > 0) {
34
- return latestPipeline.concat(latestMasterPipeline)
35
- }
36
- const masterPipelines = await fetchPipelines(projectId, config, {per_page: 50, ref: 'master'})
37
- return latestPipeline.concat(_.take(masterPipelines, 1))
18
+ async function fetchLatestAndMasterPipeline(projectId, gitlab, prioritizeRunningPipelines) {
19
+ const options = {
20
+ per_page: 100,
21
+ ...(gitlab.branch ? { ref: gitlab.branch } : {})
22
+ };
23
+ const pipelines = await fetchPipelines(projectId, gitlab, options);
24
+ if (pipelines.length === 0) {
25
+ return [];
26
+ }
27
+ const runningPipelines = pipelines.filter(pipeline => pipeline.status === 'running');
28
+ if (runningPipelines.length > 1 && prioritizeRunningPipelines) {
29
+ return runningPipelines;
30
+ }
31
+ const latestPipeline = pipelines.slice(0, 1);
32
+ if (latestPipeline[0].ref === 'master') {
33
+ return latestPipeline;
34
+ }
35
+ const latestMasterPipeline = pipelines.filter(p => p.ref === 'master').slice(0, 1);
36
+ if (latestMasterPipeline.length > 0) {
37
+ return latestPipeline.concat(latestMasterPipeline);
38
+ }
39
+ const masterPipelines = await fetchPipelines(projectId, gitlab, { per_page: 50, ref: 'master' });
40
+ return latestPipeline.concat(masterPipelines.slice(0, 1));
38
41
  }
39
-
40
- async function fetchPipelines(projectId, config, options) {
41
- const {data: pipelines} = await gitlabRequest(`/projects/${projectId}/pipelines`, options, config)
42
- return pipelines.filter(pipeline => pipeline.status !== 'skipped')
42
+ async function fetchPipelines(projectId, gitlab, params) {
43
+ const { data: pipelines } = await gitlabRequest(`/projects/${projectId}/pipelines`, params, gitlab);
44
+ return pipelines.filter(pipeline => pipeline.status !== 'skipped');
43
45
  }
44
-
45
- async function fetchDownstreamJobs(projectId, pipelineId, config) {
46
- const {data: gitlabBridgeJobs} = await gitlabRequest(`/projects/${projectId}/pipelines/${pipelineId}/bridges`, {per_page: 100}, config)
47
- const childPipelines = gitlabBridgeJobs.filter(bridge => bridge.downstream_pipeline !== null && bridge.downstream_pipeline.status !== 'skipped')
48
-
49
- const downstreamStages = []
50
- for(const childPipeline of childPipelines) {
51
- const {stages} = await fetchJobs(childPipeline.downstream_pipeline.project_id, childPipeline.downstream_pipeline.id, config)
52
- downstreamStages.push(stages.map(stage => ({
53
- ...stage,
54
- name: `${childPipeline.stage}:${stage.name}`
55
- })))
56
- }
57
- return downstreamStages.flat()
46
+ async function fetchDownstreamJobs(projectId, pipelineId, gitlab) {
47
+ const { data: gitlabBridgeJobs } = await gitlabRequest(`/projects/${projectId}/pipelines/${pipelineId}/bridges`, { per_page: 100 }, gitlab);
48
+ const childPipelines = gitlabBridgeJobs.filter((bridge) => bridge.downstream_pipeline !== null && bridge.downstream_pipeline.status !== 'skipped');
49
+ const downstreamStages = [];
50
+ for (const childPipeline of childPipelines) {
51
+ const { stages } = await fetchJobs(childPipeline.downstream_pipeline.project_id, childPipeline.downstream_pipeline.id, gitlab);
52
+ downstreamStages.push(stages.map((stage) => ({
53
+ ...stage,
54
+ name: `${childPipeline.stage}:${stage.name}`
55
+ })));
56
+ }
57
+ return downstreamStages.flat();
58
58
  }
59
-
60
- async function fetchJobs(projectId, pipelineId, config) {
61
- const {data: gitlabJobs} = await gitlabRequest(`/projects/${projectId}/pipelines/${pipelineId}/jobs?include_retried=true`, {per_page: 100}, config)
62
- if (gitlabJobs.length === 0) {
63
- return {commit: undefined, stages: []}
64
- }
65
-
66
- const commit = findCommit(gitlabJobs)
67
- const stages = _(gitlabJobs)
68
- .map(job => ({
69
- id: job.id,
70
- status: job.status,
71
- stage: job.stage,
72
- name: job.name,
73
- startedAt: job.started_at,
74
- finishedAt: job.finished_at,
75
- url: job.web_url
59
+ async function fetchJobs(projectId, pipelineId, gitlab) {
60
+ const { data: gitlabJobs } = await gitlabRequest(`/projects/${projectId}/pipelines/${pipelineId}/jobs?include_retried=true`, { per_page: 100 }, gitlab);
61
+ if (gitlabJobs.length === 0) {
62
+ return { commit: null, stages: [] };
63
+ }
64
+ const commit = findCommit(gitlabJobs);
65
+ // Map jobs and sort by id
66
+ const mappedJobs = gitlabJobs
67
+ .map(job => ({
68
+ id: job.id,
69
+ status: job.status,
70
+ stage: job.stage,
71
+ name: job.name,
72
+ startedAt: job.started_at,
73
+ finishedAt: job.finished_at,
74
+ url: job.web_url
76
75
  }))
77
- .orderBy('id')
78
- .groupBy('stage')
79
- .mapValues(mergeRetriedJobs)
80
- .mapValues(cleanup)
81
- .toPairs()
82
- .map(([name, jobs]) => ({name, jobs: _.sortBy(jobs, 'name')}))
83
- .value()
84
-
85
- return {
86
- commit,
87
- stages
88
- }
76
+ .sort((a, b) => a.id - b.id);
77
+ // Group by stage
78
+ const jobsByStage = new Map();
79
+ for (const job of mappedJobs) {
80
+ const stageJobs = jobsByStage.get(job.stage) || [];
81
+ stageJobs.push(job);
82
+ jobsByStage.set(job.stage, stageJobs);
83
+ }
84
+ // Convert to stages array
85
+ const stages = Array.from(jobsByStage.entries()).map(([name, jobs]) => ({
86
+ name,
87
+ jobs: mergeRetriedJobs(removeStageProperty(jobs)).sort(byName)
88
+ }));
89
+ return {
90
+ commit,
91
+ stages
92
+ };
93
+ }
94
+ function byName(a, b) {
95
+ return a.name.localeCompare(b.name);
89
96
  }
90
-
91
97
  function findCommit(jobs) {
92
- const [job] = jobs.filter(j => j.commit)
93
- if (!job) {
94
- return null
95
- }
96
- return {
97
- title: job.commit.title,
98
- author: job.commit.author_name
99
- }
98
+ const [job] = jobs.filter(j => j.commit);
99
+ if (!job || !job.commit) {
100
+ return null;
101
+ }
102
+ return {
103
+ title: job.commit.title,
104
+ author: job.commit.author_name
105
+ };
100
106
  }
101
-
102
107
  function mergeRetriedJobs(jobs) {
103
- return jobs.reduce((mergedJobs, job) => {
104
- const index = mergedJobs.findIndex(mergedJob => mergedJob.name === job.name)
105
- if (index >= 0) {
106
- mergedJobs[index] = job
107
- } else {
108
- mergedJobs.push(job)
109
- }
110
- return mergedJobs
111
- }, [])
108
+ return jobs.reduce((mergedJobs, job) => {
109
+ const index = mergedJobs.findIndex(mergedJob => mergedJob.name === job.name);
110
+ if (index >= 0) {
111
+ mergedJobs[index] = job;
112
+ }
113
+ else {
114
+ mergedJobs.push(job);
115
+ }
116
+ return mergedJobs;
117
+ }, []);
112
118
  }
113
-
114
- function cleanup(jobs) {
115
- return _(jobs)
116
- .map(job => _.omitBy(job, _.isNull))
117
- .map(job => _.omit(job, 'stage'))
118
- .value()
119
+ function removeStageProperty(jobs) {
120
+ return jobs.map(job => {
121
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
122
+ const { stage, ...rest } = job;
123
+ return rest;
124
+ });
119
125
  }
@@ -1,73 +1,65 @@
1
- import {gitlabRequest} from './client.js'
2
-
1
+ import { gitlabRequest } from "./client.js";
3
2
  export async function fetchProjects(gitlab) {
4
- const projects = await fetchOwnProjects(gitlab)
5
- return projects
6
- // Ignore projects for which CI/CD is not enabled
7
- .filter(project => project.jobs_enabled)
8
- .map(projectMapper)
9
- .filter(includeRegexFilter(gitlab))
10
- .filter(excludeRegexFilter(gitlab))
11
- .filter(archivedFilter(gitlab))
3
+ const projects = await fetchOwnProjects(gitlab);
4
+ return projects
5
+ // Ignore projects for which CI/CD is not enabled
6
+ .filter(project => project.jobs_enabled)
7
+ .map(projectMapper)
8
+ .filter(includeRegexFilter(gitlab))
9
+ .filter(excludeRegexFilter(gitlab))
10
+ .filter(archivedFilter(gitlab));
12
11
  }
13
-
14
12
  async function fetchOwnProjects(gitlab) {
15
- const projects = []
16
- const SAFETY_MAX_PAGE = 10
17
- for (let page = 1; page <= SAFETY_MAX_PAGE; page += 1) {
18
-
19
- const {data, headers} = await gitlabRequest('/projects', {page, per_page: 100, membership: true}, gitlab)
20
- projects.push(data)
21
- if (data.length === 0 || !headers['x-next-page']) {
22
- break
13
+ const projects = [];
14
+ const SAFETY_MAX_PAGE = 10;
15
+ for (let page = 1; page <= SAFETY_MAX_PAGE; page += 1) {
16
+ const { data, headers } = await gitlabRequest('/projects', { page, per_page: 100, membership: true }, gitlab);
17
+ projects.push(...data);
18
+ if (data.length === 0 || !headers['x-next-page']) {
19
+ break;
20
+ }
23
21
  }
24
- }
25
- return projects.flat()
22
+ return projects;
26
23
  }
27
-
28
24
  function projectMapper(project) {
29
- return {
30
- id: project.id,
31
- name: project.path_with_namespace,
32
- nameWithoutNamespace: project.path,
33
- group: getGroupName(project),
34
- archived: project.archived,
35
- default_branch: project.default_branch || 'master',
36
- url: project.web_url,
37
- tags: (project.tag_list || []).map(t => t.toLowerCase())
38
- }
25
+ return {
26
+ id: project.id,
27
+ name: project.path_with_namespace,
28
+ nameWithoutNamespace: project.path,
29
+ group: getGroupName(project),
30
+ archived: project.archived,
31
+ default_branch: project.default_branch || 'master',
32
+ url: project.web_url,
33
+ topics: (project.topics || []).map((t) => t.toLowerCase())
34
+ };
39
35
  }
40
-
41
36
  function getGroupName(project) {
42
- const pathWithNameSpace = project.path_with_namespace
43
- return pathWithNameSpace.split('/')[0]
37
+ const pathWithNameSpace = project.path_with_namespace;
38
+ return pathWithNameSpace.split('/')[0];
44
39
  }
45
-
46
- function includeRegexFilter(config) {
47
- return project => {
48
- if (config.projects && config.projects.include) {
49
- const includeRegex = new RegExp(config.projects.include, "i")
50
- return includeRegex.test(project.name)
51
- }
52
- return true
53
- }
40
+ function includeRegexFilter(gitlab) {
41
+ return (project) => {
42
+ if (gitlab.projects?.include) {
43
+ const includeRegex = new RegExp(gitlab.projects.include, "i");
44
+ return includeRegex.test(project.name);
45
+ }
46
+ return true;
47
+ };
54
48
  }
55
-
56
- function excludeRegexFilter(config) {
57
- return project => {
58
- if (config.projects && config.projects.exclude) {
59
- const excludeRegex = new RegExp(config.projects.exclude, "i")
60
- return !excludeRegex.test(project.name)
61
- }
62
- return true
63
- }
49
+ function excludeRegexFilter(gitlab) {
50
+ return (project) => {
51
+ if (gitlab.projects?.exclude) {
52
+ const excludeRegex = new RegExp(gitlab.projects.exclude, "i");
53
+ return !excludeRegex.test(project.name);
54
+ }
55
+ return true;
56
+ };
64
57
  }
65
-
66
- function archivedFilter(config) {
67
- return project => {
68
- if (config.ignoreArchived) {
69
- return !project.archived
70
- }
71
- return true
72
- }
58
+ function archivedFilter(gitlab) {
59
+ return (project) => {
60
+ if (gitlab.ignoreArchived) {
61
+ return !project.archived;
62
+ }
63
+ return true;
64
+ };
73
65
  }
@@ -1,18 +1,23 @@
1
- import {gitlabRequest} from './client.js'
2
-
1
+ import { gitlabRequest } from "./client.js";
3
2
  export async function fetchOfflineRunners(gitlab) {
4
- const runners = await fetchRunners(gitlab)
5
- const offline = runners.filter(r => r.status === 'offline')
6
- return {
7
- offline,
8
- totalCount: runners.length
9
- }
3
+ if (gitlab.offlineRunners === 'none') {
4
+ return {
5
+ offline: [],
6
+ totalCount: 0
7
+ };
8
+ }
9
+ const runners = await fetchRunners(gitlab);
10
+ const offline = runners.filter(r => r.status === 'offline');
11
+ return {
12
+ offline,
13
+ totalCount: runners.length
14
+ };
10
15
  }
11
-
12
16
  async function fetchRunners(gitlab) {
13
- const {data: runners} = await gitlabRequest('/runners', {}, gitlab)
14
- return runners.map(r => ({
15
- name: r.description || r.id,
16
- status: r.status
17
- }))
17
+ const runnersApi = gitlab.offlineRunners === 'all' ? '/runners/all' : '/runners';
18
+ const { data: runners } = await gitlabRequest(runnersApi, null, gitlab);
19
+ return runners.map(r => ({
20
+ name: r.description || r.id.toString(),
21
+ status: r.status
22
+ }));
18
23
  }
package/src/less.js CHANGED
@@ -1,26 +1,28 @@
1
- import {config} from './config.js'
2
- import fs from 'fs'
3
- import less from 'less'
4
- import path from 'path'
5
-
6
- const filename = path.join('public', 'client.less')
7
-
8
- export async function serveLessAsCss(req, res) {
9
- try {
10
- const source = await fs.promises.readFile(filename, 'utf-8')
11
- const {css} = await less.render(withColorOverrides(source), {filename})
12
- res.setHeader('content-type', 'text/css')
13
- res.send(css)
14
- } catch (err) {
15
- console.error('Failed to render client.less', err)
16
- res.sendStatus(500)
17
- }
1
+ import fs from 'fs';
2
+ import less from 'less';
3
+ import path from 'path';
4
+ import { config } from "./config.js";
5
+ const filename = path.join('public', 'client.less');
6
+ export async function serveLessAsCss(_req, res) {
7
+ try {
8
+ const source = await fs.promises.readFile(filename, 'utf-8');
9
+ const { css } = await less.render(withColorOverrides(source), { filename });
10
+ res.setHeader('content-type', 'text/css');
11
+ res.send(css);
12
+ }
13
+ catch (err) {
14
+ console.error('Failed to render client.less', err);
15
+ res.sendStatus(500);
16
+ }
18
17
  }
19
-
20
18
  function withColorOverrides(source) {
21
- let colorLess = ''
22
- Object.keys(config.colors).forEach((stateName) => {
23
- colorLess += `@${stateName}-color:${config.colors[stateName]};`
24
- })
25
- return source + colorLess
19
+ const { colors } = config;
20
+ if (!colors) {
21
+ return source;
22
+ }
23
+ let colorLess = '';
24
+ Object.keys(colors).forEach((stateName) => {
25
+ colorLess += `@${stateName}-color:${colors[stateName]};`;
26
+ });
27
+ return source + colorLess;
26
28
  }
@@ -0,0 +1 @@
1
+ {"root":["../../src/app.ts","../../src/auth.ts","../../src/config.ts","../../src/dev-assets.ts","../../src/less.ts","../../src/common/gitlab-types.d.ts","../../src/gitlab/client.ts","../../src/gitlab/index.ts","../../src/gitlab/pipelines.ts","../../src/gitlab/projects.ts","../../src/gitlab/runners.ts"],"version":"5.9.3"}