gitlab-radiator 3.3.5 → 3.3.9

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/.babelrc ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "presets": ["@babel/env", "@babel/react"],
3
+ "plugins": ["@babel/plugin-transform-runtime"]
4
+ }
package/.eslintrc ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "env": {
3
+ "es6": true,
4
+ "mocha": true,
5
+ "node": true
6
+ },
7
+ "parser": "@babel/eslint-parser",
8
+ "plugins": [
9
+ "mocha",
10
+ "react"
11
+ ],
12
+ "extends": ["eslint:recommended"]
13
+ }
@@ -0,0 +1,20 @@
1
+ name: Run tests
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ node-version: [12.x, 14.x]
11
+ steps:
12
+ - uses: actions/checkout@v2
13
+ - uses: actions/setup-node@v1
14
+ with:
15
+ node-version: ${{ matrix.node-version }}
16
+ - run: npm ci
17
+ - run: npm audit
18
+ - run: npm run eslint
19
+ - run: npm test
20
+ - run: npm run build
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 14.17.0
package/README.md CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  # The missing GitLab build radiator view
2
3
 
3
4
  ## Introduction
@@ -122,13 +123,6 @@ colors:
122
123
 
123
124
  See [releases](https://github.com/heikkipora/gitlab-radiator/releases).
124
125
 
125
- ## Breaking changes from 2.x to 3.0
126
-
127
- - Configuration file syntax has changed so that you now can define multiple gitlab instances to poll from.
128
- E.g. polling from https://gitlab.com and from your own hosted https://gitlab.yourdomain.com instance of gitlab.
129
- Unfortunately all existing configurations for single gitlab polling have to be adjusted slightly.
130
- - Also config param `order` has moved from `projects.order` to global `projectsOrder`, as the order has effect on all projects and not per gitlab config.
131
-
132
126
  ## Contributing
133
127
 
134
128
  Pull requests are welcome. Kindly check that your code passes ESLint checks by running `npm run eslint` first.
package/build-npm ADDED
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+
3
+ rm -fr build
4
+ mkdir -p build/src
5
+
6
+ # Copy static resources
7
+ cp -r public build
8
+
9
+ # Copy LICENSE, README and package.json
10
+ cp LICENSE package.json README.md build
11
+
12
+ # Copy bin script
13
+ cp -r bin build
14
+
15
+ # Bundle and minify client JS
16
+ npx webpack --config webpack.prod.js
17
+
18
+ # Transpile server
19
+ node_modules/.bin/babel src --ignore **/client/*.js,**/dev-assets.js --out-dir build/src
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "email": "heikki.pora@gmail.com"
6
6
  },
7
7
  "description": "The missing GitLab build radiator view",
8
- "version": "3.3.5",
8
+ "version": "3.3.9",
9
9
  "license": "MIT",
10
10
  "bin": {
11
11
  "gitlab-radiator": "bin/gitlab-radiator"
@@ -38,53 +38,53 @@
38
38
  }
39
39
  ],
40
40
  "dependencies": {
41
- "@babel/runtime": "7.14.8",
42
- "axios": "0.21.1",
41
+ "@babel/runtime": "7.16.3",
42
+ "axios": "0.24.0",
43
43
  "basic-auth": "2.0.1",
44
44
  "compression": "1.7.4",
45
- "date-fns": "2.23.0",
45
+ "date-fns": "2.27.0",
46
46
  "esm": "3.2.25",
47
47
  "express": "4.17.1",
48
48
  "js-yaml": "4.1.0",
49
49
  "less-middleware": "3.1.0",
50
50
  "lodash": "4.17.21",
51
- "socket.io": "4.1.3"
51
+ "socket.io": "4.4.0"
52
52
  },
