@backstage/plugin-catalog-backend-module-gitlab 0.8.2-next.0 → 0.8.2-next.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 +11 -0
- package/dist/alpha.cjs.js +2 -0
- package/dist/alpha.cjs.js.map +1 -1
- package/dist/alpha.d.ts +56 -1
- package/dist/events/GitLabScmEventsBridge.cjs.js +97 -0
- package/dist/events/GitLabScmEventsBridge.cjs.js.map +1 -0
- package/dist/events/analyzeGitLabWebhookEvent.cjs.js +280 -0
- package/dist/events/analyzeGitLabWebhookEvent.cjs.js.map +1 -0
- package/dist/module/catalogModuleGitlabDiscoveryEntityProvider.cjs.js +25 -2
- package/dist/module/catalogModuleGitlabDiscoveryEntityProvider.cjs.js.map +1 -1
- package/package.json +8 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend-module-gitlab
|
|
2
2
|
|
|
3
|
+
## 0.8.2-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 54a8300: Add GitLab SCM event translation layer for instant catalog reprocessing.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/backend-plugin-api@1.9.0-next.1
|
|
10
|
+
- @backstage/backend-defaults@0.16.1-next.1
|
|
11
|
+
- @backstage/plugin-catalog-node@2.1.1-next.1
|
|
12
|
+
- @backstage/plugin-events-node@0.4.21-next.1
|
|
13
|
+
|
|
3
14
|
## 0.8.2-next.0
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/dist/alpha.cjs.js
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var catalogModuleGitlabDiscoveryEntityProvider = require('./module/catalogModuleGitlabDiscoveryEntityProvider.cjs.js');
|
|
6
|
+
var analyzeGitLabWebhookEvent = require('./events/analyzeGitLabWebhookEvent.cjs.js');
|
|
6
7
|
|
|
7
8
|
const _feature = catalogModuleGitlabDiscoveryEntityProvider.catalogModuleGitlabDiscoveryEntityProvider;
|
|
8
9
|
|
|
10
|
+
exports.analyzeGitLabWebhookEvent = analyzeGitLabWebhookEvent.analyzeGitLabWebhookEvent;
|
|
9
11
|
exports.default = _feature;
|
|
10
12
|
//# sourceMappingURL=alpha.cjs.js.map
|
package/dist/alpha.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alpha.cjs.js","sources":["../src/alpha.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { catalogModuleGitlabDiscoveryEntityProvider } from './module/catalogModuleGitlabDiscoveryEntityProvider';\n\n/** @alpha */\nconst _feature = catalogModuleGitlabDiscoveryEntityProvider;\nexport default _feature;\n"],"names":["catalogModuleGitlabDiscoveryEntityProvider"],"mappings":"
|
|
1
|
+
{"version":3,"file":"alpha.cjs.js","sources":["../src/alpha.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { catalogModuleGitlabDiscoveryEntityProvider } from './module/catalogModuleGitlabDiscoveryEntityProvider';\n\n/** @alpha */\nconst _feature = catalogModuleGitlabDiscoveryEntityProvider;\nexport default _feature;\n\nexport {\n analyzeGitLabWebhookEvent,\n type AnalyzeWebhookEventOptions,\n type AnalyzeWebhookEventResult,\n} from './events/analyzeGitLabWebhookEvent';\n"],"names":["catalogModuleGitlabDiscoveryEntityProvider"],"mappings":";;;;;;;AAmBA,MAAM,QAAA,GAAWA;;;;;"}
|
package/dist/alpha.d.ts
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
2
|
+
import { LoggerService } from '@backstage/backend-plugin-api';
|
|
3
|
+
import { CatalogScmEvent } from '@backstage/plugin-catalog-node/alpha';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for {@link analyzeGitLabWebhookEvent}.
|
|
7
|
+
* @alpha
|
|
8
|
+
*/
|
|
9
|
+
interface AnalyzeWebhookEventOptions {
|
|
10
|
+
/** Optional logger for debug output when events are ignored or unsupported. */
|
|
11
|
+
logger?: LoggerService;
|
|
12
|
+
/**
|
|
13
|
+
* Predicate that returns true for file paths that are relevant to the
|
|
14
|
+
* catalog (e.g. paths ending in `.yaml` or `.yml`).
|
|
15
|
+
*/
|
|
16
|
+
isRelevantPath: (path: string) => boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The result of analyzing a GitLab webhook event.
|
|
20
|
+
*
|
|
21
|
+
* - `ok` — one or more catalog SCM events were produced.
|
|
22
|
+
* - `ignored` — the event was valid but not relevant (e.g. push to a
|
|
23
|
+
* non-default branch, or no catalog files affected).
|
|
24
|
+
* - `aborted` — the event could not be fully processed due to missing data.
|
|
25
|
+
* - `unsupported-event` — the event type is not handled by this analyzer.
|
|
26
|
+
*
|
|
27
|
+
* @alpha
|
|
28
|
+
*/
|
|
29
|
+
type AnalyzeWebhookEventResult = {
|
|
30
|
+
result: 'unsupported-event';
|
|
31
|
+
event: string;
|
|
32
|
+
} | {
|
|
33
|
+
result: 'ignored';
|
|
34
|
+
reason: string;
|
|
35
|
+
} | {
|
|
36
|
+
result: 'aborted';
|
|
37
|
+
reason: string;
|
|
38
|
+
} | {
|
|
39
|
+
result: 'ok';
|
|
40
|
+
events: CatalogScmEvent[];
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Analyzes a GitLab webhook event and translates it into zero or more catalog
|
|
44
|
+
* SCM events that entity providers can act on.
|
|
45
|
+
*
|
|
46
|
+
* Supported event types:
|
|
47
|
+
* - `push` — translates file-level adds, modifications, and deletions on the
|
|
48
|
+
* default branch into `location.created`, `location.updated`, and
|
|
49
|
+
* `location.deleted` events for paths matching `isRelevantPath`.
|
|
50
|
+
* - `repository_update` — translates repository renames, transfers, and
|
|
51
|
+
* deletions into `repository.moved` and `repository.deleted` events.
|
|
52
|
+
*
|
|
53
|
+
* @alpha
|
|
54
|
+
*/
|
|
55
|
+
declare function analyzeGitLabWebhookEvent(eventType: string, eventPayload: unknown, options: AnalyzeWebhookEventOptions): Promise<AnalyzeWebhookEventResult>;
|
|
2
56
|
|
|
3
57
|
/** @alpha */
|
|
4
58
|
declare const _feature: _backstage_backend_plugin_api.BackendFeature;
|
|
5
59
|
|
|
6
|
-
export { _feature as default };
|
|
60
|
+
export { analyzeGitLabWebhookEvent, _feature as default };
|
|
61
|
+
export type { AnalyzeWebhookEventOptions, AnalyzeWebhookEventResult };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var analyzeGitLabWebhookEvent = require('./analyzeGitLabWebhookEvent.cjs.js');
|
|
4
|
+
|
|
5
|
+
function determineEventType(params) {
|
|
6
|
+
const payload = params.eventPayload;
|
|
7
|
+
if (payload && typeof payload === "object" && !Array.isArray(payload) && typeof payload.object_kind === "string") {
|
|
8
|
+
return payload.object_kind;
|
|
9
|
+
}
|
|
10
|
+
const eventName = payload && typeof payload === "object" && !Array.isArray(payload) && typeof payload.event_name === "string" ? payload.event_name : void 0;
|
|
11
|
+
if (eventName) {
|
|
12
|
+
return eventName;
|
|
13
|
+
}
|
|
14
|
+
const metadataType = params.metadata?.["x-gitlab-event"];
|
|
15
|
+
if (typeof metadataType === "string" && metadataType.trim()) {
|
|
16
|
+
return metadataType.trim().toLowerCase().replace(/\s+hook$/, "").replace(/\s+/g, "_");
|
|
17
|
+
}
|
|
18
|
+
if (params.topic.startsWith("gitlab.")) {
|
|
19
|
+
return params.topic.slice("gitlab.".length);
|
|
20
|
+
}
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
class GitLabScmEventsBridge {
|
|
24
|
+
#logger;
|
|
25
|
+
#events;
|
|
26
|
+
#catalogScmEvents;
|
|
27
|
+
#shuttingDown;
|
|
28
|
+
#pendingPublish;
|
|
29
|
+
constructor(options) {
|
|
30
|
+
this.#logger = options.logger;
|
|
31
|
+
this.#events = options.events;
|
|
32
|
+
this.#catalogScmEvents = options.catalogScmEvents;
|
|
33
|
+
this.#shuttingDown = false;
|
|
34
|
+
}
|
|
35
|
+
async start() {
|
|
36
|
+
await this.#events.subscribe({
|
|
37
|
+
id: "catalog-gitlab-scm-events-bridge",
|
|
38
|
+
topics: ["gitlab"],
|
|
39
|
+
onEvent: this.#onEvent.bind(this)
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async stop() {
|
|
43
|
+
this.#shuttingDown = true;
|
|
44
|
+
await this.#pendingPublish;
|
|
45
|
+
}
|
|
46
|
+
async #onEvent(params) {
|
|
47
|
+
const eventType = determineEventType(params);
|
|
48
|
+
if (!eventType || !params.eventPayload) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (this.#shuttingDown) {
|
|
52
|
+
this.#logger.warn(
|
|
53
|
+
`Skipping GitLab webhook event of type "${eventType}" on topic "${params.topic}" because the bridge is shutting down`
|
|
54
|
+
);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const previous = this.#pendingPublish ?? Promise.resolve();
|
|
58
|
+
const current = previous.then(async () => {
|
|
59
|
+
try {
|
|
60
|
+
const output = await analyzeGitLabWebhookEvent.analyzeGitLabWebhookEvent(
|
|
61
|
+
eventType,
|
|
62
|
+
params.eventPayload,
|
|
63
|
+
{
|
|
64
|
+
logger: this.#logger,
|
|
65
|
+
isRelevantPath: (path) => path.endsWith(".yaml") || path.endsWith(".yml")
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
if (output.result === "ok") {
|
|
69
|
+
await this.#catalogScmEvents.publish(output.events);
|
|
70
|
+
} else if (output.result === "ignored") {
|
|
71
|
+
this.#logger.debug(
|
|
72
|
+
`Skipping GitLab webhook event of type "${eventType}" on topic "${params.topic}" because it is ignored: ${output.reason}`
|
|
73
|
+
);
|
|
74
|
+
} else if (output.result === "aborted") {
|
|
75
|
+
this.#logger.warn(
|
|
76
|
+
`Skipping GitLab webhook event of type "${eventType}" on topic "${params.topic}" because it is aborted: ${output.reason}`
|
|
77
|
+
);
|
|
78
|
+
} else if (output.result === "unsupported-event") {
|
|
79
|
+
this.#logger.debug(
|
|
80
|
+
`Skipping GitLab webhook event of type "${eventType}" on topic "${params.topic}" because it is unsupported: ${output.event}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
this.#logger.warn(
|
|
85
|
+
`Failed to handle GitLab webhook event of type "${eventType}"`,
|
|
86
|
+
error
|
|
87
|
+
);
|
|
88
|
+
} finally {
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
this.#pendingPublish = current;
|
|
92
|
+
await current;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
exports.GitLabScmEventsBridge = GitLabScmEventsBridge;
|
|
97
|
+
//# sourceMappingURL=GitLabScmEventsBridge.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GitLabScmEventsBridge.cjs.js","sources":["../../src/events/GitLabScmEventsBridge.ts"],"sourcesContent":["/*\n * Copyright 2026 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 { LoggerService } from '@backstage/backend-plugin-api';\nimport { CatalogScmEventsService } from '@backstage/plugin-catalog-node/alpha';\nimport { EventParams, EventsService } from '@backstage/plugin-events-node';\nimport { analyzeGitLabWebhookEvent } from './analyzeGitLabWebhookEvent';\n\nfunction determineEventType(params: EventParams): string | undefined {\n const payload = params.eventPayload;\n\n if (\n payload &&\n typeof payload === 'object' &&\n !Array.isArray(payload) &&\n typeof (payload as { object_kind?: unknown }).object_kind === 'string'\n ) {\n return (payload as { object_kind: string }).object_kind;\n }\n\n const eventName =\n payload &&\n typeof payload === 'object' &&\n !Array.isArray(payload) &&\n typeof (payload as { event_name?: unknown }).event_name === 'string'\n ? (payload as { event_name: string }).event_name\n : undefined;\n if (eventName) {\n return eventName;\n }\n\n const metadataType = params.metadata?.['x-gitlab-event'];\n if (typeof metadataType === 'string' && metadataType.trim()) {\n return metadataType\n .trim()\n .toLowerCase()\n .replace(/\\s+hook$/, '')\n .replace(/\\s+/g, '_');\n }\n\n if (params.topic.startsWith('gitlab.')) {\n return params.topic.slice('gitlab.'.length);\n }\n\n return undefined;\n}\n\n/**\n * Takes GitLab webhook events, analyzes them, and publishes them as catalog\n * SCM events that entity providers and others can subscribe to.\n */\nexport class GitLabScmEventsBridge {\n readonly #logger: LoggerService;\n readonly #events: EventsService;\n readonly #catalogScmEvents: CatalogScmEventsService;\n #shuttingDown: boolean;\n #pendingPublish: Promise<void> | undefined;\n\n constructor(options: {\n logger: LoggerService;\n events: EventsService;\n catalogScmEvents: CatalogScmEventsService;\n }) {\n this.#logger = options.logger;\n this.#events = options.events;\n this.#catalogScmEvents = options.catalogScmEvents;\n this.#shuttingDown = false;\n }\n\n async start() {\n await this.#events.subscribe({\n id: 'catalog-gitlab-scm-events-bridge',\n topics: ['gitlab'],\n onEvent: this.#onEvent.bind(this),\n });\n }\n\n async stop() {\n this.#shuttingDown = true;\n await this.#pendingPublish;\n }\n\n async #onEvent(params: EventParams): Promise<void> {\n const eventType = determineEventType(params);\n if (!eventType || !params.eventPayload) {\n return;\n }\n\n if (this.#shuttingDown) {\n this.#logger.warn(\n `Skipping GitLab webhook event of type \"${eventType}\" on topic \"${params.topic}\" because the bridge is shutting down`,\n );\n return;\n }\n\n const previous = this.#pendingPublish ?? Promise.resolve();\n const current = previous.then(async () => {\n try {\n const output = await analyzeGitLabWebhookEvent(\n eventType,\n params.eventPayload,\n {\n logger: this.#logger,\n isRelevantPath: path =>\n path.endsWith('.yaml') || path.endsWith('.yml'),\n },\n );\n\n if (output.result === 'ok') {\n await this.#catalogScmEvents.publish(output.events);\n } else if (output.result === 'ignored') {\n this.#logger.debug(\n `Skipping GitLab webhook event of type \"${eventType}\" on topic \"${params.topic}\" because it is ignored: ${output.reason}`,\n );\n } else if (output.result === 'aborted') {\n this.#logger.warn(\n `Skipping GitLab webhook event of type \"${eventType}\" on topic \"${params.topic}\" because it is aborted: ${output.reason}`,\n );\n } else if (output.result === 'unsupported-event') {\n this.#logger.debug(\n `Skipping GitLab webhook event of type \"${eventType}\" on topic \"${params.topic}\" because it is unsupported: ${output.event}`,\n );\n }\n } catch (error) {\n this.#logger.warn(\n `Failed to handle GitLab webhook event of type \"${eventType}\"`,\n error,\n );\n } finally {\n // no-op; chain handles ordering\n }\n });\n this.#pendingPublish = current;\n await current;\n }\n}\n"],"names":["analyzeGitLabWebhookEvent"],"mappings":"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"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var errors = require('@backstage/errors');
|
|
4
|
+
|
|
5
|
+
function isObject(value) {
|
|
6
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
function asString(value) {
|
|
9
|
+
return typeof value === "string" ? value : void 0;
|
|
10
|
+
}
|
|
11
|
+
function getFromChange(change) {
|
|
12
|
+
return asString(change?.from) ?? asString(change?.old) ?? asString(change?.previous) ?? asString(change?.before);
|
|
13
|
+
}
|
|
14
|
+
function getToChange(change) {
|
|
15
|
+
return asString(change?.to) ?? asString(change?.new) ?? asString(change?.current) ?? asString(change?.after);
|
|
16
|
+
}
|
|
17
|
+
function extractBranchName(ref) {
|
|
18
|
+
if (!ref || !ref.startsWith("refs/heads/")) {
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
return ref.slice("refs/heads/".length);
|
|
22
|
+
}
|
|
23
|
+
function getCommitUrl(commit, repositoryUrl) {
|
|
24
|
+
if (commit.url) {
|
|
25
|
+
return commit.url;
|
|
26
|
+
}
|
|
27
|
+
if (commit.id && repositoryUrl) {
|
|
28
|
+
return `${repositoryUrl}/-/commit/${commit.id}`;
|
|
29
|
+
}
|
|
30
|
+
return void 0;
|
|
31
|
+
}
|
|
32
|
+
function pathStateToCatalogScmEvent(path, event, repositoryUrl, branch) {
|
|
33
|
+
const toBlobUrl = (p) => `${repositoryUrl}/-/blob/${branch}/${p}`;
|
|
34
|
+
const context = event.commitUrl ? { commitUrl: event.commitUrl } : void 0;
|
|
35
|
+
switch (event.type) {
|
|
36
|
+
case "added":
|
|
37
|
+
return {
|
|
38
|
+
type: "location.created",
|
|
39
|
+
url: toBlobUrl(path),
|
|
40
|
+
context
|
|
41
|
+
};
|
|
42
|
+
case "removed":
|
|
43
|
+
return {
|
|
44
|
+
type: "location.deleted",
|
|
45
|
+
url: toBlobUrl(path),
|
|
46
|
+
context
|
|
47
|
+
};
|
|
48
|
+
case "changed":
|
|
49
|
+
return {
|
|
50
|
+
type: "location.updated",
|
|
51
|
+
url: toBlobUrl(path),
|
|
52
|
+
context
|
|
53
|
+
};
|
|
54
|
+
default:
|
|
55
|
+
throw new Error(`Unknown file event type: ${event.type}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function applyAddedPath(pathState, path, commitUrl) {
|
|
59
|
+
const previous = pathState.get(path);
|
|
60
|
+
if (!previous) {
|
|
61
|
+
pathState.set(path, { type: "added", commitUrl });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (previous.type === "removed") {
|
|
65
|
+
pathState.set(path, { type: "changed", commitUrl });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
pathState.set(path, previous);
|
|
69
|
+
}
|
|
70
|
+
function applyRemovedPath(pathState, path, commitUrl) {
|
|
71
|
+
const previous = pathState.get(path);
|
|
72
|
+
if (!previous) {
|
|
73
|
+
pathState.set(path, { type: "removed", commitUrl });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (previous.type === "added") {
|
|
77
|
+
pathState.delete(path);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (previous.type === "changed") {
|
|
81
|
+
pathState.set(path, { type: "removed", commitUrl });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
pathState.set(path, previous);
|
|
85
|
+
}
|
|
86
|
+
function applyModifiedPath(pathState, path, commitUrl) {
|
|
87
|
+
const previous = pathState.get(path);
|
|
88
|
+
if (!previous) {
|
|
89
|
+
pathState.set(path, { type: "changed", commitUrl });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (previous.type === "removed") {
|
|
93
|
+
pathState.set(path, previous);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
pathState.set(path, previous);
|
|
97
|
+
}
|
|
98
|
+
async function onPushEvent(event, options) {
|
|
99
|
+
const project = isObject(event.project) ? event.project : void 0;
|
|
100
|
+
const repositoryUrl = asString(project?.web_url);
|
|
101
|
+
const contextUrl = repositoryUrl ?? "<unknown>";
|
|
102
|
+
const defaultBranch = asString(project?.default_branch);
|
|
103
|
+
if (defaultBranch) {
|
|
104
|
+
const expectedRef = `refs/heads/${defaultBranch}`;
|
|
105
|
+
if (event.ref !== expectedRef) {
|
|
106
|
+
return {
|
|
107
|
+
result: "ignored",
|
|
108
|
+
reason: `GitLab push event did not target the default branch, found "${event.ref}" but expected "${expectedRef}": ${contextUrl}`
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const commits = Array.isArray(event.commits) ? event.commits : [];
|
|
113
|
+
if (!commits.length) {
|
|
114
|
+
return {
|
|
115
|
+
result: "ignored",
|
|
116
|
+
reason: `GitLab push event did not contain any commits: ${contextUrl}`
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const pathState = /* @__PURE__ */ new Map();
|
|
120
|
+
let hasRelevantPaths = false;
|
|
121
|
+
for (const commit of commits) {
|
|
122
|
+
const commitUrl = getCommitUrl(commit, repositoryUrl);
|
|
123
|
+
const added = (commit.added ?? []).filter(options.isRelevantPath);
|
|
124
|
+
const modified = (commit.modified ?? []).filter(options.isRelevantPath);
|
|
125
|
+
const removed = (commit.removed ?? []).filter(options.isRelevantPath);
|
|
126
|
+
if (added.length || modified.length || removed.length) {
|
|
127
|
+
hasRelevantPaths = true;
|
|
128
|
+
}
|
|
129
|
+
for (const path of modified) {
|
|
130
|
+
applyModifiedPath(pathState, path, commitUrl);
|
|
131
|
+
}
|
|
132
|
+
for (const path of added) {
|
|
133
|
+
applyAddedPath(pathState, path, commitUrl);
|
|
134
|
+
}
|
|
135
|
+
for (const path of removed) {
|
|
136
|
+
applyRemovedPath(pathState, path, commitUrl);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (!hasRelevantPaths) {
|
|
140
|
+
return {
|
|
141
|
+
result: "ignored",
|
|
142
|
+
reason: `GitLab push event did not affect any relevant paths: ${contextUrl}`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if (!repositoryUrl) {
|
|
146
|
+
return {
|
|
147
|
+
result: "aborted",
|
|
148
|
+
reason: "GitLab push event did not include project.web_url"
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const branch = defaultBranch ?? extractBranchName(event.ref) ?? "main";
|
|
152
|
+
return {
|
|
153
|
+
result: "ok",
|
|
154
|
+
events: Array.from(pathState.entries()).map(
|
|
155
|
+
([path, e]) => pathStateToCatalogScmEvent(path, e, repositoryUrl, branch)
|
|
156
|
+
)
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function getOrigin(url) {
|
|
160
|
+
try {
|
|
161
|
+
return new URL(url).origin;
|
|
162
|
+
} catch {
|
|
163
|
+
return void 0;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function toRepositoryUrl(baseUrl, pathWithNamespace) {
|
|
167
|
+
return `${baseUrl}/${pathWithNamespace}`;
|
|
168
|
+
}
|
|
169
|
+
function getCurrentRepositoryUrl(event) {
|
|
170
|
+
const projectUrl = asString(event.project?.web_url);
|
|
171
|
+
if (projectUrl) {
|
|
172
|
+
return projectUrl;
|
|
173
|
+
}
|
|
174
|
+
return getToChange(event.changes?.web_url);
|
|
175
|
+
}
|
|
176
|
+
function getPreviousRepositoryUrl(event, currentRepositoryUrl) {
|
|
177
|
+
const changedUrl = getFromChange(event.changes?.web_url);
|
|
178
|
+
if (changedUrl) {
|
|
179
|
+
return changedUrl;
|
|
180
|
+
}
|
|
181
|
+
const oldPathWithNamespace = asString(event.old_path_with_namespace) ?? getFromChange(event.changes?.path_with_namespace) ?? getFromChange(event.changes?.old_path_with_namespace);
|
|
182
|
+
if (!oldPathWithNamespace) {
|
|
183
|
+
return void 0;
|
|
184
|
+
}
|
|
185
|
+
const projectPathWithNamespace = asString(event.project?.path_with_namespace);
|
|
186
|
+
const projectUrl = asString(event.project?.web_url);
|
|
187
|
+
if (currentRepositoryUrl && projectPathWithNamespace && currentRepositoryUrl.endsWith(`/${projectPathWithNamespace}`)) {
|
|
188
|
+
const prefix = currentRepositoryUrl.slice(
|
|
189
|
+
0,
|
|
190
|
+
-projectPathWithNamespace.length - 1
|
|
191
|
+
);
|
|
192
|
+
return toRepositoryUrl(prefix, oldPathWithNamespace);
|
|
193
|
+
}
|
|
194
|
+
const baseUrl = projectUrl && getOrigin(projectUrl) || currentRepositoryUrl && getOrigin(currentRepositoryUrl);
|
|
195
|
+
if (!baseUrl) {
|
|
196
|
+
return void 0;
|
|
197
|
+
}
|
|
198
|
+
return toRepositoryUrl(baseUrl, oldPathWithNamespace);
|
|
199
|
+
}
|
|
200
|
+
function isRepositoryDeletionEvent(event) {
|
|
201
|
+
const eventName = asString(event.event_name)?.toLowerCase() ?? "";
|
|
202
|
+
const action = asString(event.action)?.toLowerCase() ?? "";
|
|
203
|
+
if (eventName.includes("destroy") || eventName.includes("delete") || action.includes("destroy") || action.includes("delete") || action.includes("remove")) {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
if (event.deleted_at || event.project?.deleted_at) {
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
return Boolean(getToChange(event.changes?.deleted_at));
|
|
210
|
+
}
|
|
211
|
+
async function onRepositoryUpdateEvent(event) {
|
|
212
|
+
const currentRepositoryUrl = getCurrentRepositoryUrl(event);
|
|
213
|
+
const previousRepositoryUrl = getPreviousRepositoryUrl(
|
|
214
|
+
event,
|
|
215
|
+
currentRepositoryUrl
|
|
216
|
+
);
|
|
217
|
+
if (isRepositoryDeletionEvent(event)) {
|
|
218
|
+
const repositoryUrl = currentRepositoryUrl ?? previousRepositoryUrl;
|
|
219
|
+
if (!repositoryUrl) {
|
|
220
|
+
return {
|
|
221
|
+
result: "ignored",
|
|
222
|
+
reason: "GitLab repository_update event did not include sufficient data for repository deletion handling"
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
result: "ok",
|
|
227
|
+
events: [
|
|
228
|
+
{
|
|
229
|
+
type: "repository.deleted",
|
|
230
|
+
url: repositoryUrl
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (previousRepositoryUrl && currentRepositoryUrl && previousRepositoryUrl !== currentRepositoryUrl) {
|
|
236
|
+
return {
|
|
237
|
+
result: "ok",
|
|
238
|
+
events: [
|
|
239
|
+
{
|
|
240
|
+
type: "repository.moved",
|
|
241
|
+
fromUrl: previousRepositoryUrl,
|
|
242
|
+
toUrl: currentRepositoryUrl
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
result: "ignored",
|
|
249
|
+
reason: "GitLab repository_update event did not contain supported changes"
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
async function analyzeGitLabWebhookEvent(eventType, eventPayload, options) {
|
|
253
|
+
if (!isObject(eventPayload)) {
|
|
254
|
+
throw new errors.InputError("GitLab webhook event payload is not an object");
|
|
255
|
+
}
|
|
256
|
+
let result;
|
|
257
|
+
if (eventType === "push") {
|
|
258
|
+
result = await onPushEvent(
|
|
259
|
+
eventPayload,
|
|
260
|
+
options
|
|
261
|
+
);
|
|
262
|
+
} else if (eventType === "repository_update") {
|
|
263
|
+
result = await onRepositoryUpdateEvent(
|
|
264
|
+
eventPayload
|
|
265
|
+
);
|
|
266
|
+
} else {
|
|
267
|
+
result = { result: "unsupported-event", event: eventType };
|
|
268
|
+
}
|
|
269
|
+
if (result.result === "ignored") {
|
|
270
|
+
options.logger?.debug(`GitLab webhook event ignored: ${result.reason}`);
|
|
271
|
+
} else if (result.result === "aborted") {
|
|
272
|
+
options.logger?.debug(`GitLab webhook event aborted: ${result.reason}`);
|
|
273
|
+
} else if (result.result === "unsupported-event") {
|
|
274
|
+
options.logger?.debug(`GitLab webhook event unsupported: ${result.event}`);
|
|
275
|
+
}
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
exports.analyzeGitLabWebhookEvent = analyzeGitLabWebhookEvent;
|
|
280
|
+
//# sourceMappingURL=analyzeGitLabWebhookEvent.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzeGitLabWebhookEvent.cjs.js","sources":["../../src/events/analyzeGitLabWebhookEvent.ts"],"sourcesContent":["/*\n * Copyright 2026 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 { LoggerService } from '@backstage/backend-plugin-api';\nimport { InputError } from '@backstage/errors';\nimport { CatalogScmEvent } from '@backstage/plugin-catalog-node/alpha';\nimport { WebhookPushEventSchema } from '@gitbeaker/rest';\n\ntype StringRecord = Record<string, unknown>;\n\n/**\n * Options for {@link analyzeGitLabWebhookEvent}.\n * @alpha\n */\nexport interface AnalyzeWebhookEventOptions {\n /** Optional logger for debug output when events are ignored or unsupported. */\n logger?: LoggerService;\n /**\n * Predicate that returns true for file paths that are relevant to the\n * catalog (e.g. paths ending in `.yaml` or `.yml`).\n */\n isRelevantPath: (path: string) => boolean;\n}\n\n/**\n * The result of analyzing a GitLab webhook event.\n *\n * - `ok` — one or more catalog SCM events were produced.\n * - `ignored` — the event was valid but not relevant (e.g. push to a\n * non-default branch, or no catalog files affected).\n * - `aborted` — the event could not be fully processed due to missing data.\n * - `unsupported-event` — the event type is not handled by this analyzer.\n *\n * @alpha\n */\nexport type AnalyzeWebhookEventResult =\n | {\n result: 'unsupported-event';\n event: string;\n }\n | {\n result: 'ignored';\n reason: string;\n }\n | {\n result: 'aborted';\n reason: string;\n }\n | {\n result: 'ok';\n events: CatalogScmEvent[];\n };\n\ntype PathState =\n | {\n type: 'added';\n commitUrl?: string;\n }\n | {\n type: 'removed';\n commitUrl?: string;\n }\n | {\n type: 'changed';\n commitUrl?: string;\n };\n\ntype GitLabPushCommit = {\n id?: string;\n url?: string;\n added?: string[];\n removed?: string[];\n modified?: string[];\n};\n\ntype ChangeDescriptor = {\n from?: unknown;\n to?: unknown;\n old?: unknown;\n new?: unknown;\n previous?: unknown;\n current?: unknown;\n before?: unknown;\n after?: unknown;\n};\n\ntype GitLabRepositoryUpdateEvent = {\n object_kind?: string;\n event_name?: string;\n action?: string;\n deleted_at?: string | null;\n path_with_namespace?: string;\n old_path_with_namespace?: string;\n project?: {\n web_url?: string;\n path_with_namespace?: string;\n deleted_at?: string | null;\n };\n changes?: {\n web_url?: ChangeDescriptor;\n path_with_namespace?: ChangeDescriptor;\n old_path_with_namespace?: ChangeDescriptor;\n deleted_at?: ChangeDescriptor;\n };\n};\n\nfunction isObject(value: unknown): value is StringRecord {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction getFromChange(change?: ChangeDescriptor): string | undefined {\n return (\n asString(change?.from) ??\n asString(change?.old) ??\n asString(change?.previous) ??\n asString(change?.before)\n );\n}\n\nfunction getToChange(change?: ChangeDescriptor): string | undefined {\n return (\n asString(change?.to) ??\n asString(change?.new) ??\n asString(change?.current) ??\n asString(change?.after)\n );\n}\n\nfunction extractBranchName(ref?: string): string | undefined {\n if (!ref || !ref.startsWith('refs/heads/')) {\n return undefined;\n }\n return ref.slice('refs/heads/'.length);\n}\n\nfunction getCommitUrl(\n commit: GitLabPushCommit,\n repositoryUrl?: string,\n): string | undefined {\n if (commit.url) {\n return commit.url;\n }\n if (commit.id && repositoryUrl) {\n return `${repositoryUrl}/-/commit/${commit.id}`;\n }\n return undefined;\n}\n\nfunction pathStateToCatalogScmEvent(\n path: string,\n event: PathState,\n repositoryUrl: string,\n branch: string,\n): CatalogScmEvent {\n const toBlobUrl = (p: string) => `${repositoryUrl}/-/blob/${branch}/${p}`;\n const context = event.commitUrl ? { commitUrl: event.commitUrl } : undefined;\n\n switch (event.type) {\n case 'added':\n return {\n type: 'location.created',\n url: toBlobUrl(path),\n context,\n };\n case 'removed':\n return {\n type: 'location.deleted',\n url: toBlobUrl(path),\n context,\n };\n case 'changed':\n return {\n type: 'location.updated',\n url: toBlobUrl(path),\n context,\n };\n default:\n // @ts-expect-error Intentionally expected, to check for exhaustive checking of the types\n throw new Error(`Unknown file event type: ${event.type}`);\n }\n}\n\nfunction applyAddedPath(\n pathState: Map<string, PathState>,\n path: string,\n commitUrl: string | undefined,\n) {\n const previous = pathState.get(path);\n if (!previous) {\n pathState.set(path, { type: 'added', commitUrl });\n return;\n }\n if (previous.type === 'removed') {\n pathState.set(path, { type: 'changed', commitUrl });\n return;\n }\n pathState.set(path, previous);\n}\n\nfunction applyRemovedPath(\n pathState: Map<string, PathState>,\n path: string,\n commitUrl: string | undefined,\n) {\n const previous = pathState.get(path);\n if (!previous) {\n pathState.set(path, { type: 'removed', commitUrl });\n return;\n }\n if (previous.type === 'added') {\n pathState.delete(path);\n return;\n }\n if (previous.type === 'changed') {\n pathState.set(path, { type: 'removed', commitUrl });\n return;\n }\n pathState.set(path, previous);\n}\n\nfunction applyModifiedPath(\n pathState: Map<string, PathState>,\n path: string,\n commitUrl: string | undefined,\n) {\n const previous = pathState.get(path);\n if (!previous) {\n pathState.set(path, { type: 'changed', commitUrl });\n return;\n }\n if (previous.type === 'removed') {\n pathState.set(path, previous);\n return;\n }\n pathState.set(path, previous);\n}\n\nasync function onPushEvent(\n event: WebhookPushEventSchema,\n options: AnalyzeWebhookEventOptions,\n): Promise<AnalyzeWebhookEventResult> {\n const project = isObject(event.project) ? event.project : undefined;\n const repositoryUrl = asString(project?.web_url);\n const contextUrl = repositoryUrl ?? '<unknown>';\n const defaultBranch = asString(project?.default_branch);\n\n if (defaultBranch) {\n const expectedRef = `refs/heads/${defaultBranch}`;\n if (event.ref !== expectedRef) {\n return {\n result: 'ignored',\n reason: `GitLab push event did not target the default branch, found \"${event.ref}\" but expected \"${expectedRef}\": ${contextUrl}`,\n };\n }\n }\n\n const commits = (\n Array.isArray(event.commits) ? event.commits : []\n ) as GitLabPushCommit[];\n\n if (!commits.length) {\n return {\n result: 'ignored',\n reason: `GitLab push event did not contain any commits: ${contextUrl}`,\n };\n }\n\n const pathState = new Map<string, PathState>();\n let hasRelevantPaths = false;\n\n for (const commit of commits) {\n const commitUrl = getCommitUrl(commit, repositoryUrl);\n const added = (commit.added ?? []).filter(options.isRelevantPath);\n const modified = (commit.modified ?? []).filter(options.isRelevantPath);\n const removed = (commit.removed ?? []).filter(options.isRelevantPath);\n\n if (added.length || modified.length || removed.length) {\n hasRelevantPaths = true;\n }\n\n for (const path of modified) {\n applyModifiedPath(pathState, path, commitUrl);\n }\n\n for (const path of added) {\n applyAddedPath(pathState, path, commitUrl);\n }\n\n for (const path of removed) {\n applyRemovedPath(pathState, path, commitUrl);\n }\n }\n\n if (!hasRelevantPaths) {\n return {\n result: 'ignored',\n reason: `GitLab push event did not affect any relevant paths: ${contextUrl}`,\n };\n }\n\n if (!repositoryUrl) {\n return {\n result: 'aborted',\n reason: 'GitLab push event did not include project.web_url',\n };\n }\n\n const branch = defaultBranch ?? extractBranchName(event.ref) ?? 'main';\n return {\n result: 'ok',\n events: Array.from(pathState.entries()).map(([path, e]) =>\n pathStateToCatalogScmEvent(path, e, repositoryUrl, branch),\n ),\n };\n}\n\nfunction getOrigin(url: string): string | undefined {\n try {\n return new URL(url).origin;\n } catch {\n return undefined;\n }\n}\n\nfunction toRepositoryUrl(baseUrl: string, pathWithNamespace: string): string {\n return `${baseUrl}/${pathWithNamespace}`;\n}\n\nfunction getCurrentRepositoryUrl(\n event: GitLabRepositoryUpdateEvent,\n): string | undefined {\n const projectUrl = asString(event.project?.web_url);\n if (projectUrl) {\n return projectUrl;\n }\n\n return getToChange(event.changes?.web_url);\n}\n\nfunction getPreviousRepositoryUrl(\n event: GitLabRepositoryUpdateEvent,\n currentRepositoryUrl?: string,\n): string | undefined {\n const changedUrl = getFromChange(event.changes?.web_url);\n if (changedUrl) {\n return changedUrl;\n }\n\n const oldPathWithNamespace =\n asString(event.old_path_with_namespace) ??\n getFromChange(event.changes?.path_with_namespace) ??\n getFromChange(event.changes?.old_path_with_namespace);\n if (!oldPathWithNamespace) {\n return undefined;\n }\n\n const projectPathWithNamespace = asString(event.project?.path_with_namespace);\n const projectUrl = asString(event.project?.web_url);\n\n if (\n currentRepositoryUrl &&\n projectPathWithNamespace &&\n currentRepositoryUrl.endsWith(`/${projectPathWithNamespace}`)\n ) {\n const prefix = currentRepositoryUrl.slice(\n 0,\n -projectPathWithNamespace.length - 1,\n );\n return toRepositoryUrl(prefix, oldPathWithNamespace);\n }\n\n const baseUrl =\n (projectUrl && getOrigin(projectUrl)) ||\n (currentRepositoryUrl && getOrigin(currentRepositoryUrl));\n if (!baseUrl) {\n return undefined;\n }\n\n return toRepositoryUrl(baseUrl, oldPathWithNamespace);\n}\n\nfunction isRepositoryDeletionEvent(\n event: GitLabRepositoryUpdateEvent,\n): boolean {\n const eventName = asString(event.event_name)?.toLowerCase() ?? '';\n const action = asString(event.action)?.toLowerCase() ?? '';\n\n if (\n eventName.includes('destroy') ||\n eventName.includes('delete') ||\n action.includes('destroy') ||\n action.includes('delete') ||\n action.includes('remove')\n ) {\n return true;\n }\n\n if (event.deleted_at || event.project?.deleted_at) {\n return true;\n }\n\n return Boolean(getToChange(event.changes?.deleted_at));\n}\n\nasync function onRepositoryUpdateEvent(\n event: GitLabRepositoryUpdateEvent,\n): Promise<AnalyzeWebhookEventResult> {\n const currentRepositoryUrl = getCurrentRepositoryUrl(event);\n const previousRepositoryUrl = getPreviousRepositoryUrl(\n event,\n currentRepositoryUrl,\n );\n\n if (isRepositoryDeletionEvent(event)) {\n const repositoryUrl = currentRepositoryUrl ?? previousRepositoryUrl;\n if (!repositoryUrl) {\n return {\n result: 'ignored',\n reason:\n 'GitLab repository_update event did not include sufficient data for repository deletion handling',\n };\n }\n\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.deleted',\n url: repositoryUrl,\n },\n ],\n };\n }\n\n if (\n previousRepositoryUrl &&\n currentRepositoryUrl &&\n previousRepositoryUrl !== currentRepositoryUrl\n ) {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.moved',\n fromUrl: previousRepositoryUrl,\n toUrl: currentRepositoryUrl,\n },\n ],\n };\n }\n\n return {\n result: 'ignored',\n reason: 'GitLab repository_update event did not contain supported changes',\n };\n}\n\n/**\n * Analyzes a GitLab webhook event and translates it into zero or more catalog\n * SCM events that entity providers can act on.\n *\n * Supported event types:\n * - `push` — translates file-level adds, modifications, and deletions on the\n * default branch into `location.created`, `location.updated`, and\n * `location.deleted` events for paths matching `isRelevantPath`.\n * - `repository_update` — translates repository renames, transfers, and\n * deletions into `repository.moved` and `repository.deleted` events.\n *\n * @alpha\n */\nexport async function analyzeGitLabWebhookEvent(\n eventType: string,\n eventPayload: unknown,\n options: AnalyzeWebhookEventOptions,\n): Promise<AnalyzeWebhookEventResult> {\n if (!isObject(eventPayload)) {\n throw new InputError('GitLab webhook event payload is not an object');\n }\n\n let result: AnalyzeWebhookEventResult;\n\n if (eventType === 'push') {\n result = await onPushEvent(\n eventPayload as unknown as WebhookPushEventSchema,\n options,\n );\n } else if (eventType === 'repository_update') {\n result = await onRepositoryUpdateEvent(\n eventPayload as GitLabRepositoryUpdateEvent,\n );\n } else {\n result = { result: 'unsupported-event', event: eventType };\n }\n\n if (result.result === 'ignored') {\n options.logger?.debug(`GitLab webhook event ignored: ${result.reason}`);\n } else if (result.result === 'aborted') {\n options.logger?.debug(`GitLab webhook event aborted: ${result.reason}`);\n } else if (result.result === 'unsupported-event') {\n options.logger?.debug(`GitLab webhook event unsupported: ${result.event}`);\n }\n\n return result;\n}\n"],"names":["InputError"],"mappings":"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"}
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
4
|
var pluginCatalogNode = require('@backstage/plugin-catalog-node');
|
|
5
|
+
var alpha = require('@backstage/plugin-catalog-node/alpha');
|
|
5
6
|
var pluginEventsNode = require('@backstage/plugin-events-node');
|
|
7
|
+
var GitLabScmEventsBridge = require('../events/GitLabScmEventsBridge.cjs.js');
|
|
6
8
|
var GitlabDiscoveryEntityProvider = require('../providers/GitlabDiscoveryEntityProvider.cjs.js');
|
|
7
9
|
require('@backstage/catalog-model');
|
|
8
10
|
require('@backstage/integration');
|
|
@@ -17,17 +19,38 @@ const catalogModuleGitlabDiscoveryEntityProvider = backendPluginApi.createBacken
|
|
|
17
19
|
deps: {
|
|
18
20
|
config: backendPluginApi.coreServices.rootConfig,
|
|
19
21
|
catalog: pluginCatalogNode.catalogProcessingExtensionPoint,
|
|
22
|
+
catalogScmEvents: alpha.catalogScmEventsServiceRef,
|
|
20
23
|
logger: backendPluginApi.coreServices.logger,
|
|
21
24
|
scheduler: backendPluginApi.coreServices.scheduler,
|
|
22
|
-
events: pluginEventsNode.eventsServiceRef
|
|
25
|
+
events: pluginEventsNode.eventsServiceRef,
|
|
26
|
+
lifecycle: backendPluginApi.coreServices.lifecycle
|
|
23
27
|
},
|
|
24
|
-
async init({
|
|
28
|
+
async init({
|
|
29
|
+
config,
|
|
30
|
+
catalog,
|
|
31
|
+
catalogScmEvents,
|
|
32
|
+
logger,
|
|
33
|
+
scheduler,
|
|
34
|
+
events,
|
|
35
|
+
lifecycle
|
|
36
|
+
}) {
|
|
25
37
|
const gitlabDiscoveryEntityProvider = GitlabDiscoveryEntityProvider.GitlabDiscoveryEntityProvider.fromConfig(config, {
|
|
26
38
|
logger,
|
|
27
39
|
events,
|
|
28
40
|
scheduler
|
|
29
41
|
});
|
|
30
42
|
catalog.addEntityProvider(gitlabDiscoveryEntityProvider);
|
|
43
|
+
const bridge = new GitLabScmEventsBridge.GitLabScmEventsBridge({
|
|
44
|
+
logger,
|
|
45
|
+
events,
|
|
46
|
+
catalogScmEvents
|
|
47
|
+
});
|
|
48
|
+
lifecycle.addStartupHook(async () => {
|
|
49
|
+
await bridge.start();
|
|
50
|
+
});
|
|
51
|
+
lifecycle.addShutdownHook(async () => {
|
|
52
|
+
await bridge.stop();
|
|
53
|
+
});
|
|
31
54
|
}
|
|
32
55
|
});
|
|
33
56
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalogModuleGitlabDiscoveryEntityProvider.cjs.js","sources":["../../src/module/catalogModuleGitlabDiscoveryEntityProvider.ts"],"sourcesContent":["/*\n * Copyright 2022 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 {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node';\nimport { eventsServiceRef } from '@backstage/plugin-events-node';\nimport { GitlabDiscoveryEntityProvider } from '../providers';\n\n/**\n * Registers the GitlabDiscoveryEntityProvider with the catalog processing extension point.\n *\n * @public\n */\n\nexport const catalogModuleGitlabDiscoveryEntityProvider = createBackendModule({\n pluginId: 'catalog',\n moduleId: 'gitlab-discovery-entity-provider',\n register(env) {\n env.registerInit({\n deps: {\n config: coreServices.rootConfig,\n catalog: catalogProcessingExtensionPoint,\n logger: coreServices.logger,\n scheduler: coreServices.scheduler,\n events: eventsServiceRef,\n },\n async init({
|
|
1
|
+
{"version":3,"file":"catalogModuleGitlabDiscoveryEntityProvider.cjs.js","sources":["../../src/module/catalogModuleGitlabDiscoveryEntityProvider.ts"],"sourcesContent":["/*\n * Copyright 2022 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 {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node';\nimport { catalogScmEventsServiceRef } from '@backstage/plugin-catalog-node/alpha';\nimport { eventsServiceRef } from '@backstage/plugin-events-node';\nimport { GitLabScmEventsBridge } from '../events/GitLabScmEventsBridge';\nimport { GitlabDiscoveryEntityProvider } from '../providers';\n\n/**\n * Registers the GitlabDiscoveryEntityProvider with the catalog processing extension point.\n *\n * @public\n */\n\nexport const catalogModuleGitlabDiscoveryEntityProvider = createBackendModule({\n pluginId: 'catalog',\n moduleId: 'gitlab-discovery-entity-provider',\n register(env) {\n env.registerInit({\n deps: {\n config: coreServices.rootConfig,\n catalog: catalogProcessingExtensionPoint,\n catalogScmEvents: catalogScmEventsServiceRef,\n logger: coreServices.logger,\n scheduler: coreServices.scheduler,\n events: eventsServiceRef,\n lifecycle: coreServices.lifecycle,\n },\n async init({\n config,\n catalog,\n catalogScmEvents,\n logger,\n scheduler,\n events,\n lifecycle,\n }) {\n const gitlabDiscoveryEntityProvider =\n GitlabDiscoveryEntityProvider.fromConfig(config, {\n logger,\n events,\n scheduler,\n });\n catalog.addEntityProvider(gitlabDiscoveryEntityProvider);\n\n const bridge = new GitLabScmEventsBridge({\n logger,\n events,\n catalogScmEvents,\n });\n\n lifecycle.addStartupHook(async () => {\n await bridge.start();\n });\n lifecycle.addShutdownHook(async () => {\n await bridge.stop();\n });\n },\n });\n },\n});\n"],"names":["createBackendModule","coreServices","catalogProcessingExtensionPoint","catalogScmEventsServiceRef","eventsServiceRef","GitlabDiscoveryEntityProvider","GitLabScmEventsBridge"],"mappings":";;;;;;;;;;;;;AAgCO,MAAM,6CAA6CA,oCAAA,CAAoB;AAAA,EAC5E,QAAA,EAAU,SAAA;AAAA,EACV,QAAA,EAAU,kCAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,QAAQC,6BAAA,CAAa,UAAA;AAAA,QACrB,OAAA,EAASC,iDAAA;AAAA,QACT,gBAAA,EAAkBC,gCAAA;AAAA,QAClB,QAAQF,6BAAA,CAAa,MAAA;AAAA,QACrB,WAAWA,6BAAA,CAAa,SAAA;AAAA,QACxB,MAAA,EAAQG,iCAAA;AAAA,QACR,WAAWH,6BAAA,CAAa;AAAA,OAC1B;AAAA,MACA,MAAM,IAAA,CAAK;AAAA,QACT,MAAA;AAAA,QACA,OAAA;AAAA,QACA,gBAAA;AAAA,QACA,MAAA;AAAA,QACA,SAAA;AAAA,QACA,MAAA;AAAA,QACA;AAAA,OACF,EAAG;AACD,QAAA,MAAM,6BAAA,GACJI,2DAAA,CAA8B,UAAA,CAAW,MAAA,EAAQ;AAAA,UAC/C,MAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACD,CAAA;AACH,QAAA,OAAA,CAAQ,kBAAkB,6BAA6B,CAAA;AAEvD,QAAA,MAAM,MAAA,GAAS,IAAIC,2CAAA,CAAsB;AAAA,UACvC,MAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACD,CAAA;AAED,QAAA,SAAA,CAAU,eAAe,YAAY;AACnC,UAAA,MAAM,OAAO,KAAA,EAAM;AAAA,QACrB,CAAC,CAAA;AACD,QAAA,SAAA,CAAU,gBAAgB,YAAY;AACpC,UAAA,MAAM,OAAO,IAAA,EAAK;AAAA,QACpB,CAAC,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-catalog-backend-module-gitlab",
|
|
3
|
-
"version": "0.8.2-next.
|
|
3
|
+
"version": "0.8.2-next.1",
|
|
4
4
|
"description": "A Backstage catalog backend module that helps integrate towards GitLab",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "backend-plugin-module",
|
|
@@ -65,22 +65,23 @@
|
|
|
65
65
|
"test": "backstage-cli package test"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@backstage/backend-defaults": "0.16.1-next.
|
|
69
|
-
"@backstage/backend-plugin-api": "1.
|
|
68
|
+
"@backstage/backend-defaults": "0.16.1-next.1",
|
|
69
|
+
"@backstage/backend-plugin-api": "1.9.0-next.1",
|
|
70
70
|
"@backstage/catalog-model": "1.7.7",
|
|
71
71
|
"@backstage/config": "1.3.6",
|
|
72
|
+
"@backstage/errors": "1.2.7",
|
|
72
73
|
"@backstage/integration": "2.0.0",
|
|
73
74
|
"@backstage/plugin-catalog-common": "1.1.8",
|
|
74
|
-
"@backstage/plugin-catalog-node": "2.1.1-next.
|
|
75
|
-
"@backstage/plugin-events-node": "0.4.21-next.
|
|
75
|
+
"@backstage/plugin-catalog-node": "2.1.1-next.1",
|
|
76
|
+
"@backstage/plugin-events-node": "0.4.21-next.1",
|
|
76
77
|
"@gitbeaker/rest": "^40.0.3",
|
|
77
78
|
"lodash": "^4.17.21",
|
|
78
79
|
"node-fetch": "^2.7.0",
|
|
79
80
|
"uuid": "^11.0.0"
|
|
80
81
|
},
|
|
81
82
|
"devDependencies": {
|
|
82
|
-
"@backstage/backend-test-utils": "1.11.2-next.
|
|
83
|
-
"@backstage/cli": "0.36.1-next.
|
|
83
|
+
"@backstage/backend-test-utils": "1.11.2-next.1",
|
|
84
|
+
"@backstage/cli": "0.36.1-next.1",
|
|
84
85
|
"@backstage/plugin-events-backend-test-utils": "0.1.54-next.0",
|
|
85
86
|
"@types/lodash": "^4.14.151",
|
|
86
87
|
"msw": "^1.0.0"
|