@backstage-community/plugin-multi-source-security-viewer 0.1.1
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 +12 -0
- package/README.md +253 -0
- package/dist/api/github.esm.js +114 -0
- package/dist/api/github.esm.js.map +1 -0
- package/dist/api/gitlab.esm.js +84 -0
- package/dist/api/gitlab.esm.js.map +1 -0
- package/dist/api/jenkins.esm.js +119 -0
- package/dist/api/jenkins.esm.js.map +1 -0
- package/dist/components/DialogLauncher/DialogLauncher.esm.js +27 -0
- package/dist/components/DialogLauncher/DialogLauncher.esm.js.map +1 -0
- package/dist/components/EmptyState/EmptyState.esm.js +51 -0
- package/dist/components/EmptyState/EmptyState.esm.js.map +1 -0
- package/dist/components/EmptyState/EmptyStateNoData.esm.js +15 -0
- package/dist/components/EmptyState/EmptyStateNoData.esm.js.map +1 -0
- package/dist/components/EmptyState/EmptyStateNoMatch.esm.js +16 -0
- package/dist/components/EmptyState/EmptyStateNoMatch.esm.js.map +1 -0
- package/dist/components/EmptyState/EmptyStateSpinner.esm.js +19 -0
- package/dist/components/EmptyState/EmptyStateSpinner.esm.js.map +1 -0
- package/dist/components/Icons/CriticalIcon.esm.js +18 -0
- package/dist/components/Icons/CriticalIcon.esm.js.map +1 -0
- package/dist/components/Icons/EqualsIcon.esm.js +18 -0
- package/dist/components/Icons/EqualsIcon.esm.js.map +1 -0
- package/dist/components/Icons/IconWithValue.esm.js +16 -0
- package/dist/components/Icons/IconWithValue.esm.js.map +1 -0
- package/dist/components/Icons/LogsIcon.esm.js +33 -0
- package/dist/components/Icons/LogsIcon.esm.js.map +1 -0
- package/dist/components/Icons/OutputIcon.esm.js +30 -0
- package/dist/components/Icons/OutputIcon.esm.js.map +1 -0
- package/dist/components/Icons/SBOMLink.esm.js +32 -0
- package/dist/components/Icons/SBOMLink.esm.js.map +1 -0
- package/dist/components/PermissionAlert/PermissionAlert.esm.js +9 -0
- package/dist/components/PermissionAlert/PermissionAlert.esm.js.map +1 -0
- package/dist/components/PipelineRunList/PipelineRunList.esm.js +72 -0
- package/dist/components/PipelineRunList/PipelineRunList.esm.js.map +1 -0
- package/dist/components/PipelineRunList/PipelineRunOutput.esm.js +23 -0
- package/dist/components/PipelineRunList/PipelineRunOutput.esm.js.map +1 -0
- package/dist/components/PipelineRunList/PipelineRunSBOMLink.esm.js +51 -0
- package/dist/components/PipelineRunList/PipelineRunSBOMLink.esm.js.map +1 -0
- package/dist/components/PipelineRunList/PipelineRunTable.esm.js +32 -0
- package/dist/components/PipelineRunList/PipelineRunTable.esm.js.map +1 -0
- package/dist/components/PipelineRunList/PipelineRunTableBody.esm.js +12 -0
- package/dist/components/PipelineRunList/PipelineRunTableBody.esm.js.map +1 -0
- package/dist/components/PipelineRunList/PipelineRunTableColumns.esm.js +50 -0
- package/dist/components/PipelineRunList/PipelineRunTableColumns.esm.js.map +1 -0
- package/dist/components/PipelineRunList/PipelineRunTablePagination.esm.js +130 -0
- package/dist/components/PipelineRunList/PipelineRunTablePagination.esm.js.map +1 -0
- package/dist/components/PipelineRunList/PipelineRunTableRow.esm.js +81 -0
- package/dist/components/PipelineRunList/PipelineRunTableRow.esm.js.map +1 -0
- package/dist/components/PipelineRunList/PipelineRunTableRowActions.esm.js +87 -0
- package/dist/components/PipelineRunList/PipelineRunTableRowActions.esm.js.map +1 -0
- package/dist/components/PipelineRunList/PipelineRunToolbar.esm.js +76 -0
- package/dist/components/PipelineRunList/PipelineRunToolbar.esm.js.map +1 -0
- package/dist/components/PipelineRunLogs/PipelineRunLogs.esm.js +34 -0
- package/dist/components/PipelineRunLogs/PipelineRunLogs.esm.js.map +1 -0
- package/dist/components/PipelineRunLogs/PipelineRunLogsDownloader.esm.js +46 -0
- package/dist/components/PipelineRunLogs/PipelineRunLogsDownloader.esm.js.map +1 -0
- package/dist/components/PipelineRunLogs/PipelineRunStepViewer.esm.js +19 -0
- package/dist/components/PipelineRunLogs/PipelineRunStepViewer.esm.js.map +1 -0
- package/dist/components/PipelineRunLogs/PipelineRunStepper.esm.js +19 -0
- package/dist/components/PipelineRunLogs/PipelineRunStepper.esm.js.map +1 -0
- package/dist/components/Router.esm.js +72 -0
- package/dist/components/Router.esm.js.map +1 -0
- package/dist/components/SecurityViewer/SecurityViewerGithubActionsDetail.esm.js +19 -0
- package/dist/components/SecurityViewer/SecurityViewerGithubActionsDetail.esm.js.map +1 -0
- package/dist/components/SecurityViewer/SecurityViewerJenkinsDetail.esm.js +19 -0
- package/dist/components/SecurityViewer/SecurityViewerJenkinsDetail.esm.js.map +1 -0
- package/dist/components/SecurityViewer/SecurityViewerMultiCIPipelines.esm.js +52 -0
- package/dist/components/SecurityViewer/SecurityViewerMultiCIPipelines.esm.js.map +1 -0
- package/dist/components/SecurityViewer/SecurityViewerPipelineDetailList.esm.js +31 -0
- package/dist/components/SecurityViewer/SecurityViewerPipelineDetailList.esm.js.map +1 -0
- package/dist/components/SecurityViewer/SecurityViewerPipelineSummaryList.esm.js +32 -0
- package/dist/components/SecurityViewer/SecurityViewerPipelineSummaryList.esm.js.map +1 -0
- package/dist/components/SecurityViewer/SecurityViewerTabbedMultiCISummaryList.esm.js +26 -0
- package/dist/components/SecurityViewer/SecurityViewerTabbedMultiCISummaryList.esm.js.map +1 -0
- package/dist/hooks/useDarkTheme.esm.js +21 -0
- package/dist/hooks/useDarkTheme.esm.js.map +1 -0
- package/dist/hooks/useMssvViewPermisson.esm.js +12 -0
- package/dist/hooks/useMssvViewPermisson.esm.js.map +1 -0
- package/dist/hooks/usePipelineDetail.esm.js +39 -0
- package/dist/hooks/usePipelineDetail.esm.js.map +1 -0
- package/dist/hooks/usePipelineSummary.esm.js +38 -0
- package/dist/hooks/usePipelineSummary.esm.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/models/pipelineRunResult.esm.js +99 -0
- package/dist/models/pipelineRunResult.esm.js.map +1 -0
- package/dist/plugin.esm.js +81 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/routes.esm.js +18 -0
- package/dist/routes.esm.js.map +1 -0
- package/dist/types/pipelinerun.esm.js +42 -0
- package/dist/types/pipelinerun.esm.js.map +1 -0
- package/dist/utils/logs.esm.js +35 -0
- package/dist/utils/logs.esm.js.map +1 -0
- package/package.json +95 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# @backstage-community/plugin-multi-source-security-viewer
|
|
2
|
+
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fda8237: Add multi-source security viewer plugin
|
|
8
|
+
|
|
9
|
+
- displays security information for github actions, jenkins, and gitlab.
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [fda8237]
|
|
12
|
+
- @backstage-community/plugin-multi-source-security-viewer-common@0.1.1
|
package/README.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Multi Source Security Viewer
|
|
2
|
+
|
|
3
|
+
This plugin enables you to visualize pipeline security information from multiple sources.
|
|
4
|
+
|
|
5
|
+
## Setting up the plugin
|
|
6
|
+
|
|
7
|
+
### For adminstrators
|
|
8
|
+
|
|
9
|
+
#### Prerequisites
|
|
10
|
+
|
|
11
|
+
1. Install the CI providers you want the plugin to handle the security scan outputs from.
|
|
12
|
+
The supported CI's are:
|
|
13
|
+
|
|
14
|
+
- [Jenkins](../../../jenkins/plugins/jenkins-backend/README.md)
|
|
15
|
+
- [Github Actions](../../../github-actions/plugins/github-actions/README.md)
|
|
16
|
+
- [Gitlab CI](https://github.com/immobiliare/backstage-plugin-gitlab?tab=readme-ov-file#setup)
|
|
17
|
+
|
|
18
|
+
#### Procedure
|
|
19
|
+
|
|
20
|
+
1. Install the plugin using the following command:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
yarn workspace app add @backstage-community/multi-source-security-viewer
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
To enable the PipelineRun list in the Security tab on the entity view page, add the following snippet in the packages/app/src/components/catalog/EntityPage.tsx.
|
|
27
|
+
|
|
28
|
+
```diff
|
|
29
|
+
+import {
|
|
30
|
+
+ isMultiCIAvailable,
|
|
31
|
+
+ EntityMultiCIPipelinesContent,
|
|
32
|
+
+} from '@backstage-community/plugin-multi-source-security-viewer';
|
|
33
|
+
+
|
|
34
|
+
+import { EntityJenkinsContent } from '@backstage-community/plugin-jenkins';
|
|
35
|
+
+import { EntityGithubActionsContent } from '@backstage-community/plugin-github-actions';
|
|
36
|
+
+
|
|
37
|
+
+const securityContent = (
|
|
38
|
+
+ <EntitySwitch>
|
|
39
|
+
+ <EntitySwitch.Case if={isMultiCIAvailable}>
|
|
40
|
+
+ <EntityMultiCIPipelinesContent />
|
|
41
|
+
+ </EntitySwitch.Case>
|
|
42
|
+
+ </EntitySwitch>
|
|
43
|
+
+);
|
|
44
|
+
+
|
|
45
|
+
+const entityServicePage = (
|
|
46
|
+
+ <EntityLayout.Route path="/security" title="Security">
|
|
47
|
+
+ {securityContent}
|
|
48
|
+
+ </EntityLayout.Route>
|
|
49
|
+
+);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## For users
|
|
53
|
+
|
|
54
|
+
### Using the plugin in Backstage
|
|
55
|
+
|
|
56
|
+
#### Prerequisites
|
|
57
|
+
|
|
58
|
+
1. Your backstage application is installed and running.
|
|
59
|
+
2. You have installed the Multi Source Security Viewer plugin.
|
|
60
|
+
3. Ensure your CI annotations are set:
|
|
61
|
+
|
|
62
|
+
- [Jenkins](../../../jenkins/plugins/jenkins/README.md)
|
|
63
|
+
- [Github Actions]([../../../github-actions/plugins/github-actions/README.md)
|
|
64
|
+
- [Gitlab CI](https://github.com/immobiliare/backstage-plugin-gitlab?tab=readme-ov-file#annotations)
|
|
65
|
+
|
|
66
|
+
### Procedure
|
|
67
|
+
|
|
68
|
+
1. Open your Backstage application and select a component from the **Catalog** page.
|
|
69
|
+
2. Go to the **Security** tab.
|
|
70
|
+
|
|
71
|
+
The **Security** tab displays the list of Pipeline Runs from your CI's based on the component annotations.
|
|
72
|
+
|
|
73
|
+

|
|
74
|
+
|
|
75
|
+
The table provides a visual representation of identified vulnerabilities in the OCI image produced by the pipelinerun.
|
|
76
|
+
The Author of the pipeline scanner task would provide the CVE summary data using the below format that the UI can interpret.
|
|
77
|
+
|
|
78
|
+
### Actions buttons
|
|
79
|
+
|
|
80
|
+
#### SBOM
|
|
81
|
+
|
|
82
|
+
Link to SBOM action will be enabled if there is a SBOM task in the pipeline or a external link if provided by the [eye catcher](#format).
|
|
83
|
+
The sbom is captured from the `show-sbom-rhdh` [step](https://github.com/redhat-appstudio/tssc-dev-multi-ci/blob/main/rhtap/show-sbom-rhdh.sh).
|
|
84
|
+
|
|
85
|
+
#### Logs
|
|
86
|
+
|
|
87
|
+
Pipeline steps and their logs are visible if steps have been identified.
|
|
88
|
+
|
|
89
|
+

|
|
90
|
+
|
|
91
|
+
#### Output
|
|
92
|
+
|
|
93
|
+
Output action will be enabled when the pipeline emits some results and/or contains its [eye catchers](#format).
|
|
94
|
+
|
|
95
|
+
This action opens a modal where it will render the reports for Enterprise contract and Advanced cluster security. The report data should be exposed via logs through EYE_CATCHERS.
|
|
96
|
+
|
|
97
|
+
Supported EYE_CATCHERS:
|
|
98
|
+
|
|
99
|
+
- ACS_IMAGE_SCAN_EYECATCHER_BEGIN
|
|
100
|
+
- ACS_IMAGE_SCAN_EYECATCHER_END
|
|
101
|
+
- ACS_IMAGE_CHECK_EYECATCHER_BEGIN
|
|
102
|
+
- ACS_IMAGE_CHECK_EYECATCHER_END
|
|
103
|
+
- ACS_DEPLOY_EYECATCHER_BEGIN
|
|
104
|
+
- ACS_DEPLOY_EYECATCHER_END
|
|
105
|
+
- EC_EYECATCHER_BEGIN
|
|
106
|
+
- EC_EYECATCHER_END
|
|
107
|
+
|
|
108
|
+

|
|
109
|
+
|
|
110
|
+
### Format:
|
|
111
|
+
|
|
112
|
+
The data is extracted from logs. The steps are identified with `Step : <ANY>`.
|
|
113
|
+
|
|
114
|
+
eg: `Step: cosign-sign-attest`.
|
|
115
|
+
|
|
116
|
+
The logs button will be disabled if no steps are found.
|
|
117
|
+
|
|
118
|
+
Scan results are extracted between EYE_CATCHERS as json.
|
|
119
|
+
|
|
120
|
+
eg: from `ACS_IMAGE_SCAN_EYECATCHER_BEGIN` to `ACS_IMAGE_SCAN_EYECATCHER_END`
|
|
121
|
+
|
|
122
|
+
Log format example:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
Step: image-scan
|
|
126
|
+
some-logs
|
|
127
|
+
...
|
|
128
|
+
...
|
|
129
|
+
...
|
|
130
|
+
ACS_IMAGE_SCAN_EYECATCHER_BEGIN
|
|
131
|
+
{
|
|
132
|
+
results: [
|
|
133
|
+
{
|
|
134
|
+
summary: {
|
|
135
|
+
CRITICAL: 0,
|
|
136
|
+
HIGH: 1,
|
|
137
|
+
LOW: 1,
|
|
138
|
+
MEDIUM: 0,
|
|
139
|
+
TOTAL: 2,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
ACS_IMAGE_SCAN_EYECATCHER_END
|
|
145
|
+
...
|
|
146
|
+
...
|
|
147
|
+
some-logs
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The above will populate the pipeline run table with its vulnerabilities summary.
|
|
151
|
+
|
|
152
|
+
### Enterprise contract Task [Optional]:
|
|
153
|
+
|
|
154
|
+
Create a CI step that captures the JSON output between the EC_EYECATCHER:
|
|
155
|
+
|
|
156
|
+
See [example](https://github.com/redhat-appstudio/tssc-dev-multi-ci/blob/main/rhtap/verify-enterprise-contract.sh) script.
|
|
157
|
+
|
|
158
|
+
[Example output](./src/__fixtures__/ec.ts)
|
|
159
|
+
|
|
160
|
+
### ACS Image scan Task [Optional]:
|
|
161
|
+
|
|
162
|
+
Create a CI step that captures the JSON output between the ACS_IMAGE_SCAN_EYECATCHER:
|
|
163
|
+
|
|
164
|
+
See [example](https://github.com/redhat-appstudio/tssc-dev-multi-ci/blob/main/rhtap/acs-image-scan.sh) script.
|
|
165
|
+
|
|
166
|
+
[Example output](./src/__fixtures__/acsimagescanresult.ts)
|
|
167
|
+
|
|
168
|
+
### ACS Image check Task [Optional]:
|
|
169
|
+
|
|
170
|
+
Create a CI step that captures the JSON output between the ACS_IMAGE_CHECK_EYECATCHER:
|
|
171
|
+
|
|
172
|
+
See [example](https://github.com/redhat-appstudio/tssc-dev-multi-ci/blob/main/rhtap/acs-image-check.sh) script.
|
|
173
|
+
|
|
174
|
+
[Example output](./src/__fixtures__/acsimagecheckresults.ts)
|
|
175
|
+
|
|
176
|
+
### ACS Deployment check Task [Optional]:
|
|
177
|
+
|
|
178
|
+
Create a CI step that captures the JSON output between the ACS_DEPLOY_EYECATCHER:
|
|
179
|
+
|
|
180
|
+
See [example](https://github.com/redhat-appstudio/tssc-dev-multi-ci/blob/main/rhtap/acs-deploy-check.sh) script.
|
|
181
|
+
|
|
182
|
+
[Example output](./src/__fixtures__/acsdeploymentcheckresults.ts)
|
|
183
|
+
|
|
184
|
+
## Additional configuration
|
|
185
|
+
|
|
186
|
+
### For adminstrators
|
|
187
|
+
|
|
188
|
+
#### Permission Framework Support
|
|
189
|
+
|
|
190
|
+
The Multi Source Security Viewer (MSSV) plugin has support for the permission framework.
|
|
191
|
+
|
|
192
|
+
1. When [RBAC permission](https://github.com/backstage/community-plugins/tree/main/workspaces/rbac/plugins/rbac-backend#installation) framework is enabled, for non-admin users to access MSSV UI, the role associated with your user should have the following permission policies associated with it.
|
|
193
|
+
Add the following in your permission policies configuration file named `rbac-policy.csv`:
|
|
194
|
+
|
|
195
|
+
```CSV
|
|
196
|
+
p, role:default/team_a, mssv.view.read, read, allow
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
You can specify the path to this configuration file in your application configuration:
|
|
200
|
+
|
|
201
|
+
```yaml
|
|
202
|
+
permission:
|
|
203
|
+
enabled: true
|
|
204
|
+
rbac:
|
|
205
|
+
policies-csv-file: /some/path/rbac-policy.csv
|
|
206
|
+
policyFileReload: true
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
2. When using the [permission policy](https://backstage.io/docs/permissions/writing-a-policy/) framework.
|
|
210
|
+
|
|
211
|
+
Configure the backend by [configuring the permission policy](https://github.com/backstage/backstage/blob/master/docs/permissions/getting-started.md#test-permission-policy). You can use below policy as example.
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { createBackendModule } from '@backstage/backend-plugin-api';
|
|
215
|
+
import {
|
|
216
|
+
PolicyDecision,
|
|
217
|
+
AuthorizeResult,
|
|
218
|
+
} from '@backstage/plugin-permission-common';
|
|
219
|
+
import {
|
|
220
|
+
PermissionPolicy,
|
|
221
|
+
PolicyQuery,
|
|
222
|
+
PolicyQueryUser,
|
|
223
|
+
} from '@backstage/plugin-permission-node';
|
|
224
|
+
import { policyExtensionPoint } from '@backstage/plugin-permission-node/alpha';
|
|
225
|
+
|
|
226
|
+
class MssvPermissionPolicy implements PermissionPolicy {
|
|
227
|
+
async handle(
|
|
228
|
+
request: PolicyQuery,
|
|
229
|
+
user: PolicyQueryUser,
|
|
230
|
+
): Promise<PolicyDecision> {
|
|
231
|
+
const isGuestUser = user.info.userEntityRef === 'user:development/guest';
|
|
232
|
+
if (isGuestUser && request.permission.name === 'mssv.view.read') {
|
|
233
|
+
return { result: AuthorizeResult.DENY };
|
|
234
|
+
}
|
|
235
|
+
return { result: AuthorizeResult.ALLOW };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export default createBackendModule({
|
|
240
|
+
pluginId: 'permission',
|
|
241
|
+
moduleId: 'permission-policy',
|
|
242
|
+
register(reg) {
|
|
243
|
+
reg.registerInit({
|
|
244
|
+
deps: { policy: policyExtensionPoint },
|
|
245
|
+
async init({ policy }) {
|
|
246
|
+
policy.setPolicy(new MssvPermissionPolicy());
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+

|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { GITHUB_ACTIONS_ANNOTATION, BuildStatus } from '@backstage-community/plugin-github-actions';
|
|
2
|
+
import { createApiRef } from '@backstage/core-plugin-api';
|
|
3
|
+
import { PipelineRunResult } from '../models/pipelineRunResult.esm.js';
|
|
4
|
+
import { RunStatus } from '../types/pipelinerun.esm.js';
|
|
5
|
+
|
|
6
|
+
const mssvGithubActionsApiRef = createApiRef({
|
|
7
|
+
id: "plugin.mssv-api-githubactions.service"
|
|
8
|
+
});
|
|
9
|
+
const mapStatus = (status) => {
|
|
10
|
+
const runStatus = BuildStatus[status];
|
|
11
|
+
switch (runStatus) {
|
|
12
|
+
case BuildStatus.success:
|
|
13
|
+
return RunStatus.Succeeded;
|
|
14
|
+
case BuildStatus.failure:
|
|
15
|
+
return RunStatus.Failed;
|
|
16
|
+
case BuildStatus.pending:
|
|
17
|
+
return RunStatus.Pending;
|
|
18
|
+
case BuildStatus.running:
|
|
19
|
+
return RunStatus.Running;
|
|
20
|
+
default:
|
|
21
|
+
return RunStatus.Unknown;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
class MssvGithubActionsClient {
|
|
25
|
+
githubActionsApi;
|
|
26
|
+
constructor(options) {
|
|
27
|
+
this.githubActionsApi = options.githubActionsApi;
|
|
28
|
+
}
|
|
29
|
+
async getPipelineSummary(options) {
|
|
30
|
+
const { entity, page, pageSize } = options;
|
|
31
|
+
const [owner, repo] = (entity?.metadata.annotations?.[GITHUB_ACTIONS_ANNOTATION] ?? "/").split("/");
|
|
32
|
+
if (!owner || !repo) {
|
|
33
|
+
return Promise.reject("No repo/owner provided");
|
|
34
|
+
}
|
|
35
|
+
const project = await this.githubActionsApi.listWorkflowRuns({
|
|
36
|
+
owner,
|
|
37
|
+
repo,
|
|
38
|
+
page: page + 1,
|
|
39
|
+
// non zero-based
|
|
40
|
+
pageSize
|
|
41
|
+
});
|
|
42
|
+
const projectWithLogs = await Promise.all(
|
|
43
|
+
project.workflow_runs.map(async (run) => {
|
|
44
|
+
const jobsList = await this.githubActionsApi.listJobsForWorkflowRun({
|
|
45
|
+
owner,
|
|
46
|
+
repo,
|
|
47
|
+
id: run.id
|
|
48
|
+
});
|
|
49
|
+
const logs = await Promise.all(
|
|
50
|
+
jobsList.jobs.map(async (job) => {
|
|
51
|
+
return await this.githubActionsApi.downloadJobLogsForWorkflowRun({
|
|
52
|
+
owner,
|
|
53
|
+
repo,
|
|
54
|
+
runId: job.id
|
|
55
|
+
}).catch(() => "");
|
|
56
|
+
})
|
|
57
|
+
).then((res) => res.join(" "));
|
|
58
|
+
const status = mapStatus(run?.status ?? "UNKNOWN");
|
|
59
|
+
return {
|
|
60
|
+
run,
|
|
61
|
+
logs,
|
|
62
|
+
status
|
|
63
|
+
};
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
const results = projectWithLogs.map(
|
|
67
|
+
(pr) => new PipelineRunResult({
|
|
68
|
+
id: pr?.run?.id.toString(),
|
|
69
|
+
displayName: pr?.run?.display_title,
|
|
70
|
+
status: pr?.status,
|
|
71
|
+
logs: pr?.logs
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
return { results, totalCount: project?.total_count ?? 0 };
|
|
75
|
+
}
|
|
76
|
+
async getPipelineDetail(options) {
|
|
77
|
+
const { entity, ref } = options;
|
|
78
|
+
const [owner, repo] = (entity?.metadata.annotations?.[GITHUB_ACTIONS_ANNOTATION] ?? "/").split("/");
|
|
79
|
+
if (!owner || !repo) {
|
|
80
|
+
return Promise.reject("No repo/owner provided");
|
|
81
|
+
}
|
|
82
|
+
const run = await this.githubActionsApi.getWorkflowRun({
|
|
83
|
+
owner,
|
|
84
|
+
repo,
|
|
85
|
+
id: parseInt(ref, 10)
|
|
86
|
+
});
|
|
87
|
+
const jobsList = await this.githubActionsApi.listJobsForWorkflowRun({
|
|
88
|
+
owner,
|
|
89
|
+
repo,
|
|
90
|
+
id: parseInt(ref, 10)
|
|
91
|
+
});
|
|
92
|
+
const logs = await Promise.all(
|
|
93
|
+
jobsList.jobs.map(async (job) => {
|
|
94
|
+
return await this.githubActionsApi.downloadJobLogsForWorkflowRun({
|
|
95
|
+
owner,
|
|
96
|
+
repo,
|
|
97
|
+
runId: job.id
|
|
98
|
+
}).catch(() => "");
|
|
99
|
+
})
|
|
100
|
+
).then((res) => res.join(" "));
|
|
101
|
+
const results = [
|
|
102
|
+
new PipelineRunResult({
|
|
103
|
+
id: run?.id.toString(),
|
|
104
|
+
displayName: run?.display_title,
|
|
105
|
+
status: mapStatus(run?.status ?? "UNKNOWN"),
|
|
106
|
+
logs
|
|
107
|
+
})
|
|
108
|
+
];
|
|
109
|
+
return { results, totalCount: results?.length ?? 0 };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export { MssvGithubActionsClient, mssvGithubActionsApiRef };
|
|
114
|
+
//# sourceMappingURL=github.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.esm.js","sources":["../../src/api/github.ts"],"sourcesContent":["/*\n * Copyright 2024 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 */\nimport {\n GithubActionsApi,\n GITHUB_ACTIONS_ANNOTATION,\n BuildStatus,\n} from '@backstage-community/plugin-github-actions';\nimport { MssvApi, MssvApiResponse } from './mssv';\nimport { Entity } from '@backstage/catalog-model';\nimport { createApiRef } from '@backstage/core-plugin-api';\nimport { PipelineRunResult } from '../models/pipelineRunResult';\nimport { RunStatus } from '../types/pipelinerun';\n\n// Apiref to map with client\nexport const mssvGithubActionsApiRef = createApiRef<MssvApi>({\n id: 'plugin.mssv-api-githubactions.service',\n});\n\nconst mapStatus = (status: string): RunStatus => {\n const runStatus = BuildStatus[status as keyof typeof BuildStatus];\n switch (runStatus) {\n case BuildStatus.success:\n return RunStatus.Succeeded;\n case BuildStatus.failure:\n return RunStatus.Failed;\n case BuildStatus.pending:\n return RunStatus.Pending;\n case BuildStatus.running:\n return RunStatus.Running;\n default:\n return RunStatus.Unknown;\n }\n};\n\nexport class MssvGithubActionsClient implements MssvApi {\n private readonly githubActionsApi: GithubActionsApi;\n\n constructor(options: { githubActionsApi: GithubActionsApi }) {\n this.githubActionsApi = options.githubActionsApi;\n }\n\n async getPipelineSummary(options: {\n entity: Entity;\n page: number;\n pageSize: number;\n }): Promise<MssvApiResponse> {\n const { entity, page, pageSize } = options;\n const [owner, repo] = (\n entity?.metadata.annotations?.[GITHUB_ACTIONS_ANNOTATION] ?? '/'\n ).split('/');\n\n if (!owner || !repo) {\n return Promise.reject('No repo/owner provided');\n }\n\n const project = await this.githubActionsApi.listWorkflowRuns({\n owner,\n repo,\n page: page + 1, // non zero-based\n pageSize,\n });\n\n const projectWithLogs = await Promise.all(\n project.workflow_runs.map(async run => {\n const jobsList = await this.githubActionsApi.listJobsForWorkflowRun({\n owner,\n repo,\n id: run.id,\n });\n\n const logs = await Promise.all(\n jobsList.jobs.map(async job => {\n return await this.githubActionsApi\n .downloadJobLogsForWorkflowRun({\n owner,\n repo,\n runId: job.id,\n })\n .catch(() => ''); // fallback on empty string. It disables logs button and updates tooltip\n }),\n ).then(res => res.join(' ')); // return as one string\n\n const status = mapStatus(run?.status ?? 'UNKNOWN');\n\n return {\n run,\n logs,\n status,\n };\n }),\n );\n\n const results = projectWithLogs.map(\n pr =>\n new PipelineRunResult({\n id: pr?.run?.id.toString(),\n displayName: pr?.run?.display_title,\n status: pr?.status,\n logs: pr?.logs,\n }),\n );\n\n return { results, totalCount: project?.total_count ?? 0 };\n }\n\n async getPipelineDetail(options: {\n entity: Entity;\n ref: string;\n page: number;\n pageSize: number;\n }): Promise<MssvApiResponse> {\n const { entity, ref } = options;\n const [owner, repo] = (\n entity?.metadata.annotations?.[GITHUB_ACTIONS_ANNOTATION] ?? '/'\n ).split('/');\n\n if (!owner || !repo) {\n return Promise.reject('No repo/owner provided');\n }\n\n const run = await this.githubActionsApi.getWorkflowRun({\n owner,\n repo,\n id: parseInt(ref, 10),\n });\n\n const jobsList = await this.githubActionsApi.listJobsForWorkflowRun({\n owner,\n repo,\n id: parseInt(ref, 10),\n });\n\n const logs = await Promise.all(\n jobsList.jobs.map(async job => {\n return await this.githubActionsApi\n .downloadJobLogsForWorkflowRun({\n owner,\n repo,\n runId: job.id,\n })\n .catch(() => ''); // fallback on empty string. It disables logs button and updates tooltip\n }),\n ).then(res => res.join(' '));\n\n // This a detail view where only 1 item is expected\n const results = [\n new PipelineRunResult({\n id: run?.id.toString(),\n displayName: run?.display_title,\n status: mapStatus(run?.status ?? 'UNKNOWN'),\n logs,\n }),\n ];\n\n return { results, totalCount: results?.length ?? 0 };\n }\n}\n"],"names":[],"mappings":";;;;;AA2BO,MAAM,0BAA0B,YAAsB,CAAA;AAAA,EAC3D,EAAI,EAAA;AACN,CAAC;AAED,MAAM,SAAA,GAAY,CAAC,MAA8B,KAAA;AAC/C,EAAM,MAAA,SAAA,GAAY,YAAY,MAAkC,CAAA;AAChE,EAAA,QAAQ,SAAW;AAAA,IACjB,KAAK,WAAY,CAAA,OAAA;AACf,MAAA,OAAO,SAAU,CAAA,SAAA;AAAA,IACnB,KAAK,WAAY,CAAA,OAAA;AACf,MAAA,OAAO,SAAU,CAAA,MAAA;AAAA,IACnB,KAAK,WAAY,CAAA,OAAA;AACf,MAAA,OAAO,SAAU,CAAA,OAAA;AAAA,IACnB,KAAK,WAAY,CAAA,OAAA;AACf,MAAA,OAAO,SAAU,CAAA,OAAA;AAAA,IACnB;AACE,MAAA,OAAO,SAAU,CAAA,OAAA;AAAA;AAEvB,CAAA;AAEO,MAAM,uBAA2C,CAAA;AAAA,EACrC,gBAAA;AAAA,EAEjB,YAAY,OAAiD,EAAA;AAC3D,IAAA,IAAA,CAAK,mBAAmB,OAAQ,CAAA,gBAAA;AAAA;AAClC,EAEA,MAAM,mBAAmB,OAII,EAAA;AAC3B,IAAA,MAAM,EAAE,MAAA,EAAQ,IAAM,EAAA,QAAA,EAAa,GAAA,OAAA;AACnC,IAAM,MAAA,CAAC,KAAO,EAAA,IAAI,CAChB,GAAA,CAAA,MAAA,EAAQ,QAAS,CAAA,WAAA,GAAc,yBAAyB,CAAA,IAAK,GAC7D,EAAA,KAAA,CAAM,GAAG,CAAA;AAEX,IAAI,IAAA,CAAC,KAAS,IAAA,CAAC,IAAM,EAAA;AACnB,MAAO,OAAA,OAAA,CAAQ,OAAO,wBAAwB,CAAA;AAAA;AAGhD,IAAA,MAAM,OAAU,GAAA,MAAM,IAAK,CAAA,gBAAA,CAAiB,gBAAiB,CAAA;AAAA,MAC3D,KAAA;AAAA,MACA,IAAA;AAAA,MACA,MAAM,IAAO,GAAA,CAAA;AAAA;AAAA,MACb;AAAA,KACD,CAAA;AAED,IAAM,MAAA,eAAA,GAAkB,MAAM,OAAQ,CAAA,GAAA;AAAA,MACpC,OAAQ,CAAA,aAAA,CAAc,GAAI,CAAA,OAAM,GAAO,KAAA;AACrC,QAAA,MAAM,QAAW,GAAA,MAAM,IAAK,CAAA,gBAAA,CAAiB,sBAAuB,CAAA;AAAA,UAClE,KAAA;AAAA,UACA,IAAA;AAAA,UACA,IAAI,GAAI,CAAA;AAAA,SACT,CAAA;AAED,QAAM,MAAA,IAAA,GAAO,MAAM,OAAQ,CAAA,GAAA;AAAA,UACzB,QAAS,CAAA,IAAA,CAAK,GAAI,CAAA,OAAM,GAAO,KAAA;AAC7B,YAAO,OAAA,MAAM,IAAK,CAAA,gBAAA,CACf,6BAA8B,CAAA;AAAA,cAC7B,KAAA;AAAA,cACA,IAAA;AAAA,cACA,OAAO,GAAI,CAAA;AAAA,aACZ,CAAA,CACA,KAAM,CAAA,MAAM,EAAE,CAAA;AAAA,WAClB;AAAA,UACD,IAAK,CAAA,CAAA,GAAA,KAAO,GAAI,CAAA,IAAA,CAAK,GAAG,CAAC,CAAA;AAE3B,QAAA,MAAM,MAAS,GAAA,SAAA,CAAU,GAAK,EAAA,MAAA,IAAU,SAAS,CAAA;AAEjD,QAAO,OAAA;AAAA,UACL,GAAA;AAAA,UACA,IAAA;AAAA,UACA;AAAA,SACF;AAAA,OACD;AAAA,KACH;AAEA,IAAA,MAAM,UAAU,eAAgB,CAAA,GAAA;AAAA,MAC9B,CAAA,EAAA,KACE,IAAI,iBAAkB,CAAA;AAAA,QACpB,EAAI,EAAA,EAAA,EAAI,GAAK,EAAA,EAAA,CAAG,QAAS,EAAA;AAAA,QACzB,WAAA,EAAa,IAAI,GAAK,EAAA,aAAA;AAAA,QACtB,QAAQ,EAAI,EAAA,MAAA;AAAA,QACZ,MAAM,EAAI,EAAA;AAAA,OACX;AAAA,KACL;AAEA,IAAA,OAAO,EAAE,OAAA,EAAS,UAAY,EAAA,OAAA,EAAS,eAAe,CAAE,EAAA;AAAA;AAC1D,EAEA,MAAM,kBAAkB,OAKK,EAAA;AAC3B,IAAM,MAAA,EAAE,MAAQ,EAAA,GAAA,EAAQ,GAAA,OAAA;AACxB,IAAM,MAAA,CAAC,KAAO,EAAA,IAAI,CAChB,GAAA,CAAA,MAAA,EAAQ,QAAS,CAAA,WAAA,GAAc,yBAAyB,CAAA,IAAK,GAC7D,EAAA,KAAA,CAAM,GAAG,CAAA;AAEX,IAAI,IAAA,CAAC,KAAS,IAAA,CAAC,IAAM,EAAA;AACnB,MAAO,OAAA,OAAA,CAAQ,OAAO,wBAAwB,CAAA;AAAA;AAGhD,IAAA,MAAM,GAAM,GAAA,MAAM,IAAK,CAAA,gBAAA,CAAiB,cAAe,CAAA;AAAA,MACrD,KAAA;AAAA,MACA,IAAA;AAAA,MACA,EAAA,EAAI,QAAS,CAAA,GAAA,EAAK,EAAE;AAAA,KACrB,CAAA;AAED,IAAA,MAAM,QAAW,GAAA,MAAM,IAAK,CAAA,gBAAA,CAAiB,sBAAuB,CAAA;AAAA,MAClE,KAAA;AAAA,MACA,IAAA;AAAA,MACA,EAAA,EAAI,QAAS,CAAA,GAAA,EAAK,EAAE;AAAA,KACrB,CAAA;AAED,IAAM,MAAA,IAAA,GAAO,MAAM,OAAQ,CAAA,GAAA;AAAA,MACzB,QAAS,CAAA,IAAA,CAAK,GAAI,CAAA,OAAM,GAAO,KAAA;AAC7B,QAAO,OAAA,MAAM,IAAK,CAAA,gBAAA,CACf,6BAA8B,CAAA;AAAA,UAC7B,KAAA;AAAA,UACA,IAAA;AAAA,UACA,OAAO,GAAI,CAAA;AAAA,SACZ,CAAA,CACA,KAAM,CAAA,MAAM,EAAE,CAAA;AAAA,OAClB;AAAA,MACD,IAAK,CAAA,CAAA,GAAA,KAAO,GAAI,CAAA,IAAA,CAAK,GAAG,CAAC,CAAA;AAG3B,IAAA,MAAM,OAAU,GAAA;AAAA,MACd,IAAI,iBAAkB,CAAA;AAAA,QACpB,EAAA,EAAI,GAAK,EAAA,EAAA,CAAG,QAAS,EAAA;AAAA,QACrB,aAAa,GAAK,EAAA,aAAA;AAAA,QAClB,MAAQ,EAAA,SAAA,CAAU,GAAK,EAAA,MAAA,IAAU,SAAS,CAAA;AAAA,QAC1C;AAAA,OACD;AAAA,KACH;AAEA,IAAA,OAAO,EAAE,OAAA,EAAS,UAAY,EAAA,OAAA,EAAS,UAAU,CAAE,EAAA;AAAA;AAEvD;;;;"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { GitlabCIClient } from '@immobiliarelabs/backstage-plugin-gitlab';
|
|
2
|
+
import { createApiRef } from '@backstage/core-plugin-api';
|
|
3
|
+
import { PipelineRunResult } from '../models/pipelineRunResult.esm.js';
|
|
4
|
+
import { GitlabPipelineStatus, RunStatus } from '../types/pipelinerun.esm.js';
|
|
5
|
+
|
|
6
|
+
const mssvGitlabCIApiRef = createApiRef({
|
|
7
|
+
id: "plugin.mssv-api-gitlabci.service"
|
|
8
|
+
});
|
|
9
|
+
const mapStatus = (status) => {
|
|
10
|
+
const runStatus = GitlabPipelineStatus[status];
|
|
11
|
+
switch (runStatus) {
|
|
12
|
+
case GitlabPipelineStatus.success:
|
|
13
|
+
return RunStatus.Succeeded;
|
|
14
|
+
case GitlabPipelineStatus.failed:
|
|
15
|
+
return RunStatus.Failed;
|
|
16
|
+
case GitlabPipelineStatus.pending:
|
|
17
|
+
return RunStatus.Pending;
|
|
18
|
+
case GitlabPipelineStatus.running:
|
|
19
|
+
return RunStatus.Running;
|
|
20
|
+
default:
|
|
21
|
+
return RunStatus.Unknown;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const GITLAB_ANNOTATION_PROJECT_ID = "gitlab.com/project-id";
|
|
25
|
+
const GITLAB_ANNOTATION_PROJECT_SLUG = "gitlab.com/project-slug";
|
|
26
|
+
const GITLAB_ANNOTATION_INSTANCE = "gitlab.com/instance";
|
|
27
|
+
class CustomGitlabCiClient extends GitlabCIClient {
|
|
28
|
+
constructor(options) {
|
|
29
|
+
super(options);
|
|
30
|
+
}
|
|
31
|
+
getPipelineJobs(projectId, pipelineId) {
|
|
32
|
+
return this.callApi(
|
|
33
|
+
`projects/${projectId}/pipelines/${pipelineId}/jobs`,
|
|
34
|
+
{}
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
getJobLogs(projectId, jobId) {
|
|
38
|
+
return this.callApi(`projects/${projectId}/jobs/${jobId}/trace`, {});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
class MssvGitlabCIClient {
|
|
42
|
+
gitlabCiApi;
|
|
43
|
+
constructor(options) {
|
|
44
|
+
this.gitlabCiApi = options.gitlabCiApi;
|
|
45
|
+
}
|
|
46
|
+
async getPipelineSummary(options) {
|
|
47
|
+
const { entity, page, pageSize } = options;
|
|
48
|
+
this.gitlabCiApi.gitlabInstance = entity.metadata.annotations?.[GITLAB_ANNOTATION_INSTANCE] ?? "gitlab.com";
|
|
49
|
+
const projectId = entity.metadata.annotations?.[GITLAB_ANNOTATION_PROJECT_ID];
|
|
50
|
+
const projectSlug = entity.metadata.annotations?.[GITLAB_ANNOTATION_PROJECT_SLUG];
|
|
51
|
+
const [sliceStart, sliceEnd] = [
|
|
52
|
+
page * pageSize,
|
|
53
|
+
page * pageSize + pageSize
|
|
54
|
+
];
|
|
55
|
+
const summary = await this.gitlabCiApi.getPipelineSummary(projectId ?? projectSlug) ?? [];
|
|
56
|
+
const summarySlice = summary.slice(sliceStart, sliceEnd);
|
|
57
|
+
const summaryWithLogs = await Promise.all(
|
|
58
|
+
summarySlice.map(async (run) => {
|
|
59
|
+
const pipelineJobs = await this.gitlabCiApi.getPipelineJobs(run.project_id, run.id) ?? [];
|
|
60
|
+
const logs = await Promise.all(
|
|
61
|
+
pipelineJobs.map(async (job) => {
|
|
62
|
+
return await this.gitlabCiApi.getJobLogs(run.project_id, job.id).catch(() => "");
|
|
63
|
+
}) ?? []
|
|
64
|
+
).then((res) => res.join(" "));
|
|
65
|
+
return { run, logs };
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
const results = summaryWithLogs.map(
|
|
69
|
+
(pr) => new PipelineRunResult({
|
|
70
|
+
id: pr?.run?.id.toString(),
|
|
71
|
+
displayName: pr?.run?.id.toString(),
|
|
72
|
+
status: mapStatus(pr?.run?.status ?? "UNKOWN"),
|
|
73
|
+
logs: pr?.logs
|
|
74
|
+
})
|
|
75
|
+
) || [];
|
|
76
|
+
return { results, totalCount: summary?.length ?? 0 };
|
|
77
|
+
}
|
|
78
|
+
async getPipelineDetail() {
|
|
79
|
+
return { results: [], totalCount: 0 };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export { CustomGitlabCiClient, MssvGitlabCIClient, mssvGitlabCIApiRef };
|
|
84
|
+
//# sourceMappingURL=gitlab.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitlab.esm.js","sources":["../../src/api/gitlab.ts"],"sourcesContent":["/*\n * Copyright 2024 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 */\nimport { GitlabCIClient } from '@immobiliarelabs/backstage-plugin-gitlab';\nimport { MssvApi, MssvApiResponse } from './mssv';\nimport { Entity } from '@backstage/catalog-model';\nimport {\n DiscoveryApi,\n IdentityApi,\n OAuthApi,\n createApiRef,\n} from '@backstage/core-plugin-api';\nimport { PipelineRunResult } from '../models/pipelineRunResult';\nimport { GitlabPipelineStatus, RunStatus } from '../types/pipelinerun';\n\n// Apiref to map with client\nexport const mssvGitlabCIApiRef = createApiRef<MssvApi>({\n id: 'plugin.mssv-api-gitlabci.service',\n});\n\nconst mapStatus = (status: string): RunStatus => {\n const runStatus =\n GitlabPipelineStatus[status as keyof typeof GitlabPipelineStatus];\n switch (runStatus) {\n case GitlabPipelineStatus.success:\n return RunStatus.Succeeded;\n case GitlabPipelineStatus.failed:\n return RunStatus.Failed;\n case GitlabPipelineStatus.pending:\n return RunStatus.Pending;\n case GitlabPipelineStatus.running:\n return RunStatus.Running;\n default:\n return RunStatus.Unknown;\n }\n};\n\nconst GITLAB_ANNOTATION_PROJECT_ID = 'gitlab.com/project-id';\nconst GITLAB_ANNOTATION_PROJECT_SLUG = 'gitlab.com/project-slug';\nconst GITLAB_ANNOTATION_INSTANCE = 'gitlab.com/instance';\n\ntype APIOptions = {\n discoveryApi: DiscoveryApi;\n identityApi: IdentityApi;\n codeOwnersPath?: string;\n readmePath?: string;\n gitlabAuthApi: OAuthApi;\n useOAuth?: boolean;\n};\n\nexport class CustomGitlabCiClient extends GitlabCIClient {\n constructor(options: APIOptions) {\n // gitlabInstance is omitted as it is set through the entity\n super(options as any);\n }\n\n getPipelineJobs(\n projectId: number | string,\n pipelineId: number,\n ): Promise<any[] | undefined> {\n return this.callApi(\n `projects/${projectId}/pipelines/${pipelineId}/jobs`,\n {},\n );\n }\n\n getJobLogs(\n projectId: number | string,\n jobId: number,\n ): Promise<string | undefined> {\n return this.callApi(`projects/${projectId}/jobs/${jobId}/trace`, {});\n }\n}\n\nexport class MssvGitlabCIClient implements Partial<MssvApi> {\n private readonly gitlabCiApi: CustomGitlabCiClient;\n\n constructor(options: { gitlabCiApi: CustomGitlabCiClient }) {\n this.gitlabCiApi = options.gitlabCiApi;\n }\n\n async getPipelineSummary(options: {\n entity: Entity;\n page: number;\n pageSize: number;\n }): Promise<MssvApiResponse> {\n const { entity, page, pageSize } = options;\n\n this.gitlabCiApi.gitlabInstance =\n entity.metadata.annotations?.[GITLAB_ANNOTATION_INSTANCE] ?? 'gitlab.com';\n\n const projectId =\n entity.metadata.annotations?.[GITLAB_ANNOTATION_PROJECT_ID];\n\n const projectSlug =\n entity.metadata.annotations?.[GITLAB_ANNOTATION_PROJECT_SLUG];\n\n const [sliceStart, sliceEnd] = [\n page * pageSize,\n page * pageSize + pageSize,\n ];\n\n const summary =\n (await this.gitlabCiApi.getPipelineSummary(projectId ?? projectSlug)) ??\n [];\n\n const summarySlice = summary.slice(sliceStart, sliceEnd);\n const summaryWithLogs = await Promise.all(\n summarySlice.map(async run => {\n const pipelineJobs =\n (await this.gitlabCiApi.getPipelineJobs(run.project_id, run.id)) ??\n [];\n\n const logs = await Promise.all(\n pipelineJobs.map(async job => {\n return await this.gitlabCiApi\n .getJobLogs(run.project_id, job.id)\n .catch(() => ''); // fallback on empty string. It disables logs button and updates tooltip\n }) ?? [],\n ).then(res => res.join(' ')); // return as one\n return { run, logs };\n }),\n );\n\n const results =\n summaryWithLogs.map(\n pr =>\n new PipelineRunResult({\n id: pr?.run?.id.toString(),\n displayName: pr?.run?.id.toString(),\n status: mapStatus(pr?.run?.status ?? 'UNKOWN'),\n logs: pr?.logs,\n }),\n ) || [];\n\n return { results, totalCount: summary?.length ?? 0 };\n }\n\n async getPipelineDetail(): Promise<MssvApiResponse> {\n return { results: [], totalCount: 0 };\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { getCompoundEntityRef } from '@backstage/catalog-model';
|
|
2
|
+
import { createApiRef } from '@backstage/core-plugin-api';
|
|
3
|
+
import { PipelineRunResult } from '../models/pipelineRunResult.esm.js';
|
|
4
|
+
import { JenkinsRunStatus, RunStatus } from '../types/pipelinerun.esm.js';
|
|
5
|
+
|
|
6
|
+
const mssvJenkinsApiRef = createApiRef({
|
|
7
|
+
id: "plugin.mssv-api-jenkins.service"
|
|
8
|
+
});
|
|
9
|
+
const mapStatus = (status) => {
|
|
10
|
+
const runStatus = JenkinsRunStatus[status];
|
|
11
|
+
switch (runStatus) {
|
|
12
|
+
case JenkinsRunStatus.SUCCESS:
|
|
13
|
+
return RunStatus.Succeeded;
|
|
14
|
+
case JenkinsRunStatus.FAILURE:
|
|
15
|
+
return RunStatus.Failed;
|
|
16
|
+
case JenkinsRunStatus.RUNNING:
|
|
17
|
+
return RunStatus.Running;
|
|
18
|
+
case JenkinsRunStatus["IN PROGRESS"]:
|
|
19
|
+
return RunStatus["In Progress"];
|
|
20
|
+
case JenkinsRunStatus.NOT_BUILT:
|
|
21
|
+
return RunStatus.FailedToStart;
|
|
22
|
+
case JenkinsRunStatus.ABORTED:
|
|
23
|
+
return RunStatus.Cancelled;
|
|
24
|
+
case JenkinsRunStatus.PENDING:
|
|
25
|
+
return RunStatus.Pending;
|
|
26
|
+
default:
|
|
27
|
+
return RunStatus.Unknown;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
class MssvJenkinsClient {
|
|
31
|
+
jenkinsApi;
|
|
32
|
+
constructor(options) {
|
|
33
|
+
this.jenkinsApi = options.jenkinsApi;
|
|
34
|
+
}
|
|
35
|
+
async getPipelineSummary(options) {
|
|
36
|
+
const { entity, page, pageSize } = options;
|
|
37
|
+
const projects = await this.jenkinsApi.getProjects({
|
|
38
|
+
entity: getCompoundEntityRef(entity),
|
|
39
|
+
filter: {}
|
|
40
|
+
// TODO: TO CHECK
|
|
41
|
+
});
|
|
42
|
+
const [sliceStart, sliceEnd] = [
|
|
43
|
+
page * pageSize,
|
|
44
|
+
page * pageSize + pageSize
|
|
45
|
+
];
|
|
46
|
+
const projectsSlice = projects.slice(sliceStart, sliceEnd);
|
|
47
|
+
const projectWithLogs = await Promise.all(
|
|
48
|
+
projectsSlice.map(async (project) => {
|
|
49
|
+
const { consoleText: logs } = await this.jenkinsApi.getBuildConsoleText({
|
|
50
|
+
jobFullName: project.displayName,
|
|
51
|
+
entity: getCompoundEntityRef(entity),
|
|
52
|
+
buildNumber: (project.lastBuild?.number ?? 0).toString()
|
|
53
|
+
}).catch(() => ({ consoleText: "" })).then((res) => res);
|
|
54
|
+
const run = await this.jenkinsApi.getBuild({
|
|
55
|
+
jobFullName: project.displayName,
|
|
56
|
+
entity: getCompoundEntityRef(entity),
|
|
57
|
+
buildNumber: (project.lastBuild?.number ?? 0).toString()
|
|
58
|
+
});
|
|
59
|
+
const status = mapStatus(project?.status);
|
|
60
|
+
return {
|
|
61
|
+
project,
|
|
62
|
+
run,
|
|
63
|
+
logs,
|
|
64
|
+
status
|
|
65
|
+
};
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
const results = projectWithLogs.map(
|
|
69
|
+
(pr) => new PipelineRunResult({
|
|
70
|
+
id: pr?.project?.displayName,
|
|
71
|
+
displayName: pr?.project?.displayName,
|
|
72
|
+
logs: pr?.logs,
|
|
73
|
+
status: pr?.status
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
return { results, totalCount: projects?.length ?? 0 };
|
|
77
|
+
}
|
|
78
|
+
async getPipelineDetail(options) {
|
|
79
|
+
const { ref, entity, page, pageSize } = options;
|
|
80
|
+
const job = await this.jenkinsApi.getJobBuilds({
|
|
81
|
+
entity: getCompoundEntityRef(entity),
|
|
82
|
+
jobFullName: ref
|
|
83
|
+
});
|
|
84
|
+
const [sliceStart, sliceEnd] = [
|
|
85
|
+
page * pageSize,
|
|
86
|
+
page * pageSize + pageSize
|
|
87
|
+
];
|
|
88
|
+
const builds = job?.builds ?? [];
|
|
89
|
+
const buildsSlice = builds.slice(sliceStart, sliceEnd);
|
|
90
|
+
const buildWithLogs = await Promise.all(
|
|
91
|
+
buildsSlice.map(async (run) => {
|
|
92
|
+
const { consoleText: logs } = await this.jenkinsApi.getBuildConsoleText({
|
|
93
|
+
buildNumber: run.number.toString(),
|
|
94
|
+
jobFullName: ref,
|
|
95
|
+
entity: getCompoundEntityRef(entity)
|
|
96
|
+
}).then((res) => res);
|
|
97
|
+
return {
|
|
98
|
+
run,
|
|
99
|
+
logs
|
|
100
|
+
};
|
|
101
|
+
})
|
|
102
|
+
);
|
|
103
|
+
const results = buildsSlice.map((run) => {
|
|
104
|
+
const buildLogEntry = buildWithLogs.find((b) => b?.run?.id === run?.id);
|
|
105
|
+
const logs = buildLogEntry ? buildLogEntry?.logs : "";
|
|
106
|
+
const status = mapStatus(run?.result ?? "UNKNOWN");
|
|
107
|
+
return new PipelineRunResult({
|
|
108
|
+
id: run?.id?.toString(),
|
|
109
|
+
displayName: run?.id?.toString(),
|
|
110
|
+
logs,
|
|
111
|
+
status
|
|
112
|
+
});
|
|
113
|
+
}) || [];
|
|
114
|
+
return { results, totalCount: builds?.length ?? 0 };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export { MssvJenkinsClient, mssvJenkinsApiRef };
|
|
119
|
+
//# sourceMappingURL=jenkins.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jenkins.esm.js","sources":["../../src/api/jenkins.ts"],"sourcesContent":["/*\n * Copyright 2024 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 */\nimport { JenkinsApi } from '@backstage-community/plugin-jenkins';\nimport { Entity, getCompoundEntityRef } from '@backstage/catalog-model';\nimport { createApiRef } from '@backstage/core-plugin-api';\nimport { MssvApi, MssvApiResponse } from './mssv';\nimport { PipelineRunResult } from '../models/pipelineRunResult';\nimport { JenkinsRunStatus, RunStatus } from '../types/pipelinerun';\n\n// Apiref to map with client\nexport const mssvJenkinsApiRef = createApiRef<MssvApi>({\n id: 'plugin.mssv-api-jenkins.service',\n});\n\nconst mapStatus = (status: string): RunStatus => {\n const runStatus = JenkinsRunStatus[status as keyof typeof JenkinsRunStatus];\n switch (runStatus) {\n case JenkinsRunStatus.SUCCESS:\n return RunStatus.Succeeded;\n case JenkinsRunStatus.FAILURE:\n return RunStatus.Failed;\n case JenkinsRunStatus.RUNNING:\n return RunStatus.Running;\n case JenkinsRunStatus['IN PROGRESS']:\n return RunStatus['In Progress'];\n case JenkinsRunStatus.NOT_BUILT:\n return RunStatus.FailedToStart;\n case JenkinsRunStatus.ABORTED:\n return RunStatus.Cancelled;\n case JenkinsRunStatus.PENDING:\n return RunStatus.Pending;\n default:\n return RunStatus.Unknown;\n }\n};\n\nexport class MssvJenkinsClient implements MssvApi {\n private readonly jenkinsApi: JenkinsApi;\n\n constructor(options: { jenkinsApi: JenkinsApi }) {\n this.jenkinsApi = options.jenkinsApi;\n }\n\n async getPipelineSummary(options: {\n entity: Entity;\n page: number;\n pageSize: number;\n }): Promise<MssvApiResponse> {\n const { entity, page, pageSize } = options;\n\n const projects = await this.jenkinsApi.getProjects({\n entity: getCompoundEntityRef(entity),\n filter: {}, // TODO: TO CHECK\n });\n\n const [sliceStart, sliceEnd] = [\n page * pageSize,\n page * pageSize + pageSize,\n ];\n\n const projectsSlice = projects.slice(sliceStart, sliceEnd);\n\n // Fetch logs for each projects last build\n const projectWithLogs = await Promise.all(\n projectsSlice.map(async project => {\n const { consoleText: logs } = await this.jenkinsApi\n .getBuildConsoleText({\n jobFullName: project.displayName,\n entity: getCompoundEntityRef(entity),\n buildNumber: (project.lastBuild?.number ?? 0).toString(),\n })\n .catch(() => ({ consoleText: '' })) // fallback on empty string. It disables logs button and updates tooltip\n .then(res => res);\n\n const run = await this.jenkinsApi.getBuild({\n jobFullName: project.displayName,\n entity: getCompoundEntityRef(entity),\n buildNumber: (project.lastBuild?.number ?? 0).toString(),\n });\n\n const status = mapStatus(project?.status);\n\n return {\n project,\n run,\n logs,\n status,\n };\n }),\n );\n\n const results = projectWithLogs.map(\n pr =>\n new PipelineRunResult({\n id: pr?.project?.displayName,\n displayName: pr?.project?.displayName,\n logs: pr?.logs,\n status: pr?.status,\n }),\n );\n\n return { results, totalCount: projects?.length ?? 0 };\n }\n\n async getPipelineDetail(options: {\n entity: Entity;\n ref: string;\n page: number;\n pageSize: number;\n }): Promise<MssvApiResponse> {\n const { ref, entity, page, pageSize } = options;\n const job = await this.jenkinsApi.getJobBuilds({\n entity: getCompoundEntityRef(entity),\n jobFullName: ref,\n });\n\n // Mimic pagination as Jenkins doesn't provide paginated responses\n const [sliceStart, sliceEnd] = [\n page * pageSize,\n page * pageSize + pageSize,\n ];\n\n const builds = job?.builds ?? [];\n const buildsSlice = builds.slice(sliceStart, sliceEnd);\n // Fetch logs for each project last build\n const buildWithLogs = await Promise.all(\n buildsSlice.map(async run => {\n const { consoleText: logs } = await this.jenkinsApi\n .getBuildConsoleText({\n buildNumber: run.number.toString(),\n jobFullName: ref,\n entity: getCompoundEntityRef(entity),\n })\n .then(res => res);\n\n return {\n run,\n logs,\n };\n }),\n );\n\n // Map builds to PipelineRun\n const results =\n buildsSlice.map(run => {\n const buildLogEntry = buildWithLogs.find(b => b?.run?.id === run?.id);\n const logs = buildLogEntry ? buildLogEntry?.logs : '';\n const status = mapStatus(run?.result ?? 'UNKNOWN');\n return new PipelineRunResult({\n id: run?.id?.toString(),\n displayName: run?.id?.toString(),\n logs,\n status,\n });\n }) || [];\n\n return { results, totalCount: builds?.length ?? 0 };\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ErrorBoundary } from '@backstage/core-components';
|
|
2
|
+
import { Dialog, DialogTitle, Box, IconButton, DialogContent } from '@material-ui/core';
|
|
3
|
+
import CloseIcon from '@material-ui/icons/Close';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
const DialogLauncher = ({
|
|
7
|
+
open,
|
|
8
|
+
onClose,
|
|
9
|
+
component: Component,
|
|
10
|
+
componentProps = {},
|
|
11
|
+
title,
|
|
12
|
+
...rest
|
|
13
|
+
}) => {
|
|
14
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Dialog, { open, onClose, ...rest }, /* @__PURE__ */ React.createElement(DialogTitle, null, /* @__PURE__ */ React.createElement(
|
|
15
|
+
Box,
|
|
16
|
+
{
|
|
17
|
+
display: "flex",
|
|
18
|
+
justifyContent: { xs: "center", sm: "space-between" },
|
|
19
|
+
alignItems: "center"
|
|
20
|
+
},
|
|
21
|
+
title,
|
|
22
|
+
/* @__PURE__ */ React.createElement(IconButton, { onClick: onClose }, /* @__PURE__ */ React.createElement(CloseIcon, null))
|
|
23
|
+
)), /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(ErrorBoundary, null, /* @__PURE__ */ React.createElement(Component, { ...componentProps })))));
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export { DialogLauncher };
|
|
27
|
+
//# sourceMappingURL=DialogLauncher.esm.js.map
|