53
53
  "devDependencies": {
54
- "@babel/cli": "7.14.8",
55
- "@babel/core": "7.14.8",
56
- "@babel/node": "7.14.9",
57
- "@babel/plugin-transform-runtime": "7.14.5",
58
- "@babel/preset-env": "7.14.9",
59
- "@babel/preset-react": "7.14.5",
60
- "@babel/register": "7.14.5",
61
- "@types/lodash": "4.14.171",
62
- "@types/react": "17.0.15",
63
- "@types/react-dom": "17.0.9",
64
- "@types/webpack-env": "1.16.2",
65
- "@typescript-eslint/eslint-plugin": "4.28.5",
66
- "@typescript-eslint/parser": "4.28.5",
67
- "babel-eslint": "10.1.0",
68
- "babel-loader": "8.2.2",
54
+ "@babel/cli": "7.16.0",
55
+ "@babel/core": "7.16.0",
56
+ "@babel/node": "7.16.0",
57
+ "@babel/plugin-transform-runtime": "7.16.4",
58
+ "@babel/preset-env": "7.16.4",
59
+ "@babel/preset-react": "7.16.0",
60
+ "@babel/register": "7.16.0",
61
+ "@types/lodash": "4.14.177",
62
+ "@types/react": "17.0.37",
63
+ "@types/react-dom": "17.0.11",
64
+ "@types/webpack-env": "1.16.3",
65
+ "@typescript-eslint/eslint-plugin": "5.5.0",
66
+ "@typescript-eslint/parser": "5.5.0",
67
+ "@babel/eslint-parser": "7.16.3",
68
+ "babel-loader": "8.2.3",
69
69
  "chai": "4.3.4",
70
- "css-loader": "6.2.0",
71
- "eslint": "7.32.0",
70
+ "css-loader": "6.5.1",
71
+ "eslint": "8.3.0",
72
72
  "eslint-plugin-mocha": "9.0.0",
73
- "eslint-plugin-react": "7.24.0",
74
- "less": "4.1.1",
75
- "less-loader": "10.0.1",
76
- "mocha": "9.0.3",
73
+ "eslint-plugin-react": "7.27.1",
74
+ "less": "4.1.2",
75
+ "less-loader": "10.2.0",
76
+ "mocha": "9.1.3",
77
77
  "normalize.css": "8.0.1",
78
78
  "react": "17.0.2",
79
79
  "react-dom": "17.0.2",
80
- "sinon": "11.1.2",
81
- "style-loader": "3.2.1",
82
- "ts-loader": "9.2.4",
83
- "typescript": "4.3.5",
84
- "webpack": "5.47.1",
85
- "webpack-cli": "4.7.2",
86
- "webpack-dev-middleware": "5.0.0",
87
- "webpack-hot-middleware": "2.25.0",
80
+ "sinon": "12.0.1",
81
+ "style-loader": "3.3.1",
82
+ "ts-loader": "9.2.6",
83
+ "typescript": "4.5.2",
84
+ "webpack": "5.64.4",
85
+ "webpack-cli": "4.9.1",
86
+ "webpack-dev-middleware": "5.2.2",
87
+ "webpack-hot-middleware": "2.25.1",
88
88
  "webpack-merge": "5.8.0"
89
89
  },
