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.
- package/README.md +11 -6
- package/package.json +27 -19
- package/public/client.js +1 -1
- package/public/client.less +5 -0
- package/public/colors.less +2 -0
- package/public/index.html +0 -1
- package/src/app.js +63 -74
- package/src/auth.js +15 -18
- package/src/config.js +78 -42
- package/src/dev-assets.js +11 -0
- package/src/gitlab/client.js +17 -24
- package/src/gitlab/index.js +41 -47
- package/src/gitlab/pipelines.js +114 -108
- package/src/gitlab/projects.js +53 -61
- package/src/gitlab/runners.js +19 -14
- package/src/less.js +25 -23
- package/src/tsconfig.build.tsbuildinfo +1 -0
package/public/client.less
CHANGED
|
@@ -215,6 +215,11 @@ ol.jobs {
|
|
|
215
215
|
background-color: @success-background-color;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
&.manual {
|
|
219
|
+
color: @manual-text-color;
|
|
220
|
+
background-color: @manual-background-color;
|
|
221
|
+
}
|
|
222
|
+
|
|
218
223
|
&.running {
|
|
219
224
|
color: @running-text-color;
|
|
220
225
|
background: repeating-linear-gradient(
|
package/public/colors.less
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
@failed-background-color: rgb(204, 208, 0);
|
|
17
17
|
@running-text-color: @light-text-color;
|
|
18
18
|
@running-background-color: @success-background-color;
|
|
19
|
+
@manual-text-color: @light-text-color;
|
|
20
|
+
@manual-background-color: @group-background-color;
|
|
19
21
|
|
|
20
22
|
@error-message-text-color: rgb(255, 0, 0);
|
|
21
23
|
@error-message-background-color: rgb(139, 0, 0);
|
package/public/index.html
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
<meta http-equiv="x-ua-compatible" content="IE=edge">
|
|
6
6
|
<meta name="description" content="GitLab build radiator"/>
|
|
7
7
|
<title>GitLab build radiator</title>
|
|
8
|
-
<script src="/socket.io/socket.io.js"></script>
|
|
9
8
|
<link rel="stylesheet" type="text/css" href="/client.css"/>
|
|
10
9
|
</head>
|
|
11
10
|
<body>
|
package/src/app.js
CHANGED
|
@@ -1,85 +1,74 @@
|
|
|
1
|
-
import {basicAuth} from
|
|
2
|
-
import compression from 'compression'
|
|
3
|
-
import {config} from
|
|
4
|
-
import express from 'express'
|
|
5
|
-
import fs from 'fs'
|
|
6
|
-
import {fetchOfflineRunners} from
|
|
7
|
-
import http from 'http'
|
|
8
|
-
import {serveLessAsCss} from
|
|
9
|
-
import {Server} from 'socket.io'
|
|
10
|
-
import {update} from
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const {bindDevAssets} = await import('./dev-assets.js')
|
|
19
|
-
bindDevAssets(app)
|
|
1
|
+
import { basicAuth } from "./auth.js";
|
|
2
|
+
import compression from 'compression';
|
|
3
|
+
import { config } from "./config.js";
|
|
4
|
+
import express from 'express';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import { fetchOfflineRunners } from "./gitlab/runners.js";
|
|
7
|
+
import http from 'http';
|
|
8
|
+
import { serveLessAsCss } from "./less.js";
|
|
9
|
+
import { Server } from 'socket.io';
|
|
10
|
+
import { update } from "./gitlab/index.js";
|
|
11
|
+
const app = express();
|
|
12
|
+
const httpServer = new http.Server(app);
|
|
13
|
+
const socketIoServer = new Server(httpServer);
|
|
14
|
+
if (process.env.NODE_ENV !== 'production' && fs.existsSync('./src/dev-assets.ts')) {
|
|
15
|
+
const { bindDevAssets } = await import("./dev-assets.js");
|
|
16
|
+
bindDevAssets(app);
|
|
20
17
|
}
|
|
21
|
-
|
|
22
|
-
app.
|
|
23
|
-
app.
|
|
24
|
-
app.use(
|
|
25
|
-
app.use(
|
|
26
|
-
app.use(basicAuth(config.auth))
|
|
27
|
-
|
|
18
|
+
app.disable('x-powered-by');
|
|
19
|
+
app.get('/client.css', serveLessAsCss);
|
|
20
|
+
app.use(express.static('public'));
|
|
21
|
+
app.use(compression());
|
|
22
|
+
app.use(basicAuth(config.auth));
|
|
28
23
|
httpServer.listen(config.port, () => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
})
|
|
32
|
-
|
|
24
|
+
console.log(`Listening on port *:${config.port}`);
|
|
25
|
+
});
|
|
33
26
|
const globalState = {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
27
|
+
projects: null,
|
|
28
|
+
error: null,
|
|
29
|
+
zoom: config.zoom,
|
|
30
|
+
projectsOrder: config.projectsOrder,
|
|
31
|
+
columns: config.columns,
|
|
32
|
+
horizontal: config.horizontal,
|
|
33
|
+
rotateRunningPipelines: config.rotateRunningPipelines,
|
|
34
|
+
groupSuccessfulProjects: config.groupSuccessfulProjects
|
|
35
|
+
};
|
|
43
36
|
socketIoServer.on('connection', (socket) => {
|
|
44
|
-
|
|
45
|
-
})
|
|
46
|
-
|
|
37
|
+
socket.emit('state', withDate(globalState));
|
|
38
|
+
});
|
|
47
39
|
async function runUpdate() {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
40
|
+
try {
|
|
41
|
+
globalState.projects = await update(config.gitlabs, config.rotateRunningPipelines > 0);
|
|
42
|
+
globalState.error = await errorIfRunnerOffline();
|
|
43
|
+
socketIoServer.emit('state', withDate(globalState));
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
47
|
+
console.error(message);
|
|
48
|
+
globalState.error = `Failed to communicate with GitLab API: ${message}`;
|
|
49
|
+
socketIoServer.emit('state', withDate(globalState));
|
|
50
|
+
}
|
|
51
|
+
setTimeout(runUpdate, config.interval);
|
|
59
52
|
}
|
|
60
|
-
|
|
61
53
|
async function errorIfRunnerOffline() {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
const offlineRunnersPerGitlab = await Promise.all(config.gitlabs.map(fetchOfflineRunners));
|
|
55
|
+
const { offline, totalCount } = offlineRunnersPerGitlab.reduce((acc, runner) => {
|
|
56
|
+
return {
|
|
57
|
+
offline: acc.offline.concat(runner.offline),
|
|
58
|
+
totalCount: acc.totalCount + runner.totalCount
|
|
59
|
+
};
|
|
60
|
+
}, { offline: [], totalCount: 0 });
|
|
61
|
+
if (offline.length > 0) {
|
|
62
|
+
const names = offline.map(r => r.name).sort().join(', ');
|
|
63
|
+
const counts = offline.length === totalCount ? 'All' : `${offline.length}/${totalCount}`;
|
|
64
|
+
return `${counts} runners offline: ${names}`;
|
|
67
65
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (offline.length > 0) {
|
|
71
|
-
const names = offline.map(r => r.name).sort().join(', ')
|
|
72
|
-
const counts = offline.length === totalCount ? 'All' : `${offline.length}/${totalCount}`
|
|
73
|
-
return `${counts} runners offline: ${names}`
|
|
74
|
-
}
|
|
75
|
-
return null
|
|
66
|
+
return null;
|
|
76
67
|
}
|
|
77
|
-
|
|
78
|
-
await runUpdate()
|
|
79
|
-
|
|
68
|
+
await runUpdate();
|
|
80
69
|
function withDate(state) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
return {
|
|
71
|
+
...state,
|
|
72
|
+
now: Date.now()
|
|
73
|
+
};
|
|
85
74
|
}
|
package/src/auth.js
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import authenticate from 'basic-auth'
|
|
2
|
-
|
|
1
|
+
import authenticate from 'basic-auth';
|
|
3
2
|
export function basicAuth(auth) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return (req, res, next) => next()
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
console.log('HTTP basic auth enabled')
|
|
12
|
-
return (req, res, next) => {
|
|
13
|
-
const {name, pass} = authenticate(req) || {}
|
|
14
|
-
if (auth.username === name && auth.password === pass) {
|
|
15
|
-
next()
|
|
16
|
-
} else {
|
|
17
|
-
res.setHeader('WWW-Authenticate', 'Basic realm="gitlab-radiator"')
|
|
18
|
-
res.status(401).end()
|
|
3
|
+
if (!auth) {
|
|
4
|
+
console.log('No authentication configured');
|
|
5
|
+
return (req, res, next) => next();
|
|
19
6
|
}
|
|
20
|
-
|
|
7
|
+
console.log('HTTP basic auth enabled');
|
|
8
|
+
return (req, res, next) => {
|
|
9
|
+
const { name, pass } = authenticate(req) || {};
|
|
10
|
+
if (auth.username === name && auth.password === pass) {
|
|
11
|
+
next();
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
res.setHeader('WWW-Authenticate', 'Basic realm="gitlab-radiator"');
|
|
15
|
+
res.status(401).end();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
21
18
|
}
|
package/src/config.js
CHANGED
|
@@ -1,44 +1,80 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
const configFile = expandTilde(process.env.GITLAB_RADIATOR_CONFIG || '~/.gitlab-radiator.yml')
|
|
7
|
-
const yamlContent = fs.readFileSync(configFile, 'utf8')
|
|
8
|
-
export const config = validate(yaml.load(yamlContent))
|
|
9
|
-
|
|
10
|
-
config.interval = Number(config.interval || 10) * 1000
|
|
11
|
-
config.port = Number(config.port || 3000)
|
|
12
|
-
config.zoom = Number(config.zoom || 1.0)
|
|
13
|
-
config.columns = Number(config.columns || 1)
|
|
14
|
-
config.horizontal = config.horizontal || false
|
|
15
|
-
config.groupSuccessfulProjects = config.groupSuccessfulProjects || false
|
|
16
|
-
config.projectsOrder = config.projectsOrder || ['name']
|
|
17
|
-
config.gitlabs = config.gitlabs.map((gitlab) => {
|
|
18
|
-
return {
|
|
19
|
-
url: gitlab.url,
|
|
20
|
-
ignoreArchived: gitlab.ignoreArchived === undefined ? true : gitlab.ignoreArchived,
|
|
21
|
-
maxNonFailedJobsVisible: Number(gitlab.maxNonFailedJobsVisible || 999999),
|
|
22
|
-
ca: gitlab.caFile && fs.existsSync(gitlab.caFile, 'utf-8') ? fs.readFileSync(gitlab.caFile) : undefined,
|
|
23
|
-
'access-token': gitlab['access-token'] || process.env.GITLAB_ACCESS_TOKEN,
|
|
24
|
-
projects: {
|
|
25
|
-
excludePipelineStatus: (gitlab.projects || {}).excludePipelineStatus || [],
|
|
26
|
-
include: (gitlab.projects || {}).include || '',
|
|
27
|
-
exclude: (gitlab.projects || {}).exclude || ''
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
})
|
|
31
|
-
config.colors = config.colors || {}
|
|
32
|
-
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
4
|
+
import { z } from 'zod';
|
|
33
5
|
function expandTilde(path) {
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function validate(cfg) {
|
|
38
|
-
assert.ok(cfg.gitlabs, 'Mandatory gitlab properties missing from configuration file')
|
|
39
|
-
cfg.gitlabs.forEach((gitlab) => {
|
|
40
|
-
assert.ok(gitlab.url, 'Mandatory gitlab url missing from configuration file')
|
|
41
|
-
assert.ok(gitlab['access-token'] || process.env.GITLAB_ACCESS_TOKEN, 'Mandatory gitlab access token missing from configuration (and none present at GITLAB_ACCESS_TOKEN env variable)')
|
|
42
|
-
})
|
|
43
|
-
return cfg
|
|
6
|
+
return path.replace(/^~($|\/|\\)/, `${os.homedir()}$1`);
|
|
44
7
|
}
|
|
8
|
+
const JobStatusSchema = z.literal(['canceled', 'created', 'failed', 'manual', 'pending', 'running', 'skipped', 'success']);
|
|
9
|
+
const GitlabSchema = z.strictObject({
|
|
10
|
+
url: z.string().min(1, 'Mandatory gitlab url missing from configuration file'),
|
|
11
|
+
'access-token': z.string().min(1).optional(),
|
|
12
|
+
ignoreArchived: z.boolean().default(true),
|
|
13
|
+
maxNonFailedJobsVisible: z.coerce.number().int().default(999999),
|
|
14
|
+
branch: z.string().min(1).optional(),
|
|
15
|
+
caFile: z.string().optional(),
|
|
16
|
+
offlineRunners: z.literal(['all', 'default', 'none']).default('default'),
|
|
17
|
+
projects: z.strictObject({
|
|
18
|
+
excludePipelineStatus: z.array(JobStatusSchema).optional(),
|
|
19
|
+
include: z.string().min(1).optional(),
|
|
20
|
+
exclude: z.string().min(1).optional()
|
|
21
|
+
}).optional()
|
|
22
|
+
}).transform(gitlab => {
|
|
23
|
+
const accessToken = gitlab['access-token'] || process.env.GITLAB_ACCESS_TOKEN;
|
|
24
|
+
if (!accessToken) {
|
|
25
|
+
throw new Error('Mandatory gitlab access token missing from configuration (and none present at GITLAB_ACCESS_TOKEN env variable)');
|
|
26
|
+
}
|
|
27
|
+
const { url, ignoreArchived, maxNonFailedJobsVisible, caFile, branch, offlineRunners, projects } = gitlab;
|
|
28
|
+
const ca = caFile && fs.existsSync(caFile) ? fs.readFileSync(caFile, 'utf-8') : undefined;
|
|
29
|
+
return {
|
|
30
|
+
url,
|
|
31
|
+
ignoreArchived,
|
|
32
|
+
maxNonFailedJobsVisible,
|
|
33
|
+
branch,
|
|
34
|
+
ca,
|
|
35
|
+
offlineRunners,
|
|
36
|
+
'access-token': accessToken,
|
|
37
|
+
projects
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
const OrderSchema = z.literal(['status', 'name', 'id', 'nameWithoutNamespace', 'group']);
|
|
41
|
+
const ConfigSchema = z.strictObject({
|
|
42
|
+
interval: z.coerce.number().default(10).transform(sec => sec * 1000),
|
|
43
|
+
port: z.coerce.number().int().default(3000),
|
|
44
|
+
zoom: z.coerce.number().default(1.0),
|
|
45
|
+
columns: z.coerce.number().int().default(1),
|
|
46
|
+
horizontal: z.boolean().default(false),
|
|
47
|
+
rotateRunningPipelines: z.coerce.number().min(0).default(0).transform(sec => sec * 1000),
|
|
48
|
+
groupSuccessfulProjects: z.boolean().default(false),
|
|
49
|
+
projectsOrder: z.array(OrderSchema).default(['name']),
|
|
50
|
+
gitlabs: z.array(GitlabSchema).min(1, { message: 'Mandatory gitlab properties missing from configuration file' }),
|
|
51
|
+
colors: z.strictObject({
|
|
52
|
+
background: z.string(),
|
|
53
|
+
'created-background': z.string(),
|
|
54
|
+
'created-text': z.string(),
|
|
55
|
+
'dark-text': z.string(),
|
|
56
|
+
'error-message-background': z.string(),
|
|
57
|
+
'error-message-text': z.string(),
|
|
58
|
+
'failed-background': z.string(),
|
|
59
|
+
'failed-text': z.string(),
|
|
60
|
+
'group-background': z.string(),
|
|
61
|
+
'light-text': z.string(),
|
|
62
|
+
'pending-background': z.string(),
|
|
63
|
+
'pending-text': z.string(),
|
|
64
|
+
'project-background': z.string(),
|
|
65
|
+
'running-background': z.string(),
|
|
66
|
+
'running-text': z.string(),
|
|
67
|
+
'skipped-background': z.string(),
|
|
68
|
+
'skipped-text': z.string(),
|
|
69
|
+
'success-background': z.string(),
|
|
70
|
+
'success-text': z.string()
|
|
71
|
+
}).partial().optional(),
|
|
72
|
+
auth: z.strictObject({
|
|
73
|
+
username: z.string(),
|
|
74
|
+
password: z.string()
|
|
75
|
+
}).optional()
|
|
76
|
+
});
|
|
77
|
+
const configFile = expandTilde(process.env.GITLAB_RADIATOR_CONFIG || '~/.gitlab-radiator.yml');
|
|
78
|
+
const yamlContent = fs.readFileSync(configFile, 'utf8');
|
|
79
|
+
const rawConfig = yaml.load(yamlContent);
|
|
80
|
+
export const config = ConfigSchema.parse(rawConfig);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import webpack from 'webpack';
|
|
3
|
+
import webpackDevMiddleware from 'webpack-dev-middleware';
|
|
4
|
+
import webpackHotMiddleware from 'webpack-hot-middleware';
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
export function bindDevAssets(app) {
|
|
7
|
+
const config = require('../webpack.dev.cjs');
|
|
8
|
+
const compiler = webpack(config);
|
|
9
|
+
app.use(webpackDevMiddleware(compiler));
|
|
10
|
+
app.use(webpackHotMiddleware(compiler));
|
|
11
|
+
}
|
package/src/gitlab/client.js
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
|
-
import axios from 'axios'
|
|
2
|
-
import https from 'https'
|
|
3
|
-
import url from 'url'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return lazyClient(gitlab).get(path, {params})
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import https from 'https';
|
|
3
|
+
import url from 'url';
|
|
4
|
+
export function gitlabRequest(pathStr, params, gitlab) {
|
|
5
|
+
return lazyClient(gitlab).get(pathStr, { params: params || {} });
|
|
7
6
|
}
|
|
8
|
-
|
|
9
|
-
const clients = new Map()
|
|
10
|
-
|
|
7
|
+
const clients = new Map();
|
|
11
8
|
function lazyClient(gitlab) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
})
|
|
24
|
-
clients.set(gitlabUrl, client)
|
|
25
|
-
}
|
|
26
|
-
return clients.get(gitlabUrl)
|
|
9
|
+
let client = clients.get(gitlab.url);
|
|
10
|
+
if (!client) {
|
|
11
|
+
client = axios.create({
|
|
12
|
+
baseURL: url.resolve(gitlab.url, '/api/v4/'),
|
|
13
|
+
headers: { 'PRIVATE-TOKEN': gitlab['access-token'] },
|
|
14
|
+
httpsAgent: new https.Agent({ keepAlive: true, ca: gitlab.ca }),
|
|
15
|
+
timeout: 30 * 1000
|
|
16
|
+
});
|
|
17
|
+
clients.set(gitlab.url, client);
|
|
18
|
+
}
|
|
19
|
+
return client;
|
|
27
20
|
}
|
package/src/gitlab/index.js
CHANGED
|
@@ -1,55 +1,49 @@
|
|
|
1
|
-
import {fetchLatestPipelines} from
|
|
2
|
-
import {fetchProjects} from
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
.filter(project => project.pipelines.length > 0)
|
|
1
|
+
import { fetchLatestPipelines } from "./pipelines.js";
|
|
2
|
+
import { fetchProjects } from "./projects.js";
|
|
3
|
+
export async function update(gitlabs, prioritizeRunningPipelines) {
|
|
4
|
+
const projectsWithPipelines = await loadProjectsWithPipelines(gitlabs, prioritizeRunningPipelines);
|
|
5
|
+
return projectsWithPipelines
|
|
6
|
+
.filter((project) => project.pipelines.length > 0);
|
|
8
7
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}))
|
|
23
|
-
return allProjectsWithPipelines
|
|
8
|
+
async function loadProjectsWithPipelines(gitlabs, prioritizeRunningPipelines) {
|
|
9
|
+
const allProjectsWithPipelines = [];
|
|
10
|
+
await Promise.all(gitlabs.map(async (gitlab) => {
|
|
11
|
+
const projects = (await fetchProjects(gitlab))
|
|
12
|
+
.map(project => ({
|
|
13
|
+
...project,
|
|
14
|
+
maxNonFailedJobsVisible: gitlab.maxNonFailedJobsVisible
|
|
15
|
+
}));
|
|
16
|
+
for (const project of projects) {
|
|
17
|
+
allProjectsWithPipelines.push(await projectWithPipelines(project, gitlab, prioritizeRunningPipelines));
|
|
18
|
+
}
|
|
19
|
+
}));
|
|
20
|
+
return allProjectsWithPipelines;
|
|
24
21
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
22
|
+
async function projectWithPipelines(project, gitlab, prioritizeRunningPipelines) {
|
|
23
|
+
const pipelines = filterOutEmpty(await fetchLatestPipelines(project.id, gitlab, prioritizeRunningPipelines))
|
|
24
|
+
.filter(excludePipelineStatusFilter(gitlab));
|
|
25
|
+
const status = defaultBranchStatus(project, pipelines);
|
|
26
|
+
return {
|
|
27
|
+
...project,
|
|
28
|
+
maxNonFailedJobsVisible: gitlab.maxNonFailedJobsVisible,
|
|
29
|
+
pipelines,
|
|
30
|
+
status
|
|
31
|
+
};
|
|
35
32
|
}
|
|
36
|
-
|
|
37
33
|
function defaultBranchStatus(project, pipelines) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
const [head] = pipelines
|
|
35
|
+
.filter(({ ref }) => ref === project.default_branch)
|
|
36
|
+
.map(({ status }) => status);
|
|
37
|
+
return head;
|
|
42
38
|
}
|
|
43
|
-
|
|
44
39
|
function filterOutEmpty(pipelines) {
|
|
45
|
-
|
|
40
|
+
return pipelines.filter(pipeline => pipeline.stages);
|
|
46
41
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
42
|
+
function excludePipelineStatusFilter(gitlab) {
|
|
43
|
+
return (pipeline) => {
|
|
44
|
+
if (gitlab.projects?.excludePipelineStatus) {
|
|
45
|
+
return !gitlab.projects.excludePipelineStatus.includes(pipeline.status);
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
};
|
|
55
49
|
}
|