@backstage-community/plugin-gocd 0.1.42 → 0.1.43
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/CHANGELOG.md +6 -0
- package/dist/api/gocdApi.client.esm.js +28 -0
- package/dist/api/gocdApi.client.esm.js.map +1 -0
- package/dist/api/gocdApi.model.esm.js +28 -0
- package/dist/api/gocdApi.model.esm.js.map +1 -0
- package/dist/components/GoCdBuildsComponent/GoCdBuildsComponent.esm.js +87 -0
- package/dist/components/GoCdBuildsComponent/GoCdBuildsComponent.esm.js.map +1 -0
- package/dist/components/GoCdBuildsComponent/index.esm.js +2 -0
- package/dist/components/GoCdBuildsComponent/index.esm.js.map +1 -0
- package/dist/components/GoCdBuildsInsights/GoCdBuildsInsights.esm.js +126 -0
- package/dist/components/GoCdBuildsInsights/GoCdBuildsInsights.esm.js.map +1 -0
- package/dist/components/GoCdBuildsTable/GoCdBuildsTable.esm.js +135 -0
- package/dist/components/GoCdBuildsTable/GoCdBuildsTable.esm.js.map +1 -0
- package/dist/components/Select/Select.esm.js +48 -0
- package/dist/components/Select/Select.esm.js.map +1 -0
- package/dist/extensions.esm.js +16 -0
- package/dist/extensions.esm.js.map +1 -0
- package/dist/index.esm.js +3 -24
- package/dist/index.esm.js.map +1 -1
- package/dist/plugin.esm.js +21 -0
- package/dist/plugin.esm.js.map +1 -0
- package/package.json +22 -11
- package/dist/esm/index-CMeyzgrA.esm.js +0 -460
- package/dist/esm/index-CMeyzgrA.esm.js.map +0 -1
- package/dist/esm/index-Dpt975Dd.esm.js +0 -25
- package/dist/esm/index-Dpt975Dd.esm.js.map +0 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,25 +1,4 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import 'react';
|
|
5
|
-
import '@backstage/plugin-catalog-react';
|
|
6
|
-
import '@backstage/core-components';
|
|
7
|
-
import 'react-use/esm/useAsync';
|
|
8
|
-
import '@backstage/catalog-model';
|
|
9
|
-
import '@material-ui/lab/Alert';
|
|
10
|
-
import '@material-ui/core/Button';
|
|
11
|
-
import '@material-ui/icons/GitHub';
|
|
12
|
-
import 'luxon';
|
|
13
|
-
import 'lodash';
|
|
14
|
-
import '@material-ui/core/Box';
|
|
15
|
-
import '@material-ui/core/Grid';
|
|
16
|
-
import '@material-ui/core/Card';
|
|
17
|
-
import '@material-ui/core/CardContent';
|
|
18
|
-
import '@material-ui/core/Tooltip';
|
|
19
|
-
import '@material-ui/core/Typography';
|
|
20
|
-
import '@material-ui/core/FormControl';
|
|
21
|
-
import '@material-ui/core/InputLabel';
|
|
22
|
-
import '@material-ui/core/MenuItem';
|
|
23
|
-
import '@material-ui/core/Select';
|
|
24
|
-
import '@material-ui/core/styles';
|
|
1
|
+
export { gocdPlugin } from './plugin.esm.js';
|
|
2
|
+
export { EntityGoCdContent } from './extensions.esm.js';
|
|
3
|
+
export { GOCD_PIPELINES_ANNOTATION, isGoCdAvailable } from './components/GoCdBuildsComponent/GoCdBuildsComponent.esm.js';
|
|
25
4
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { GoCdClientApi } from './api/gocdApi.client.esm.js';
|
|
2
|
+
import { createApiRef, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';
|
|
3
|
+
|
|
4
|
+
const gocdApiRef = createApiRef({
|
|
5
|
+
id: "plugin.gocd.service"
|
|
6
|
+
});
|
|
7
|
+
const gocdPlugin = createPlugin({
|
|
8
|
+
id: "gocd",
|
|
9
|
+
apis: [
|
|
10
|
+
createApiFactory({
|
|
11
|
+
api: gocdApiRef,
|
|
12
|
+
deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },
|
|
13
|
+
factory: ({ discoveryApi, fetchApi }) => {
|
|
14
|
+
return new GoCdClientApi(discoveryApi, fetchApi);
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
]
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export { gocdApiRef, gocdPlugin };
|
|
21
|
+
//# sourceMappingURL=plugin.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { GoCdClientApi } from './api/gocdApi.client';\nimport { GoCdApi } from './api/gocdApi';\nimport {\n discoveryApiRef,\n createApiRef,\n createApiFactory,\n createPlugin,\n fetchApiRef,\n} from '@backstage/core-plugin-api';\n\nexport const gocdApiRef = createApiRef<GoCdApi>({\n id: 'plugin.gocd.service',\n});\n\n/**\n * Plugin definition.\n *\n * @public\n */\nexport const gocdPlugin = createPlugin({\n id: 'gocd',\n apis: [\n createApiFactory({\n api: gocdApiRef,\n deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },\n factory: ({ discoveryApi, fetchApi }) => {\n return new GoCdClientApi(discoveryApi, fetchApi);\n },\n }),\n ],\n});\n"],"names":[],"mappings":";;;AA0BO,MAAM,aAAa,YAAsB,CAAA;AAAA,EAC9C,EAAI,EAAA,qBAAA;AACN,CAAC,EAAA;AAOM,MAAM,aAAa,YAAa,CAAA;AAAA,EACrC,EAAI,EAAA,MAAA;AAAA,EACJ,IAAM,EAAA;AAAA,IACJ,gBAAiB,CAAA;AAAA,MACf,GAAK,EAAA,UAAA;AAAA,MACL,IAAM,EAAA,EAAE,YAAc,EAAA,eAAA,EAAiB,UAAU,WAAY,EAAA;AAAA,MAC7D,OAAS,EAAA,CAAC,EAAE,YAAA,EAAc,UAAe,KAAA;AACvC,QAAO,OAAA,IAAI,aAAc,CAAA,YAAA,EAAc,QAAQ,CAAA,CAAA;AAAA,OACjD;AAAA,KACD,CAAA;AAAA,GACH;AACF,CAAC;;;;"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage-community/plugin-gocd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.43",
|
|
4
4
|
"description": "A Backstage plugin that integrates towards GoCD",
|
|
5
5
|
"backstage": {
|
|
6
|
-
"role": "frontend-plugin"
|
|
6
|
+
"role": "frontend-plugin",
|
|
7
|
+
"pluginId": "gocd",
|
|
8
|
+
"pluginPackages": [
|
|
9
|
+
"@backstage-community/plugin-gocd"
|
|
10
|
+
]
|
|
7
11
|
},
|
|
8
12
|
"publishConfig": {
|
|
9
13
|
"access": "public",
|
|
@@ -34,11 +38,11 @@
|
|
|
34
38
|
"test": "backstage-cli package test"
|
|
35
39
|
},
|
|
36
40
|
"dependencies": {
|
|
37
|
-
"@backstage/catalog-model": "^1.
|
|
38
|
-
"@backstage/core-components": "^0.14.
|
|
39
|
-
"@backstage/core-plugin-api": "^1.9.
|
|
41
|
+
"@backstage/catalog-model": "^1.5.0",
|
|
42
|
+
"@backstage/core-components": "^0.14.9",
|
|
43
|
+
"@backstage/core-plugin-api": "^1.9.3",
|
|
40
44
|
"@backstage/errors": "^1.2.4",
|
|
41
|
-
"@backstage/plugin-catalog-react": "^1.
|
|
45
|
+
"@backstage/plugin-catalog-react": "^1.12.2",
|
|
42
46
|
"@material-ui/core": "^4.12.2",
|
|
43
47
|
"@material-ui/icons": "^4.9.1",
|
|
44
48
|
"@material-ui/lab": "4.0.0-alpha.61",
|
|
@@ -48,10 +52,10 @@
|
|
|
48
52
|
"react-use": "^17.2.4"
|
|
49
53
|
},
|
|
50
54
|
"devDependencies": {
|
|
51
|
-
"@backstage/cli": "^0.26.
|
|
52
|
-
"@backstage/core-app-api": "^1.
|
|
53
|
-
"@backstage/dev-utils": "^1.0.
|
|
54
|
-
"@backstage/test-utils": "^1.5.
|
|
55
|
+
"@backstage/cli": "^0.26.11",
|
|
56
|
+
"@backstage/core-app-api": "^1.14.1",
|
|
57
|
+
"@backstage/dev-utils": "^1.0.36",
|
|
58
|
+
"@backstage/test-utils": "^1.5.9",
|
|
55
59
|
"@testing-library/dom": "^10.0.0",
|
|
56
60
|
"@testing-library/jest-dom": "^6.0.0",
|
|
57
61
|
"@testing-library/react": "^15.0.0",
|
|
@@ -61,7 +65,7 @@
|
|
|
61
65
|
"@types/react": "^16.13.1 || ^17.0.0",
|
|
62
66
|
"@types/react-dom": "^18.2.19",
|
|
63
67
|
"canvas": "^2.11.2",
|
|
64
|
-
"msw": "^
|
|
68
|
+
"msw": "^2.0.0",
|
|
65
69
|
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
|
66
70
|
"react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
|
67
71
|
"react-router-dom": "6.0.0-beta.0 || ^6.3.0"
|
|
@@ -72,5 +76,12 @@
|
|
|
72
76
|
"react-router-dom": "6.0.0-beta.0 || ^6.3.0"
|
|
73
77
|
},
|
|
74
78
|
"configSchema": "config.d.ts",
|
|
79
|
+
"jest": {
|
|
80
|
+
"testEnvironmentOptions": {
|
|
81
|
+
"customExportConditions": [
|
|
82
|
+
""
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
},
|
|
75
86
|
"module": "./dist/index.esm.js"
|
|
76
87
|
}
|
|
@@ -1,460 +0,0 @@
|
|
|
1
|
-
import { ResponseError } from '@backstage/errors';
|
|
2
|
-
import { createApiRef, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, createComponentExtension, useApi, configApiRef } from '@backstage/core-plugin-api';
|
|
3
|
-
import React, { useState } from 'react';
|
|
4
|
-
import { useEntity, MissingAnnotationEmptyState } from '@backstage/plugin-catalog-react';
|
|
5
|
-
import { Table, SubvalueCell, StatusWarning, StatusPending, StatusAborted, StatusRunning, StatusError, StatusOK, Link, EmptyState, Page, Content, ContentHeader } from '@backstage/core-components';
|
|
6
|
-
import useAsync from 'react-use/esm/useAsync';
|
|
7
|
-
import { getEntitySourceLocation } from '@backstage/catalog-model';
|
|
8
|
-
import Alert from '@material-ui/lab/Alert';
|
|
9
|
-
import Button from '@material-ui/core/Button';
|
|
10
|
-
import GitHubIcon from '@material-ui/icons/GitHub';
|
|
11
|
-
import { DateTime, Duration } from 'luxon';
|
|
12
|
-
import { mean, groupBy } from 'lodash';
|
|
13
|
-
import Box from '@material-ui/core/Box';
|
|
14
|
-
import Grid from '@material-ui/core/Grid';
|
|
15
|
-
import Card from '@material-ui/core/Card';
|
|
16
|
-
import CardContent from '@material-ui/core/CardContent';
|
|
17
|
-
import Tooltip from '@material-ui/core/Tooltip';
|
|
18
|
-
import Typography from '@material-ui/core/Typography';
|
|
19
|
-
import FormControl from '@material-ui/core/FormControl';
|
|
20
|
-
import InputLabel from '@material-ui/core/InputLabel';
|
|
21
|
-
import MenuItem from '@material-ui/core/MenuItem';
|
|
22
|
-
import Select from '@material-ui/core/Select';
|
|
23
|
-
import { makeStyles, createStyles } from '@material-ui/core/styles';
|
|
24
|
-
|
|
25
|
-
const DEFAULT_PAGE_SIZE = 100;
|
|
26
|
-
class GoCdClientApi {
|
|
27
|
-
constructor(discoveryApi, fetchApi) {
|
|
28
|
-
this.discoveryApi = discoveryApi;
|
|
29
|
-
this.fetchApi = fetchApi;
|
|
30
|
-
}
|
|
31
|
-
async getPipelineHistory(pipelineName) {
|
|
32
|
-
const { fetch } = this.fetchApi;
|
|
33
|
-
const baseUrl = await this.discoveryApi.getBaseUrl("proxy");
|
|
34
|
-
const pipelineHistoryResponse = await fetch(
|
|
35
|
-
`${baseUrl}/gocd/pipelines/${pipelineName}/history?page_size=${DEFAULT_PAGE_SIZE}`,
|
|
36
|
-
{
|
|
37
|
-
headers: {
|
|
38
|
-
Accept: "application/vnd.go.cd+json"
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
);
|
|
42
|
-
if (!pipelineHistoryResponse.ok) {
|
|
43
|
-
throw await ResponseError.fromResponse(pipelineHistoryResponse);
|
|
44
|
-
}
|
|
45
|
-
return await pipelineHistoryResponse.json();
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const gocdApiRef = createApiRef({
|
|
50
|
-
id: "plugin.gocd.service"
|
|
51
|
-
});
|
|
52
|
-
const gocdPlugin = createPlugin({
|
|
53
|
-
id: "gocd",
|
|
54
|
-
apis: [
|
|
55
|
-
createApiFactory({
|
|
56
|
-
api: gocdApiRef,
|
|
57
|
-
deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },
|
|
58
|
-
factory: ({ discoveryApi, fetchApi }) => {
|
|
59
|
-
return new GoCdClientApi(discoveryApi, fetchApi);
|
|
60
|
-
}
|
|
61
|
-
})
|
|
62
|
-
]
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const EntityGoCdContent = gocdPlugin.provide(
|
|
66
|
-
createComponentExtension({
|
|
67
|
-
name: "EntityGoCdContent",
|
|
68
|
-
component: {
|
|
69
|
-
lazy: () => import('./index-Dpt975Dd.esm.js').then(
|
|
70
|
-
(m) => m.GoCdBuildsComponent
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
var GoCdBuildResultStatus = /* @__PURE__ */ ((GoCdBuildResultStatus2) => {
|
|
77
|
-
GoCdBuildResultStatus2[GoCdBuildResultStatus2["running"] = 0] = "running";
|
|
78
|
-
GoCdBuildResultStatus2[GoCdBuildResultStatus2["successful"] = 1] = "successful";
|
|
79
|
-
GoCdBuildResultStatus2[GoCdBuildResultStatus2["warning"] = 2] = "warning";
|
|
80
|
-
GoCdBuildResultStatus2[GoCdBuildResultStatus2["aborted"] = 3] = "aborted";
|
|
81
|
-
GoCdBuildResultStatus2[GoCdBuildResultStatus2["error"] = 4] = "error";
|
|
82
|
-
GoCdBuildResultStatus2[GoCdBuildResultStatus2["pending"] = 5] = "pending";
|
|
83
|
-
return GoCdBuildResultStatus2;
|
|
84
|
-
})(GoCdBuildResultStatus || {});
|
|
85
|
-
const toBuildResultStatus = (status) => {
|
|
86
|
-
switch (status.toLocaleLowerCase("en-US")) {
|
|
87
|
-
case "passed":
|
|
88
|
-
return 1 /* successful */;
|
|
89
|
-
case "failed":
|
|
90
|
-
return 4 /* error */;
|
|
91
|
-
case "aborted":
|
|
92
|
-
return 3 /* aborted */;
|
|
93
|
-
case "building":
|
|
94
|
-
return 0 /* running */;
|
|
95
|
-
case "pending":
|
|
96
|
-
return 5 /* pending */;
|
|
97
|
-
default:
|
|
98
|
-
return 3 /* aborted */;
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const renderTrigger = (build) => {
|
|
103
|
-
const subvalue = /* @__PURE__ */ React.createElement(React.Fragment, null, build.pipeline, /* @__PURE__ */ React.createElement("br", null), build.author);
|
|
104
|
-
return /* @__PURE__ */ React.createElement(SubvalueCell, { value: build.message, subvalue });
|
|
105
|
-
};
|
|
106
|
-
const renderStages = (build) => {
|
|
107
|
-
return build.stages.map((s) => {
|
|
108
|
-
switch (s.status) {
|
|
109
|
-
case GoCdBuildResultStatus.successful: {
|
|
110
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(StatusOK, null, s.text), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null));
|
|
111
|
-
}
|
|
112
|
-
case GoCdBuildResultStatus.error: {
|
|
113
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(StatusError, null, s.text), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null));
|
|
114
|
-
}
|
|
115
|
-
case GoCdBuildResultStatus.running: {
|
|
116
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(StatusRunning, null, s.text), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null));
|
|
117
|
-
}
|
|
118
|
-
case GoCdBuildResultStatus.aborted: {
|
|
119
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(StatusAborted, null, s.text), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null));
|
|
120
|
-
}
|
|
121
|
-
case GoCdBuildResultStatus.pending: {
|
|
122
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(StatusPending, null, s.text), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null));
|
|
123
|
-
}
|
|
124
|
-
default: {
|
|
125
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(StatusWarning, null, s.text), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null));
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
};
|
|
130
|
-
const renderSource = (build) => {
|
|
131
|
-
return /* @__PURE__ */ React.createElement(
|
|
132
|
-
Button,
|
|
133
|
-
{
|
|
134
|
-
href: build.source,
|
|
135
|
-
target: "_blank",
|
|
136
|
-
rel: "noreferrer",
|
|
137
|
-
startIcon: /* @__PURE__ */ React.createElement(GitHubIcon, null)
|
|
138
|
-
},
|
|
139
|
-
build.commitHash
|
|
140
|
-
);
|
|
141
|
-
};
|
|
142
|
-
const renderId = (goCdBaseUrl, build) => {
|
|
143
|
-
const goCdBuildUrl = `${goCdBaseUrl}/go/pipelines/value_stream_map/${build.buildSlug}`;
|
|
144
|
-
return /* @__PURE__ */ React.createElement(
|
|
145
|
-
SubvalueCell,
|
|
146
|
-
{
|
|
147
|
-
value: /* @__PURE__ */ React.createElement(Link, { to: goCdBuildUrl, target: "_blank", rel: "noreferrer" }, build.id),
|
|
148
|
-
subvalue: build.triggerTime && DateTime.fromMillis(build.triggerTime).toLocaleString(
|
|
149
|
-
DateTime.DATETIME_MED
|
|
150
|
-
)
|
|
151
|
-
}
|
|
152
|
-
);
|
|
153
|
-
};
|
|
154
|
-
const renderError = (error) => {
|
|
155
|
-
return /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, error.message);
|
|
156
|
-
};
|
|
157
|
-
const toBuildResults = (entity, builds) => {
|
|
158
|
-
const entitySourceLocation = getEntitySourceLocation(entity).target.split("/tree")[0];
|
|
159
|
-
return builds.map((build) => {
|
|
160
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
161
|
-
return {
|
|
162
|
-
id: build.counter,
|
|
163
|
-
source: `${entitySourceLocation}/commit/${(_b = (_a = build.build_cause) == null ? void 0 : _a.material_revisions[0]) == null ? void 0 : _b.modifications[0].revision}`,
|
|
164
|
-
stages: build.stages.map((s) => ({
|
|
165
|
-
status: toBuildResultStatus(s.status),
|
|
166
|
-
text: s.name
|
|
167
|
-
})),
|
|
168
|
-
buildSlug: `${build.name}/${build.counter}`,
|
|
169
|
-
message: (_e = (_d = (_c = build.build_cause) == null ? void 0 : _c.material_revisions[0]) == null ? void 0 : _d.modifications[0].comment) != null ? _e : "",
|
|
170
|
-
pipeline: build.name,
|
|
171
|
-
author: (_g = (_f = build.build_cause) == null ? void 0 : _f.material_revisions[0]) == null ? void 0 : _g.modifications[0].user_name,
|
|
172
|
-
commitHash: build.label,
|
|
173
|
-
triggerTime: build.scheduled_date
|
|
174
|
-
};
|
|
175
|
-
});
|
|
176
|
-
};
|
|
177
|
-
const GoCdBuildsTable = (props) => {
|
|
178
|
-
const { pipelineHistory, loading, error } = props;
|
|
179
|
-
const { entity } = useEntity();
|
|
180
|
-
const [, setSelectedSearchTerm] = useState("");
|
|
181
|
-
const columns = [
|
|
182
|
-
{
|
|
183
|
-
title: "ID",
|
|
184
|
-
field: "id",
|
|
185
|
-
highlight: true,
|
|
186
|
-
render: (build) => renderId(props.goCdBaseUrl, build)
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
title: "Trigger",
|
|
190
|
-
customFilterAndSearch: (query, row) => `${row.message} ${row.pipeline} ${row.author}`.toLocaleUpperCase("en-US").includes(query.toLocaleUpperCase("en-US")),
|
|
191
|
-
field: "message",
|
|
192
|
-
highlight: true,
|
|
193
|
-
render: (build) => renderTrigger(build)
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
title: "Stages",
|
|
197
|
-
field: "status",
|
|
198
|
-
render: (build) => renderStages(build)
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
title: "Source",
|
|
202
|
-
field: "source",
|
|
203
|
-
render: (build) => renderSource(build)
|
|
204
|
-
}
|
|
205
|
-
];
|
|
206
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, error && renderError(error), !error && /* @__PURE__ */ React.createElement(
|
|
207
|
-
Table,
|
|
208
|
-
{
|
|
209
|
-
title: "GoCD builds",
|
|
210
|
-
isLoading: loading,
|
|
211
|
-
options: {
|
|
212
|
-
search: true,
|
|
213
|
-
searchAutoFocus: true,
|
|
214
|
-
debounceInterval: 800,
|
|
215
|
-
paging: true,
|
|
216
|
-
padding: "dense",
|
|
217
|
-
pageSizeOptions: [5, 10, 20, 50],
|
|
218
|
-
showFirstLastPageButtons: false,
|
|
219
|
-
pageSize: 20
|
|
220
|
-
},
|
|
221
|
-
columns,
|
|
222
|
-
data: pipelineHistory ? toBuildResults(entity, pipelineHistory.pipelines) || [] : [],
|
|
223
|
-
onSearchChange: (searchTerm) => setSelectedSearchTerm(searchTerm)
|
|
224
|
-
}
|
|
225
|
-
));
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
function runFrequency(pipelineHistory) {
|
|
229
|
-
const lastMonth = DateTime.now().minus({ month: 1 });
|
|
230
|
-
const buildLastMonth = pipelineHistory.pipelines.map((p) => p.scheduled_date).filter((d) => !!d).map((d) => DateTime.fromMillis(d)).filter((d) => d > lastMonth).length;
|
|
231
|
-
return `${buildLastMonth} last month`;
|
|
232
|
-
}
|
|
233
|
-
function meanTimeBetweenFailures(jobs) {
|
|
234
|
-
const timeBetweenFailures = [];
|
|
235
|
-
for (let index = 1; index < jobs.length; index++) {
|
|
236
|
-
const job = jobs[index];
|
|
237
|
-
if (!job.result) {
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
if (toBuildResultStatus(job.result) === GoCdBuildResultStatus.error) {
|
|
241
|
-
let previousFailedJob = null;
|
|
242
|
-
for (let j = index - 1; j >= 0; j--) {
|
|
243
|
-
const candidateJob = jobs[j];
|
|
244
|
-
if (!candidateJob.result) {
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
if (toBuildResultStatus(candidateJob.result) === GoCdBuildResultStatus.error) {
|
|
248
|
-
previousFailedJob = candidateJob;
|
|
249
|
-
break;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
if (!previousFailedJob || !job.scheduled_date || !previousFailedJob.scheduled_date) {
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
const failedJobDate = DateTime.fromMillis(job.scheduled_date);
|
|
256
|
-
const previousFailedJobDate = DateTime.fromMillis(
|
|
257
|
-
previousFailedJob.scheduled_date
|
|
258
|
-
);
|
|
259
|
-
const timeBetweenFailure = failedJobDate.diff(previousFailedJobDate);
|
|
260
|
-
timeBetweenFailures.push(timeBetweenFailure);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return formatMean(timeBetweenFailures);
|
|
264
|
-
}
|
|
265
|
-
function meanTimeToRecovery(jobs) {
|
|
266
|
-
const timeToRecoverIntervals = [];
|
|
267
|
-
for (let index = 0; index < jobs.length; index++) {
|
|
268
|
-
const job = jobs[index];
|
|
269
|
-
if (!job.result) {
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
if (toBuildResultStatus(job.result) === GoCdBuildResultStatus.error) {
|
|
273
|
-
let nextSuccessfulJob = null;
|
|
274
|
-
for (let j = index + 1; j < jobs.length; j++) {
|
|
275
|
-
const candidateJob = jobs[j];
|
|
276
|
-
if (!candidateJob.result) {
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
if (toBuildResultStatus(candidateJob.result) === GoCdBuildResultStatus.successful) {
|
|
280
|
-
nextSuccessfulJob = candidateJob;
|
|
281
|
-
break;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
if (!nextSuccessfulJob || !job.scheduled_date || !nextSuccessfulJob.scheduled_date) {
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
const failedJobDate = DateTime.fromMillis(job.scheduled_date);
|
|
288
|
-
const successfulJobDate = DateTime.fromMillis(
|
|
289
|
-
nextSuccessfulJob.scheduled_date
|
|
290
|
-
);
|
|
291
|
-
const timeToRecovery = successfulJobDate.diff(failedJobDate);
|
|
292
|
-
timeToRecoverIntervals.push(timeToRecovery);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
return formatMean(timeToRecoverIntervals);
|
|
296
|
-
}
|
|
297
|
-
function formatMean(durations) {
|
|
298
|
-
if (durations.length === 0) {
|
|
299
|
-
return "N/A";
|
|
300
|
-
}
|
|
301
|
-
const mttr = Duration.fromMillis(
|
|
302
|
-
mean(durations.map((i) => i.milliseconds))
|
|
303
|
-
);
|
|
304
|
-
return mttr.toFormat("d'd' h'h' m'm' s's'");
|
|
305
|
-
}
|
|
306
|
-
function failureRate(jobs) {
|
|
307
|
-
const resultGroups = new Map(
|
|
308
|
-
Object.entries(groupBy(jobs, "result")).map(([key, value]) => [
|
|
309
|
-
toBuildResultStatus(key),
|
|
310
|
-
value.flat()
|
|
311
|
-
])
|
|
312
|
-
);
|
|
313
|
-
const failedJobs = resultGroups.get(GoCdBuildResultStatus.error);
|
|
314
|
-
if (!failedJobs) {
|
|
315
|
-
return {
|
|
316
|
-
title: "0",
|
|
317
|
-
subtitle: "(no failed jobs found)"
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
resultGroups.delete(GoCdBuildResultStatus.error);
|
|
321
|
-
const nonFailedJobs = Array.from(resultGroups.values()).flat();
|
|
322
|
-
const totalJobs = failedJobs.length + nonFailedJobs.length;
|
|
323
|
-
const percentage = failedJobs.length / totalJobs * 100;
|
|
324
|
-
const decimalPercentage = (Math.round(percentage * 100) / 100).toFixed(2);
|
|
325
|
-
return {
|
|
326
|
-
title: `${decimalPercentage}%`,
|
|
327
|
-
subtitle: `(${failedJobs.length} out of ${totalJobs} jobs)`
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
const GoCdBuildsInsights = (props) => {
|
|
331
|
-
const { pipelineHistory, loading, error } = props;
|
|
332
|
-
if (loading || error || !pipelineHistory) {
|
|
333
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null);
|
|
334
|
-
}
|
|
335
|
-
const stages = pipelineHistory.pipelines.slice().reverse().map((p) => p.stages).flat();
|
|
336
|
-
const jobs = stages.map((s) => s.jobs).flat();
|
|
337
|
-
const failureRateObj = failureRate(jobs);
|
|
338
|
-
return /* @__PURE__ */ React.createElement(Box, { "data-testid": "GoCdBuildsInsightsBox", sx: { mb: 1 } }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 1 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6, sm: 3 }, /* @__PURE__ */ React.createElement(Tooltip, { title: "What is your deployment frequency?" }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "Run Frequency"), /* @__PURE__ */ React.createElement(Typography, { variant: "h4" }, runFrequency(pipelineHistory)))))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6, sm: 3 }, /* @__PURE__ */ React.createElement(Tooltip, { title: "How long does it take to fix a failure?" }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "Mean Time to Recovery"), /* @__PURE__ */ React.createElement(Typography, { variant: "h4" }, meanTimeToRecovery(jobs)))))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6, sm: 3 }, /* @__PURE__ */ React.createElement(Tooltip, { title: "How often do changes fail?" }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "Mean Time Between Failures"), /* @__PURE__ */ React.createElement(Typography, { variant: "h4" }, meanTimeBetweenFailures(jobs)))))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6, sm: 3 }, /* @__PURE__ */ React.createElement(Tooltip, { title: "What percentage of changes result in a failure?" }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "Failure Rate"), /* @__PURE__ */ React.createElement(Typography, { variant: "h4" }, failureRateObj.title), /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, failureRateObj.subtitle)))))));
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
const useStyles = makeStyles(
|
|
342
|
-
(theme) => createStyles({
|
|
343
|
-
formControl: {
|
|
344
|
-
margin: theme.spacing(1),
|
|
345
|
-
minWidth: 120
|
|
346
|
-
},
|
|
347
|
-
selectEmpty: {
|
|
348
|
-
marginTop: theme.spacing(2)
|
|
349
|
-
}
|
|
350
|
-
})
|
|
351
|
-
);
|
|
352
|
-
const renderItems = (items) => {
|
|
353
|
-
return items.map((item) => {
|
|
354
|
-
return /* @__PURE__ */ React.createElement(MenuItem, { value: item.value, key: item.label }, item.label);
|
|
355
|
-
});
|
|
356
|
-
};
|
|
357
|
-
const SelectComponent = ({
|
|
358
|
-
value,
|
|
359
|
-
items,
|
|
360
|
-
label,
|
|
361
|
-
onChange
|
|
362
|
-
}) => {
|
|
363
|
-
const classes = useStyles();
|
|
364
|
-
const handleChange = (event) => {
|
|
365
|
-
const val = event.target.value;
|
|
366
|
-
onChange(val);
|
|
367
|
-
};
|
|
368
|
-
return /* @__PURE__ */ React.createElement(
|
|
369
|
-
FormControl,
|
|
370
|
-
{
|
|
371
|
-
variant: "outlined",
|
|
372
|
-
className: classes.formControl,
|
|
373
|
-
disabled: items.length === 0
|
|
374
|
-
},
|
|
375
|
-
/* @__PURE__ */ React.createElement(InputLabel, null, label),
|
|
376
|
-
/* @__PURE__ */ React.createElement(Select, { label, value, onChange: handleChange }, renderItems(items))
|
|
377
|
-
);
|
|
378
|
-
};
|
|
379
|
-
|
|
380
|
-
const GOCD_PIPELINES_ANNOTATION = "gocd.org/pipelines";
|
|
381
|
-
const isGoCdAvailable = (entity) => {
|
|
382
|
-
var _a;
|
|
383
|
-
return Boolean((_a = entity.metadata.annotations) == null ? void 0 : _a[GOCD_PIPELINES_ANNOTATION]);
|
|
384
|
-
};
|
|
385
|
-
const GoCdBuildsComponent = () => {
|
|
386
|
-
var _a, _b;
|
|
387
|
-
const { entity } = useEntity();
|
|
388
|
-
const config = useApi(configApiRef);
|
|
389
|
-
const rawPipelines = (_b = (_a = entity.metadata.annotations) == null ? void 0 : _a[GOCD_PIPELINES_ANNOTATION]) == null ? void 0 : _b.split(",").map((p) => p.trim());
|
|
390
|
-
const gocdApi = useApi(gocdApiRef);
|
|
391
|
-
const [selectedPipeline, setSelectedPipeline] = useState(
|
|
392
|
-
rawPipelines ? rawPipelines[0] : ""
|
|
393
|
-
);
|
|
394
|
-
const {
|
|
395
|
-
value: pipelineHistory,
|
|
396
|
-
loading,
|
|
397
|
-
error
|
|
398
|
-
} = useAsync(async () => {
|
|
399
|
-
return await gocdApi.getPipelineHistory(selectedPipeline);
|
|
400
|
-
}, [selectedPipeline]);
|
|
401
|
-
const onSelectedPipelineChanged = (pipeline) => {
|
|
402
|
-
setSelectedPipeline(pipeline);
|
|
403
|
-
};
|
|
404
|
-
const getSelectionItems = (pipelines) => {
|
|
405
|
-
return pipelines.map((p) => ({ label: p, value: p }));
|
|
406
|
-
};
|
|
407
|
-
function isError(apiResult) {
|
|
408
|
-
return (apiResult == null ? void 0 : apiResult.message) !== void 0;
|
|
409
|
-
}
|
|
410
|
-
if (!rawPipelines) {
|
|
411
|
-
return /* @__PURE__ */ React.createElement(MissingAnnotationEmptyState, { annotation: GOCD_PIPELINES_ANNOTATION });
|
|
412
|
-
}
|
|
413
|
-
if (isError(pipelineHistory)) {
|
|
414
|
-
return /* @__PURE__ */ React.createElement(
|
|
415
|
-
EmptyState,
|
|
416
|
-
{
|
|
417
|
-
title: "GoCD pipelines",
|
|
418
|
-
description: `Could not fetch pipelines defined for entity ${entity.metadata.name}. Error: ${pipelineHistory.message}`,
|
|
419
|
-
missing: "content"
|
|
420
|
-
}
|
|
421
|
-
);
|
|
422
|
-
}
|
|
423
|
-
if (!loading && !pipelineHistory) {
|
|
424
|
-
return /* @__PURE__ */ React.createElement(
|
|
425
|
-
EmptyState,
|
|
426
|
-
{
|
|
427
|
-
title: "GoCD pipelines",
|
|
428
|
-
description: `We could not find pipelines defined for entity ${entity.metadata.name}.`,
|
|
429
|
-
missing: "data"
|
|
430
|
-
}
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
return /* @__PURE__ */ React.createElement(Page, { themeId: "tool" }, /* @__PURE__ */ React.createElement(Content, { noPadding: true }, /* @__PURE__ */ React.createElement(ContentHeader, { title: entity.metadata.name }, /* @__PURE__ */ React.createElement(
|
|
434
|
-
SelectComponent,
|
|
435
|
-
{
|
|
436
|
-
value: selectedPipeline,
|
|
437
|
-
onChange: (pipeline) => onSelectedPipelineChanged(pipeline),
|
|
438
|
-
label: "Pipeline",
|
|
439
|
-
items: getSelectionItems(rawPipelines)
|
|
440
|
-
}
|
|
441
|
-
)), /* @__PURE__ */ React.createElement(
|
|
442
|
-
GoCdBuildsInsights,
|
|
443
|
-
{
|
|
444
|
-
pipelineHistory,
|
|
445
|
-
loading,
|
|
446
|
-
error
|
|
447
|
-
}
|
|
448
|
-
), /* @__PURE__ */ React.createElement(
|
|
449
|
-
GoCdBuildsTable,
|
|
450
|
-
{
|
|
451
|
-
goCdBaseUrl: config.getString("gocd.baseUrl"),
|
|
452
|
-
pipelineHistory,
|
|
453
|
-
loading,
|
|
454
|
-
error
|
|
455
|
-
}
|
|
456
|
-
)));
|
|
457
|
-
};
|
|
458
|
-
|
|
459
|
-
export { EntityGoCdContent as E, GoCdBuildsComponent as G, GOCD_PIPELINES_ANNOTATION as a, gocdPlugin as g, isGoCdAvailable as i };
|
|
460
|
-
//# sourceMappingURL=index-CMeyzgrA.esm.js.map
|