90
90
  "browserslist": [
package/screenshot.png ADDED
Binary file
package/src/app.js CHANGED
@@ -1,209 +1,100 @@
1
- "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
-
5
- var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
6
-
7
- var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
8
-
9
- var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
-
11
- var _auth = require("./auth");
12
-
13
- var _compression = _interopRequireDefault(require("compression"));
14
-
15
- var _config = require("./config");
16
-
17
- var _express = _interopRequireDefault(require("express"));
18
-
19
- var _runners = require("./gitlab/runners");
20
-
21
- var _http = _interopRequireDefault(require("http"));
22
-
23
- var _lessMiddleware = _interopRequireDefault(require("less-middleware"));
24
-
25
- var _os = _interopRequireDefault(require("os"));
26
-
27
- var _path = _interopRequireDefault(require("path"));
28
-
29
- var _socket = _interopRequireDefault(require("socket.io"));
30
-
31
- var _gitlab = require("./gitlab");
32
-
33
- function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
34
-
35
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
36
-
37
- var cacheDir = _path.default.join(_os.default.tmpdir(), 'gitlab-radiator-css-cache');
38
-
39
- var app = (0, _express.default)();
40
-
41
- var httpServer = _http.default.Server(app);
42
-
43
- var socketIoServer = (0, _socket.default)(httpServer);
1
+ import {basicAuth} from './auth'
2
+ import compression from 'compression'
3
+ import {config} from './config'
4
+ import express from 'express'
5
+ import {fetchOfflineRunners} from './gitlab/runners'
6
+ import http from 'http'
7
+ import lessMiddleware from 'less-middleware'
8
+ import os from 'os'
9
+ import path from 'path'
10
+ import socketIo from 'socket.io'
11
+ import {update} from './gitlab'
12
+
13
+ const cacheDir = path.join(os.tmpdir(), 'gitlab-radiator-css-cache')
14
+
15
+ const app = express()
16
+ const httpServer = http.Server(app)
17
+ const socketIoServer = socketIo(httpServer)
44
18
 
45
19
  if (process.env.NODE_ENV !== 'production') {
46
20
  // eslint-disable-next-line global-require
47
- var _require = require('./dev-assets'),
48
- bindDevAssets = _require.bindDevAssets;
49
-
50
- bindDevAssets(app);
21
+ const {bindDevAssets} = require('./dev-assets')
22
+ bindDevAssets(app)
51
23
  }
52
24
 
53
- app.disable('x-powered-by');
54
- app.use((0, _lessMiddleware.default)("".concat(__dirname, "/../public"), {
55
- dest: cacheDir,
56
- preprocess: {
57
- less: function less(src) {
58
- var colorLess = '';
59
- Object.keys(_config.config.colors).forEach(function (stateName) {
60
- colorLess += "@".concat(stateName, "-color:").concat(_config.config.colors[stateName], ";");
61
- });
62
- return src + colorLess;
25
+ app.disable('x-powered-by')
26
+ app.use(lessMiddleware(`${__dirname}/../public`, {
27
+ dest: cacheDir,
28
+ preprocess: {
29
+ less: (src) => {
30
+ let colorLess = ''
31
+ Object.keys(config.colors).forEach((stateName) => {
32
+ colorLess += `@${stateName}-color:${config.colors[stateName]};`
33
+ })
34
+ return src + colorLess
35
+ }
63
36
  }
64
37
  }
65
- }));
66
- app.use(_express.default.static(cacheDir));
67
- app.use(_express.default.static("".concat(__dirname, "/../public")));
68
- app.use((0, _compression.default)());
69
- app.use((0, _auth.basicAuth)(_config.config.auth));
70
- httpServer.listen(_config.config.port, function () {
38
+ ))
39
+ app.use(express.static(cacheDir))
40
+ app.use(express.static(`${__dirname}/../public`))
41
+ app.use(compression())
42
+ app.use(basicAuth(config.auth))
43
+
44
+ httpServer.listen(config.port, () => {
71
45
  // eslint-disable-next-line no-console
72
- console.log("Listening on port *:".concat(_config.config.port));
73
- });
74
- var globalState = {
46
+ console.log(`Listening on port *:${config.port}`)
47
+ })
48
+
49
+ const globalState = {
75
50
  projects: null,
76
51
  error: null,
77
- zoom: _config.config.zoom,
78
- projectsOrder: _config.config.projectsOrder,
79
- columns: _config.config.columns,
80
- groupSuccessfulProjects: _config.config.groupSuccessfulProjects
81
- };
82
- socketIoServer.on('connection', function (socket) {
83
- socket.emit('state', withDate(globalState));
84
- });
85
-
86
- function runUpdate() {
87
- return _runUpdate.apply(this, arguments);
52
+ zoom: config.zoom,
53
+ projectsOrder: config.projectsOrder,
54
+ columns: config.columns,
55
+ groupSuccessfulProjects: config.groupSuccessfulProjects
88
56
  }
89
57
 
90
- function _runUpdate() {
91
- _runUpdate = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
92
- return _regenerator.default.wrap(function _callee$(_context) {
93
- while (1) {
94
- switch (_context.prev = _context.next) {
95
- case 0:
96
- _context.prev = 0;
97
- _context.next = 3;
98
- return (0, _gitlab.update)(_config.config);
99
-
100
- case 3:
101
- globalState.projects = _context.sent;
102
- _context.next = 6;
103
- return errorIfRunnerOffline();
104
-
105
- case 6:
106
- globalState.error = _context.sent;
107
- socketIoServer.emit('state', withDate(globalState));
108
- _context.next = 15;
109
- break;
110
-
111
- case 10:
112
- _context.prev = 10;
113
- _context.t0 = _context["catch"](0);
114
- // eslint-disable-next-line no-console
115
- console.error(_context.t0.message);
116
- globalState.error = "Failed to communicate with GitLab API: ".concat(_context.t0.message);
117
- socketIoServer.emit('state', withDate(globalState));
58
+ socketIoServer.on('connection', (socket) => {
59
+ socket.emit('state', withDate(globalState))
60
+ })
118
61
 
119
- case 15:
120
- setTimeout(runUpdate, _config.config.interval);
121
-
122
- case 16:
123
- case "end":
124
- return _context.stop();
125
- }
126
- }
127
- }, _callee, null, [[0, 10]]);
128
- }));
129
- return _runUpdate.apply(this, arguments);
130
- }
131
-
132
- function errorIfRunnerOffline() {
133
- return _errorIfRunnerOffline.apply(this, arguments);
62
+ async function runUpdate() {
63
+ try {
64
+ globalState.projects = await update(config)
65
+ globalState.error = await errorIfRunnerOffline()
66
+ socketIoServer.emit('state', withDate(globalState))
67
+ } catch (error) {
68
+ // eslint-disable-next-line no-console
69
+ console.error(error.message)
70
+ globalState.error = `Failed to communicate with GitLab API: ${error.message}`
71
+ socketIoServer.emit('state', withDate(globalState))
72
+ }
73
+ setTimeout(runUpdate, config.interval)
134
74
  }
135
75
 
136
- function _errorIfRunnerOffline() {
137
- _errorIfRunnerOffline = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() {
138
- var offlineRunnersPerGitlab, _offlineRunnersPerGit, offline, totalCount, names, counts;
139
-
140
- return _regenerator.default.wrap(function _callee2$(_context2) {
141
- while (1) {
142
- switch (_context2.prev = _context2.next) {
143
- case 0:
144
- _context2.next = 2;
145
- return Promise.all(_config.config.gitlabs.map(_runners.fetchOfflineRunners));
146
-
147
- case 2:
148
- offlineRunnersPerGitlab = _context2.sent;
149
- _offlineRunnersPerGit = offlineRunnersPerGitlab.reduce(function (acc, runner) {
150
- return {
151
- offline: acc.offline.concat(runner.offline),
152
- totalCount: acc.totalCount + runner.totalCount
153
- };
154
- }, {
155
- offline: [],
156
- totalCount: 0
157
- }), offline = _offlineRunnersPerGit.offline, totalCount = _offlineRunnersPerGit.totalCount;
158
-
159
- if (!(offline.length > 0)) {
160
- _context2.next = 8;
161
- break;
162
- }
163
-
164
- names = offline.map(function (r) {
165
- return r.name;
166
- }).sort().join(', ');
167
- counts = offline.length === totalCount ? 'All' : "".concat(offline.length, "/").concat(totalCount);
168
- return _context2.abrupt("return", "".concat(counts, " runners offline: ").concat(names));
169
-
170
- case 8:
171
- return _context2.abrupt("return", null);
76
+ async function errorIfRunnerOffline() {
77
+ const offlineRunnersPerGitlab = await Promise.all(config.gitlabs.map(fetchOfflineRunners))
78
+ const {offline, totalCount} = offlineRunnersPerGitlab.reduce((acc, runner) => {
79
+ return {
80
+ offline: acc.offline.concat(runner.offline),
81
+ totalCount: acc.totalCount + runner.totalCount
82
+ }
83
+ }, {offline: [], totalCount: 0})
172
84
 
173
- case 9:
174
- case "end":
175
- return _context2.stop();
176
- }
177
- }
178
- }, _callee2);
179
- }));
180
- return _errorIfRunnerOffline.apply(this, arguments);
85
+ if (offline.length > 0) {
86
+ const names = offline.map(r => r.name).sort().join(', ')
87
+ const counts = offline.length === totalCount ? 'All' : `${offline.length}/${totalCount}`
88
+ return `${counts} runners offline: ${names}`
89
+ }
90
+ return null
181
91
  }
182
92
 
183
- runUpdate();
93
+ runUpdate()
184
94
 
185
95
  function withDate(state) {
186
- return _objectSpread(_objectSpread({}, state), {}, {
96
+ return {
97
+ ...state,
187
98
  now: Date.now()
188
- });
189
- }
190
-
191
- var signals = {
192
- 'SIGINT': 2,
193
- 'SIGTERM': 15
194
- };
195
-
196
- function shutdown(signal, value) {
197
- httpServer.close(function () {
198
- // eslint-disable-next-line no-console
199
- console.log("Server stopped by ".concat(signal, ".")); // eslint-disable-next-line no-process-exit
200
-
201
- process.exit(128 + value);
202
- });
99
+ }
203
100
  }
204
-
205
- Object.keys(signals).forEach(function (signal) {
206
- process.on(signal, function () {
207
- return shutdown(signal, signals[signal]);
208
- });
209
- });
package/src/auth.js CHANGED
@@ -1,35 +1,21 @@
1
- "use strict";
1
+ import authenticate from 'basic-auth'
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
-
5
- Object.defineProperty(exports, "__esModule", {
6
- value: true
7
- });
8
- exports.basicAuth = basicAuth;
9
-
10
- var _basicAuth = _interopRequireDefault(require("basic-auth"));
11
-
12
- function basicAuth(auth) {
3
+ export function basicAuth(auth) {
13
4
  if (!auth || !auth.username || !auth.password) {
14
5
  // eslint-disable-next-line no-console
15
- console.log('No authentication configured');
16
- return function (req, res, next) {
17
- return next();
18
- };
19
- } // eslint-disable-next-line no-console
20
-
21
-
22
- console.log('HTTP basic auth enabled');
23
- return function (req, res, next) {
24
- var _ref = (0, _basicAuth.default)(req) || {},
25
- name = _ref.name,
26
- pass = _ref.pass;
6
+ console.log('No authentication configured')
7
+ return (req, res, next) => next()
8
+ }
27
9
 
10
+ // eslint-disable-next-line no-console
11
+ console.log('HTTP basic auth enabled')
12
+ return (req, res, next) => {
13
+ const {name, pass} = authenticate(req) || {}
28
14
  if (auth.username === name && auth.password === pass) {
29
- next();
15
+ next()
30
16
  } else {
31
- res.setHeader('WWW-Authenticate', 'Basic realm="gitlab-radiator"');
32
- res.status(401).end();
17
+ res.setHeader('WWW-Authenticate', 'Basic realm="gitlab-radiator"')
18
+ res.status(401).end()
33
19
  }
34
- };
35
- }
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "env": {
3
+ "browser": true,
4
+ "es6": true
5
+ },
6
+ "parser": "@typescript-eslint/parser",
7
+ "plugins": [
8
+ "@typescript-eslint"
9
+ ],
10
+ "extends": [
11
+ "eslint:recommended",
12
+ "plugin:react/recommended",
13
+ "plugin:@typescript-eslint/eslint-recommended",
14
+ "plugin:@typescript-eslint/recommended"
15
+ ],
16
+ "settings": {
17
+ "react": {
18
+ "version": "17.0"
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,75 @@
1
+ interface ParsedQueryString {
2
+ [key: string]: string | undefined
3
+ }
4
+
5
+ export function argumentsFromDocumentUrl(): {override: {columns?: number, zoom?: number}, includedTags: string[] | null, screen: {id: number, total: number}} {
6
+ const args = parseQueryString(document.location.search)
7
+ return {
8
+ override: overrideArguments(args),
9
+ includedTags: tagArguments(args),
10
+ screen: screenArguments(args)
11
+ }
12
+ }
13
+
14
+ function tagArguments(args: ParsedQueryString): string[] | null {
15
+ if (args.tags === undefined) {
16
+ return null
17
+ }
18
+ return args.tags
19
+ .split(',')
20
+ .map(t => t.toLowerCase().trim())
21
+ .filter(t => t)
22
+ }
23
+
24
+ function overrideArguments(args: ParsedQueryString): {columns?: number, zoom?: number} {
25
+ return {
26
+ ...parseColumns(args),
27
+ ...parseZoom(args)
28
+ }
29
+ }
30
+
31
+ function parseColumns(args: ParsedQueryString) {
32
+ if (args.columns) {
33
+ const columns = Number(args.columns)
34
+ if (columns > 0 && columns <= 10) {
35
+ return {columns}
36
+ }
37
+ }
38
+ return {}
39
+ }
40
+
41
+
42
+ function parseZoom(args: ParsedQueryString) {
43
+ if (args.zoom) {
44
+ const zoom = Number(args.zoom)
45
+ if (zoom > 0 && zoom <= 2) {
46
+ return {zoom}
47
+ }
48
+ }
49
+ return {}
50
+ }
51
+
52
+ function screenArguments(args: ParsedQueryString): {id: number, total: number} {
53
+ const matches = (/(\d)of(\d)/).exec(args.screen || '')
54
+ let id = matches ? Number(matches[1]) : 1
55
+ const total = matches ? Number(matches[2]) : 1
56
+ if (id > total) {
57
+ id = total
58
+ }
59
+ return {
60
+ id,
61
+ total
62
+ }
63
+ }
64
+
65
+ function parseQueryString(search: string): ParsedQueryString {
66
+ const entries = search
67
+ .slice(1)
68
+ .split('&')
69
+ .filter(parameter => parameter)
70
+ .map((parameter: string): [string, string | undefined] => {
71
+ const [key, value] = parameter.split('=')
72
+ return [key, value]
73
+ })
74
+ return Object.fromEntries(entries)
75
+ }