@backstage/cli 0.22.8-next.0 → 0.22.8-next.2
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 +34 -0
- package/dist/cjs/{build-0f8f0f78.cjs.js → build-94b166b9.cjs.js} +7 -7
- package/dist/cjs/{buildBackend-b6668077.cjs.js → buildBackend-1610ed47.cjs.js} +6 -6
- package/dist/cjs/{buildWorkspace-a1ad431d.cjs.js → buildWorkspace-0729ac23.cjs.js} +4 -4
- package/dist/cjs/{bump-d637f839.cjs.js → bump-2e41888b.cjs.js} +5 -5
- package/dist/cjs/{clean-03329c21.cjs.js → clean-707fb954.cjs.js} +2 -2
- package/dist/cjs/{clean-3ebe5831.cjs.js → clean-fef9647d.cjs.js} +2 -2
- package/dist/cjs/{codeowners-a1dfa089.cjs.js → codeowners-5b10b186.cjs.js} +2 -2
- package/dist/cjs/{config-5e07cd44.cjs.js → config-1d171d7e.cjs.js} +2 -2
- package/dist/cjs/{createDistWorkspace-e9c7663c.cjs.js → createDistWorkspace-6f0f28d5.cjs.js} +3 -3
- package/dist/cjs/{createPlugin-7387334c.cjs.js → createPlugin-33d16874.cjs.js} +4 -4
- package/dist/cjs/{diff-ca3e092b.cjs.js → diff-c80533a8.cjs.js} +2 -2
- package/dist/cjs/{docs-30d19467.cjs.js → docs-c23fb420.cjs.js} +3 -3
- package/dist/cjs/{fix-dad6e2d6.cjs.js → fix-7ed7159b.cjs.js} +2 -2
- package/dist/cjs/{index-f42dfc80.cjs.js → index-4438059a.cjs.js} +8 -8
- package/dist/cjs/index-7b1afb9b.cjs.js +1211 -0
- package/dist/cjs/{index-a8c49a1b.cjs.js → index-a83455a6.cjs.js} +7 -7
- package/dist/cjs/{index-b5e86b0d.cjs.js → index-ad52f12e.cjs.js} +47 -42
- package/dist/cjs/{index-28a697ef.cjs.js → index-dc7cc004.cjs.js} +2 -2
- package/dist/cjs/{info-76600f2a.cjs.js → info-5202a3df.cjs.js} +3 -3
- package/dist/cjs/{install-b198cdab.cjs.js → install-ad1fcacf.cjs.js} +4 -4
- package/dist/cjs/{lint-2ebffd14.cjs.js → lint-0c3133b5.cjs.js} +2 -2
- package/dist/cjs/{lint-a2342595.cjs.js → lint-733ca435.cjs.js} +3 -3
- package/dist/cjs/{lint-dfb472ac.cjs.js → lint-784a0c3b.cjs.js} +2 -2
- package/dist/cjs/{list-deprecations-87e249f0.cjs.js → list-deprecations-9eac9ad6.cjs.js} +2 -2
- package/dist/cjs/{new-5f99b9c8.cjs.js → new-dd994b5f.cjs.js} +4 -4
- package/dist/cjs/{pack-273421b1.cjs.js → pack-5a0cf2b2.cjs.js} +2 -2
- package/dist/cjs/{packageLintConfigs-87149dd5.cjs.js → packageLintConfigs-0041e26e.cjs.js} +3 -3
- package/dist/cjs/{packageRole-be3239ca.cjs.js → packageRole-908ff155.cjs.js} +2 -2
- package/dist/cjs/{packages-9fc68dee.cjs.js → packages-93128416.cjs.js} +3 -3
- package/dist/cjs/{paths-d8c1f54e.cjs.js → paths-82663f40.cjs.js} +3 -3
- package/dist/cjs/{print-ed7ecdb5.cjs.js → print-64356633.cjs.js} +3 -3
- package/dist/cjs/{role-a7faefb6.cjs.js → role-1007229a.cjs.js} +2 -2
- package/dist/cjs/{run-cdf3f08c.cjs.js → run-b5198e24.cjs.js} +2 -2
- package/dist/cjs/{schema-010879d0.cjs.js → schema-04967f8e.cjs.js} +3 -3
- package/dist/cjs/{test-09399c75.cjs.js → test-dcc99d38.cjs.js} +3 -3
- package/dist/cjs/{test-a7b87e2f.cjs.js → test-fc1fa743.cjs.js} +3 -3
- package/dist/cjs/{validate-81a7e4c3.cjs.js → validate-e8119f4f.cjs.js} +3 -3
- package/dist/index.cjs.js +1 -1
- package/package.json +16 -11
- package/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs +12 -18
- package/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs +231 -18
- package/dist/cjs/index-9b9f70f2.cjs.js +0 -471
|
@@ -0,0 +1,1211 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chalk = require('chalk');
|
|
4
|
+
var inquirer = require('inquirer');
|
|
5
|
+
var tasks = require('./tasks-84de240c.cjs.js');
|
|
6
|
+
var oauthApp = require('@octokit/oauth-app');
|
|
7
|
+
var fs = require('fs-extra');
|
|
8
|
+
var yaml = require('yaml');
|
|
9
|
+
var cliCommon = require('@backstage/cli-common');
|
|
10
|
+
var path = require('path');
|
|
11
|
+
var differ = require('diff');
|
|
12
|
+
var config = require('./config-1d171d7e.cjs.js');
|
|
13
|
+
var catalogModel = require('@backstage/catalog-model');
|
|
14
|
+
var z = require('zod');
|
|
15
|
+
var integration = require('@backstage/integration');
|
|
16
|
+
var graphql = require('@octokit/graphql');
|
|
17
|
+
var parseGitUrl = require('git-url-parse');
|
|
18
|
+
var fetch = require('node-fetch');
|
|
19
|
+
require('handlebars');
|
|
20
|
+
require('ora');
|
|
21
|
+
require('util');
|
|
22
|
+
require('recursive-readdir');
|
|
23
|
+
require('child_process');
|
|
24
|
+
require('@backstage/errors');
|
|
25
|
+
require('@backstage/config-loader');
|
|
26
|
+
require('@backstage/config');
|
|
27
|
+
require('./index-ad52f12e.cjs.js');
|
|
28
|
+
require('commander');
|
|
29
|
+
require('semver');
|
|
30
|
+
require('@manypkg/get-packages');
|
|
31
|
+
require('@backstage/cli-node');
|
|
32
|
+
|
|
33
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
34
|
+
|
|
35
|
+
function _interopNamespace(e) {
|
|
36
|
+
if (e && e.__esModule) return e;
|
|
37
|
+
var n = Object.create(null);
|
|
38
|
+
if (e) {
|
|
39
|
+
Object.keys(e).forEach(function (k) {
|
|
40
|
+
if (k !== 'default') {
|
|
41
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
42
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () { return e[k]; }
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
n["default"] = e;
|
|
50
|
+
return Object.freeze(n);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
|
|
54
|
+
var inquirer__default = /*#__PURE__*/_interopDefaultLegacy(inquirer);
|
|
55
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
56
|
+
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
57
|
+
var yaml__default = /*#__PURE__*/_interopDefaultLegacy(yaml);
|
|
58
|
+
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
59
|
+
var differ__namespace = /*#__PURE__*/_interopNamespace(differ);
|
|
60
|
+
var z__default = /*#__PURE__*/_interopDefaultLegacy(z);
|
|
61
|
+
var parseGitUrl__default = /*#__PURE__*/_interopDefaultLegacy(parseGitUrl);
|
|
62
|
+
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
|
|
63
|
+
|
|
64
|
+
const readYaml = async (file) => {
|
|
65
|
+
return yaml__default["default"].parse(await fs__namespace.readFile(file, "utf8"));
|
|
66
|
+
};
|
|
67
|
+
const updateConfigFile = async (file, config) => {
|
|
68
|
+
const staticContent = "# Backstage override configuration for your local development environment \n";
|
|
69
|
+
const content = fs__namespace.existsSync(file) ? yaml__default["default"].stringify(
|
|
70
|
+
{ ...await readYaml(file), ...config },
|
|
71
|
+
{
|
|
72
|
+
indent: 2
|
|
73
|
+
}
|
|
74
|
+
) : staticContent.concat(
|
|
75
|
+
yaml__default["default"].stringify(
|
|
76
|
+
{ ...config },
|
|
77
|
+
{
|
|
78
|
+
indent: 2
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
);
|
|
82
|
+
return await fs__namespace.writeFile(file, content, "utf8");
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const { targetRoot: targetRoot$1, ownDir } = cliCommon.findPaths(__dirname);
|
|
86
|
+
const APP_CONFIG_FILE = path__namespace.join(targetRoot$1, "app-config.local.yaml");
|
|
87
|
+
const DISCOVERED_ENTITIES_FILE = path__namespace.join(
|
|
88
|
+
targetRoot$1,
|
|
89
|
+
"examples",
|
|
90
|
+
"discovered-entities.yaml"
|
|
91
|
+
);
|
|
92
|
+
const PATCH_FOLDER = path__namespace.join(
|
|
93
|
+
ownDir,
|
|
94
|
+
"src",
|
|
95
|
+
"commands",
|
|
96
|
+
"onboard",
|
|
97
|
+
"auth",
|
|
98
|
+
"patches"
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const { targetRoot } = cliCommon.findPaths(__dirname);
|
|
102
|
+
const patch = async (patchFile) => {
|
|
103
|
+
const patchContent = await fs__namespace.readFile(
|
|
104
|
+
path__namespace.join(PATCH_FOLDER, patchFile),
|
|
105
|
+
"utf8"
|
|
106
|
+
);
|
|
107
|
+
const targetName = patchContent.split("\n")[0].replace("--- a", "");
|
|
108
|
+
const targetFile = path__namespace.join(targetRoot, targetName);
|
|
109
|
+
const oldContent = await fs__namespace.readFile(targetFile, "utf8");
|
|
110
|
+
const newContent = differ__namespace.applyPatch(oldContent, patchContent);
|
|
111
|
+
if (!newContent) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Patch ${patchFile} was not applied correctly.
|
|
114
|
+
Did you change ${targetName} manually before running this command?`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return await fs__namespace.writeFile(targetFile, newContent, "utf8");
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const validateCredentials = async (clientId, clientSecret) => {
|
|
121
|
+
try {
|
|
122
|
+
const app = new oauthApp.OAuthApp({
|
|
123
|
+
clientId,
|
|
124
|
+
clientSecret
|
|
125
|
+
});
|
|
126
|
+
await app.createToken({
|
|
127
|
+
code: "%NOT-VALID-CODE%"
|
|
128
|
+
});
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (error.response.status !== 200 && error.response.data.error !== "bad_verification_code") {
|
|
131
|
+
throw new Error(`Validating GitHub Credentials failed.`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
const getConfig$2 = (answers) => {
|
|
136
|
+
const { clientId, clientSecret, hasEnterprise, enterpriseInstanceUrl } = answers;
|
|
137
|
+
return {
|
|
138
|
+
auth: {
|
|
139
|
+
providers: {
|
|
140
|
+
github: {
|
|
141
|
+
development: {
|
|
142
|
+
clientId,
|
|
143
|
+
clientSecret,
|
|
144
|
+
...hasEnterprise && {
|
|
145
|
+
enterpriseInstanceUrl
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
const github$1 = async () => {
|
|
154
|
+
tasks.Task.log(`
|
|
155
|
+
To add GitHub authentication, you must create an OAuth App from the GitHub developer settings: ${chalk__default["default"].blue(
|
|
156
|
+
"https://github.com/settings/developers"
|
|
157
|
+
)}
|
|
158
|
+
The Homepage URL should point to Backstage's frontend, while the Authorization callback URL will point to the auth backend.
|
|
159
|
+
|
|
160
|
+
Settings for local development:
|
|
161
|
+
${chalk__default["default"].cyan(`
|
|
162
|
+
Homepage URL: http://localhost:3000
|
|
163
|
+
Authorization callback URL: http://localhost:7007/api/auth/github/handler/frame`)}
|
|
164
|
+
|
|
165
|
+
You can find the full documentation page here: ${chalk__default["default"].blue(
|
|
166
|
+
"https://backstage.io/docs/auth/github/provider"
|
|
167
|
+
)}
|
|
168
|
+
`);
|
|
169
|
+
const answers = await inquirer__default["default"].prompt([
|
|
170
|
+
{
|
|
171
|
+
type: "input",
|
|
172
|
+
name: "clientId",
|
|
173
|
+
message: "What is your Client Id?",
|
|
174
|
+
validate: (input) => input.length ? true : false
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
type: "input",
|
|
178
|
+
name: "clientSecret",
|
|
179
|
+
message: "What is your Client Secret?",
|
|
180
|
+
validate: (input) => input.length ? true : false
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
type: "confirm",
|
|
184
|
+
name: "hasEnterprise",
|
|
185
|
+
message: "Are you using GitHub Enterprise?"
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
type: "input",
|
|
189
|
+
name: "enterpriseInstanceUrl",
|
|
190
|
+
message: "What is your URL for GitHub Enterprise?",
|
|
191
|
+
when: ({ hasEnterprise }) => hasEnterprise,
|
|
192
|
+
validate: (input) => Boolean(new URL(input))
|
|
193
|
+
}
|
|
194
|
+
]);
|
|
195
|
+
const { clientId, clientSecret } = answers;
|
|
196
|
+
const config = getConfig$2(answers);
|
|
197
|
+
tasks.Task.log("Setting up GitHub Authentication for you...");
|
|
198
|
+
await tasks.Task.forItem(
|
|
199
|
+
"Validating",
|
|
200
|
+
"credentials",
|
|
201
|
+
async () => await validateCredentials(clientId, clientSecret)
|
|
202
|
+
);
|
|
203
|
+
await tasks.Task.forItem(
|
|
204
|
+
"Updating",
|
|
205
|
+
APP_CONFIG_FILE,
|
|
206
|
+
async () => await updateConfigFile(APP_CONFIG_FILE, config)
|
|
207
|
+
);
|
|
208
|
+
const patches = await fs__namespace.readdir(PATCH_FOLDER);
|
|
209
|
+
for (const patchFile of patches.filter((p) => p.includes("github"))) {
|
|
210
|
+
await tasks.Task.forItem("Patching", patchFile, async () => {
|
|
211
|
+
await patch(patchFile);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return answers;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const getConfig$1 = (answers) => {
|
|
218
|
+
const { clientId, clientSecret, hasAudience, audience } = answers;
|
|
219
|
+
return {
|
|
220
|
+
auth: {
|
|
221
|
+
providers: {
|
|
222
|
+
gitlab: {
|
|
223
|
+
development: {
|
|
224
|
+
clientId,
|
|
225
|
+
clientSecret,
|
|
226
|
+
...hasAudience && {
|
|
227
|
+
audience
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
const gitlab = async () => {
|
|
236
|
+
tasks.Task.log(`
|
|
237
|
+
To add GitLab authentication, you must create an Application from the GitLab Settings: ${chalk__default["default"].blue(
|
|
238
|
+
"https://gitlab.com/-/profile/applications"
|
|
239
|
+
)}
|
|
240
|
+
The Redirect URI should point to your Backstage backend auth handler.
|
|
241
|
+
|
|
242
|
+
Settings for local development:
|
|
243
|
+
${chalk__default["default"].cyan(`
|
|
244
|
+
Name: Backstage (or your custom app name)
|
|
245
|
+
Redirect URI: http://localhost:7007/api/auth/gitlab/handler/frame
|
|
246
|
+
Scopes: read_api and read_user`)}
|
|
247
|
+
|
|
248
|
+
You can find the full documentation page here: ${chalk__default["default"].blue(
|
|
249
|
+
"https://backstage.io/docs/auth/gitlab/provider"
|
|
250
|
+
)}
|
|
251
|
+
`);
|
|
252
|
+
const answers = await inquirer__default["default"].prompt([
|
|
253
|
+
{
|
|
254
|
+
type: "input",
|
|
255
|
+
name: "clientId",
|
|
256
|
+
message: "What is your Application Id?",
|
|
257
|
+
validate: (input) => input.length ? true : false
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
type: "input",
|
|
261
|
+
name: "clientSecret",
|
|
262
|
+
message: "What is your Application Secret?",
|
|
263
|
+
validate: (input) => input.length ? true : false
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
type: "confirm",
|
|
267
|
+
name: "hasAudience",
|
|
268
|
+
message: "Do you have a self-hosted instance of GitLab?"
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
type: "input",
|
|
272
|
+
name: "audience",
|
|
273
|
+
message: "What is the URL for your GitLab instance?",
|
|
274
|
+
when: ({ hasAudience }) => hasAudience,
|
|
275
|
+
validate: (input) => Boolean(new URL(input))
|
|
276
|
+
}
|
|
277
|
+
]);
|
|
278
|
+
const config = getConfig$1(answers);
|
|
279
|
+
tasks.Task.log("Setting up GitLab Authentication for you...");
|
|
280
|
+
await tasks.Task.forItem(
|
|
281
|
+
"Updating",
|
|
282
|
+
APP_CONFIG_FILE,
|
|
283
|
+
async () => await updateConfigFile(APP_CONFIG_FILE, config)
|
|
284
|
+
);
|
|
285
|
+
const patches = await fs__namespace.readdir(PATCH_FOLDER);
|
|
286
|
+
for (const patchFile of patches.filter((p) => p.includes("gitlab"))) {
|
|
287
|
+
await tasks.Task.forItem("Patching", patchFile, async () => {
|
|
288
|
+
await patch(patchFile);
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
return answers;
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
async function auth() {
|
|
295
|
+
const answers = await inquirer__default["default"].prompt([
|
|
296
|
+
{
|
|
297
|
+
type: "list",
|
|
298
|
+
name: "provider",
|
|
299
|
+
message: "Please select an authentication provider:",
|
|
300
|
+
choices: ["GitHub", "GitLab"]
|
|
301
|
+
}
|
|
302
|
+
]);
|
|
303
|
+
const { provider } = answers;
|
|
304
|
+
let providerAnswers;
|
|
305
|
+
switch (provider) {
|
|
306
|
+
case "GitHub": {
|
|
307
|
+
providerAnswers = await github$1();
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
case "GitLab": {
|
|
311
|
+
providerAnswers = await gitlab();
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
default:
|
|
315
|
+
throw new Error(`Provider ${provider} not implemented yet.`);
|
|
316
|
+
}
|
|
317
|
+
tasks.Task.log();
|
|
318
|
+
tasks.Task.log(`Done setting up ${provider} Authentication!`);
|
|
319
|
+
tasks.Task.log();
|
|
320
|
+
return {
|
|
321
|
+
provider,
|
|
322
|
+
answers: providerAnswers
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const getConfig = ({
|
|
327
|
+
hasEnterprise,
|
|
328
|
+
apiBaseUrl,
|
|
329
|
+
host,
|
|
330
|
+
token
|
|
331
|
+
}) => ({
|
|
332
|
+
integrations: {
|
|
333
|
+
github: [
|
|
334
|
+
{
|
|
335
|
+
host,
|
|
336
|
+
token,
|
|
337
|
+
...hasEnterprise && {
|
|
338
|
+
apiBaseUrl
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
const github = async (providerAnswers) => {
|
|
345
|
+
var _a, _b, _c;
|
|
346
|
+
const answers = await inquirer__default["default"].prompt([
|
|
347
|
+
{
|
|
348
|
+
type: "confirm",
|
|
349
|
+
name: "hasEnterprise",
|
|
350
|
+
message: "Are you using GitHub Enterprise?",
|
|
351
|
+
when: () => typeof providerAnswers === "undefined"
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
type: "input",
|
|
355
|
+
name: "enterpriseInstanceUrl",
|
|
356
|
+
message: "What is your URL for GitHub Enterprise?",
|
|
357
|
+
when: ({ hasEnterprise }) => hasEnterprise,
|
|
358
|
+
validate: (input) => Boolean(new URL(input))
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
type: "input",
|
|
362
|
+
name: "apiBaseUrl",
|
|
363
|
+
message: "What is your GitHub Enterprise API path?",
|
|
364
|
+
default: "/api/v3",
|
|
365
|
+
when: ({ hasEnterprise }) => hasEnterprise || (providerAnswers == null ? void 0 : providerAnswers.hasEnterprise)
|
|
366
|
+
// TODO(tudi2d): Fetch API using OAuth Token if Auth was set up
|
|
367
|
+
}
|
|
368
|
+
]);
|
|
369
|
+
const host = new URL(
|
|
370
|
+
(_b = (_a = providerAnswers == null ? void 0 : providerAnswers.enterpriseInstanceUrl) != null ? _a : answers == null ? void 0 : answers.enterpriseInstanceUrl) != null ? _b : "http://github.com"
|
|
371
|
+
);
|
|
372
|
+
tasks.Task.log(`
|
|
373
|
+
To create new repositories in GitHub using Software Templates you first need to create a personal access token: ${chalk__default["default"].blue(
|
|
374
|
+
`${host.origin}/settings/tokens/new`
|
|
375
|
+
)}
|
|
376
|
+
|
|
377
|
+
Select the following scopes:
|
|
378
|
+
|
|
379
|
+
Reading software components:${chalk__default["default"].cyan(`
|
|
380
|
+
- "repo"`)}
|
|
381
|
+
|
|
382
|
+
Reading organization data:${chalk__default["default"].cyan(`
|
|
383
|
+
- "read:org"
|
|
384
|
+
- "read:user"
|
|
385
|
+
- "user:email"`)}
|
|
386
|
+
|
|
387
|
+
Publishing software templates:${chalk__default["default"].cyan(`
|
|
388
|
+
- "repo"
|
|
389
|
+
- "workflow" (if templates include GitHub workflows)
|
|
390
|
+
`)}
|
|
391
|
+
|
|
392
|
+
You can find the full documentation page here: ${chalk__default["default"].blue(
|
|
393
|
+
"https://backstage.io/docs/integrations/github/locations"
|
|
394
|
+
)}
|
|
395
|
+
`);
|
|
396
|
+
const { token } = await inquirer__default["default"].prompt([
|
|
397
|
+
{
|
|
398
|
+
type: "input",
|
|
399
|
+
name: "token",
|
|
400
|
+
message: "Please insert your personal access token to setup the GitHub Integration"
|
|
401
|
+
// TODO(tudi2d): validate
|
|
402
|
+
}
|
|
403
|
+
]);
|
|
404
|
+
const config = getConfig({
|
|
405
|
+
hasEnterprise: (_c = providerAnswers == null ? void 0 : providerAnswers.hasEnterprise) != null ? _c : answers.hasEnterprise,
|
|
406
|
+
apiBaseUrl: host.origin + answers.apiBaseUrl,
|
|
407
|
+
host: host.hostname,
|
|
408
|
+
token
|
|
409
|
+
});
|
|
410
|
+
tasks.Task.log("Setting up Software Templates using GitHub integration for you...");
|
|
411
|
+
await tasks.Task.forItem(
|
|
412
|
+
"Updating",
|
|
413
|
+
APP_CONFIG_FILE,
|
|
414
|
+
async () => await updateConfigFile(APP_CONFIG_FILE, config)
|
|
415
|
+
);
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const Integrations = ["GitHub" /* GITHUB */];
|
|
419
|
+
async function integrations(providerInfo) {
|
|
420
|
+
const answers = await inquirer__default["default"].prompt([
|
|
421
|
+
{
|
|
422
|
+
type: "confirm",
|
|
423
|
+
name: "shouldUsePreviousProvider",
|
|
424
|
+
message: `Do you want to keep using ${providerInfo == null ? void 0 : providerInfo.provider} as your provider when setting up Software Templates?`,
|
|
425
|
+
when: () => (providerInfo == null ? void 0 : providerInfo.provider) && Object.values(Integrations).includes(
|
|
426
|
+
providerInfo.provider
|
|
427
|
+
)
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
// TODO(tudi2d): Let's start with one, but it should be multiple choice in the future
|
|
431
|
+
type: "list",
|
|
432
|
+
name: "integration",
|
|
433
|
+
message: "Please select an integration provider:",
|
|
434
|
+
choices: Integrations,
|
|
435
|
+
when: ({ shouldUsePreviousProvider }) => !shouldUsePreviousProvider
|
|
436
|
+
}
|
|
437
|
+
]);
|
|
438
|
+
if (answers.shouldUsePreviousProvider) {
|
|
439
|
+
answers.integration = providerInfo.provider;
|
|
440
|
+
}
|
|
441
|
+
switch (answers.integration) {
|
|
442
|
+
case "GitHub" /* GITHUB */: {
|
|
443
|
+
const providerAnswers = (providerInfo == null ? void 0 : providerInfo.provider) === "GitHub" ? providerInfo.answers : void 0;
|
|
444
|
+
await github(providerAnswers);
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
tasks.Task.log();
|
|
449
|
+
tasks.Task.log(`Done setting up ${answers.integration} Integration!`);
|
|
450
|
+
tasks.Task.log();
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
var __accessCheck$7 = (obj, member, msg) => {
|
|
454
|
+
if (!member.has(obj))
|
|
455
|
+
throw TypeError("Cannot " + msg);
|
|
456
|
+
};
|
|
457
|
+
var __privateGet$6 = (obj, member, getter) => {
|
|
458
|
+
__accessCheck$7(obj, member, "read from private field");
|
|
459
|
+
return getter ? getter.call(obj) : member.get(obj);
|
|
460
|
+
};
|
|
461
|
+
var __privateAdd$7 = (obj, member, value) => {
|
|
462
|
+
if (member.has(obj))
|
|
463
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
464
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
465
|
+
};
|
|
466
|
+
var _outputs;
|
|
467
|
+
class DefaultAnalysisOutputs {
|
|
468
|
+
constructor() {
|
|
469
|
+
__privateAdd$7(this, _outputs, /* @__PURE__ */ new Map());
|
|
470
|
+
}
|
|
471
|
+
produce(output) {
|
|
472
|
+
__privateGet$6(this, _outputs).set(output.entity.metadata.name, output);
|
|
473
|
+
}
|
|
474
|
+
list() {
|
|
475
|
+
return Array.from(__privateGet$6(this, _outputs)).map(([_, output]) => output);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
_outputs = new WeakMap();
|
|
479
|
+
|
|
480
|
+
var __accessCheck$6 = (obj, member, msg) => {
|
|
481
|
+
if (!member.has(obj))
|
|
482
|
+
throw TypeError("Cannot " + msg);
|
|
483
|
+
};
|
|
484
|
+
var __privateGet$5 = (obj, member, getter) => {
|
|
485
|
+
__accessCheck$6(obj, member, "read from private field");
|
|
486
|
+
return getter ? getter.call(obj) : member.get(obj);
|
|
487
|
+
};
|
|
488
|
+
var __privateAdd$6 = (obj, member, value) => {
|
|
489
|
+
if (member.has(obj))
|
|
490
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
491
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
492
|
+
};
|
|
493
|
+
var _providers, _analyzers;
|
|
494
|
+
class Discovery {
|
|
495
|
+
constructor() {
|
|
496
|
+
__privateAdd$6(this, _providers, []);
|
|
497
|
+
__privateAdd$6(this, _analyzers, []);
|
|
498
|
+
}
|
|
499
|
+
addProvider(provider) {
|
|
500
|
+
__privateGet$5(this, _providers).push(provider);
|
|
501
|
+
}
|
|
502
|
+
addAnalyzer(analyzer) {
|
|
503
|
+
__privateGet$5(this, _analyzers).push(analyzer);
|
|
504
|
+
}
|
|
505
|
+
async run(url) {
|
|
506
|
+
tasks.Task.log(`Running discovery for ${chalk__default["default"].cyan(url)}`);
|
|
507
|
+
const result = [];
|
|
508
|
+
for (const provider of __privateGet$5(this, _providers)) {
|
|
509
|
+
const repositories = await provider.discover(url);
|
|
510
|
+
if (repositories && repositories.length) {
|
|
511
|
+
tasks.Task.log(
|
|
512
|
+
`Discovered ${chalk__default["default"].cyan(
|
|
513
|
+
repositories.length
|
|
514
|
+
)} repositories for ${chalk__default["default"].cyan(provider.name())}`
|
|
515
|
+
);
|
|
516
|
+
for (const repository of repositories) {
|
|
517
|
+
await tasks.Task.forItem("Analyzing", repository.name, async () => {
|
|
518
|
+
const output = new DefaultAnalysisOutputs();
|
|
519
|
+
for (const analyzer of __privateGet$5(this, _analyzers)) {
|
|
520
|
+
await analyzer.analyzeRepository({ repository, output });
|
|
521
|
+
}
|
|
522
|
+
output.list().filter((entry) => entry.type === "entity").forEach(({ entity }) => result.push(entity));
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
tasks.Task.log(`Produced ${chalk__default["default"].cyan(result.length || "no")} entities`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
entities: result
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
_providers = new WeakMap();
|
|
534
|
+
_analyzers = new WeakMap();
|
|
535
|
+
|
|
536
|
+
class BasicRepositoryAnalyzer {
|
|
537
|
+
name() {
|
|
538
|
+
return BasicRepositoryAnalyzer.name;
|
|
539
|
+
}
|
|
540
|
+
async analyzeRepository(options) {
|
|
541
|
+
const entity = {
|
|
542
|
+
apiVersion: "backstage.io/v1alpha1",
|
|
543
|
+
kind: "Component",
|
|
544
|
+
metadata: {
|
|
545
|
+
name: options.repository.name,
|
|
546
|
+
...options.repository.description ? { description: options.repository.description } : {}
|
|
547
|
+
},
|
|
548
|
+
spec: {
|
|
549
|
+
type: "service",
|
|
550
|
+
lifecycle: "production",
|
|
551
|
+
owner: "user:guest"
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
options.output.produce({
|
|
555
|
+
type: "entity",
|
|
556
|
+
path: "/",
|
|
557
|
+
entity
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
class PackageJsonAnalyzer {
|
|
563
|
+
name() {
|
|
564
|
+
return PackageJsonAnalyzer.name;
|
|
565
|
+
}
|
|
566
|
+
async analyzeRepository(options) {
|
|
567
|
+
var _a, _b;
|
|
568
|
+
const packageJson = await options.repository.file("package.json");
|
|
569
|
+
if (!packageJson) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
const content = await readPackageJson(packageJson);
|
|
573
|
+
if (!content) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const name = (_a = sanitizeName(content == null ? void 0 : content.name)) != null ? _a : options.repository.name;
|
|
577
|
+
const entity = {
|
|
578
|
+
apiVersion: "backstage.io/v1alpha1",
|
|
579
|
+
kind: "Component",
|
|
580
|
+
metadata: {
|
|
581
|
+
name,
|
|
582
|
+
...options.repository.description ? { description: options.repository.description } : {},
|
|
583
|
+
tags: ["javascript"],
|
|
584
|
+
annotations: {
|
|
585
|
+
[catalogModel.ANNOTATION_SOURCE_LOCATION]: `url:${options.repository.url}`
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
spec: {
|
|
589
|
+
type: "website",
|
|
590
|
+
lifecycle: "production",
|
|
591
|
+
owner: "user:guest"
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
const decorate = options.output.list().find((entry) => entry.entity.metadata.name === name);
|
|
595
|
+
if (decorate) {
|
|
596
|
+
decorate.entity.spec = {
|
|
597
|
+
...decorate.entity.spec,
|
|
598
|
+
type: "website"
|
|
599
|
+
};
|
|
600
|
+
decorate.entity.metadata.tags = [
|
|
601
|
+
...(_b = decorate.entity.metadata.tags) != null ? _b : [],
|
|
602
|
+
"javascript"
|
|
603
|
+
];
|
|
604
|
+
decorate.entity.metadata.annotations = {
|
|
605
|
+
...decorate.entity.metadata.annotations,
|
|
606
|
+
[catalogModel.ANNOTATION_SOURCE_LOCATION]: `url:${options.repository.url}`
|
|
607
|
+
};
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
options.output.produce({
|
|
611
|
+
type: "entity",
|
|
612
|
+
path: "/",
|
|
613
|
+
entity
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
const packageSchema = z__default["default"].object({
|
|
618
|
+
name: z__default["default"].string().optional()
|
|
619
|
+
});
|
|
620
|
+
function sanitizeName(name) {
|
|
621
|
+
return name && name !== "root" ? name.replace(/[^a-z0-9A-Z]/g, "_").substring(0, 62) : void 0;
|
|
622
|
+
}
|
|
623
|
+
async function readPackageJson(file) {
|
|
624
|
+
try {
|
|
625
|
+
const text = await file.text();
|
|
626
|
+
const result = packageSchema.safeParse(JSON.parse(text));
|
|
627
|
+
if (!result.success) {
|
|
628
|
+
return void 0;
|
|
629
|
+
}
|
|
630
|
+
return { name: result.data.name };
|
|
631
|
+
} catch (e) {
|
|
632
|
+
return void 0;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
var __accessCheck$5 = (obj, member, msg) => {
|
|
637
|
+
if (!member.has(obj))
|
|
638
|
+
throw TypeError("Cannot " + msg);
|
|
639
|
+
};
|
|
640
|
+
var __privateGet$4 = (obj, member, getter) => {
|
|
641
|
+
__accessCheck$5(obj, member, "read from private field");
|
|
642
|
+
return getter ? getter.call(obj) : member.get(obj);
|
|
643
|
+
};
|
|
644
|
+
var __privateAdd$5 = (obj, member, value) => {
|
|
645
|
+
if (member.has(obj))
|
|
646
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
647
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
648
|
+
};
|
|
649
|
+
var __privateSet$4 = (obj, member, value, setter) => {
|
|
650
|
+
__accessCheck$5(obj, member, "write to private field");
|
|
651
|
+
setter ? setter.call(obj, value) : member.set(obj, value);
|
|
652
|
+
return value;
|
|
653
|
+
};
|
|
654
|
+
var _path$1, _content$1;
|
|
655
|
+
class GithubFile {
|
|
656
|
+
constructor(path, content) {
|
|
657
|
+
__privateAdd$5(this, _path$1, void 0);
|
|
658
|
+
__privateAdd$5(this, _content$1, void 0);
|
|
659
|
+
__privateSet$4(this, _path$1, path);
|
|
660
|
+
__privateSet$4(this, _content$1, content);
|
|
661
|
+
}
|
|
662
|
+
get path() {
|
|
663
|
+
return __privateGet$4(this, _path$1);
|
|
664
|
+
}
|
|
665
|
+
async text() {
|
|
666
|
+
return __privateGet$4(this, _content$1);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
_path$1 = new WeakMap();
|
|
670
|
+
_content$1 = new WeakMap();
|
|
671
|
+
|
|
672
|
+
var __accessCheck$4 = (obj, member, msg) => {
|
|
673
|
+
if (!member.has(obj))
|
|
674
|
+
throw TypeError("Cannot " + msg);
|
|
675
|
+
};
|
|
676
|
+
var __privateGet$3 = (obj, member, getter) => {
|
|
677
|
+
__accessCheck$4(obj, member, "read from private field");
|
|
678
|
+
return getter ? getter.call(obj) : member.get(obj);
|
|
679
|
+
};
|
|
680
|
+
var __privateAdd$4 = (obj, member, value) => {
|
|
681
|
+
if (member.has(obj))
|
|
682
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
683
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
684
|
+
};
|
|
685
|
+
var __privateSet$3 = (obj, member, value, setter) => {
|
|
686
|
+
__accessCheck$4(obj, member, "write to private field");
|
|
687
|
+
setter ? setter.call(obj, value) : member.set(obj, value);
|
|
688
|
+
return value;
|
|
689
|
+
};
|
|
690
|
+
var __privateMethod$3 = (obj, member, method) => {
|
|
691
|
+
__accessCheck$4(obj, member, "access private method");
|
|
692
|
+
return method;
|
|
693
|
+
};
|
|
694
|
+
var _client, _repo, _org, _getFileContent$1, getFileContent_fn$1;
|
|
695
|
+
class GithubRepository {
|
|
696
|
+
constructor(client, repo, org) {
|
|
697
|
+
__privateAdd$4(this, _getFileContent$1);
|
|
698
|
+
__privateAdd$4(this, _client, void 0);
|
|
699
|
+
__privateAdd$4(this, _repo, void 0);
|
|
700
|
+
__privateAdd$4(this, _org, void 0);
|
|
701
|
+
__privateSet$3(this, _client, client);
|
|
702
|
+
__privateSet$3(this, _repo, repo);
|
|
703
|
+
__privateSet$3(this, _org, org);
|
|
704
|
+
}
|
|
705
|
+
get url() {
|
|
706
|
+
return __privateGet$3(this, _repo).url;
|
|
707
|
+
}
|
|
708
|
+
get name() {
|
|
709
|
+
return __privateGet$3(this, _repo).name;
|
|
710
|
+
}
|
|
711
|
+
get owner() {
|
|
712
|
+
return __privateGet$3(this, _org);
|
|
713
|
+
}
|
|
714
|
+
get description() {
|
|
715
|
+
var _a;
|
|
716
|
+
return (_a = __privateGet$3(this, _repo).description) != null ? _a : void 0;
|
|
717
|
+
}
|
|
718
|
+
async file(filename) {
|
|
719
|
+
var _a;
|
|
720
|
+
const content = await __privateMethod$3(this, _getFileContent$1, getFileContent_fn$1).call(this, filename);
|
|
721
|
+
if (!content || content.isBinary || !content.text) {
|
|
722
|
+
return void 0;
|
|
723
|
+
}
|
|
724
|
+
return new GithubFile(filename, (_a = content.text) != null ? _a : "");
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
_client = new WeakMap();
|
|
728
|
+
_repo = new WeakMap();
|
|
729
|
+
_org = new WeakMap();
|
|
730
|
+
_getFileContent$1 = new WeakSet();
|
|
731
|
+
getFileContent_fn$1 = async function(filename) {
|
|
732
|
+
const query = `query RepoFiles($owner: String!, $name: String!, $expr: String!) {
|
|
733
|
+
repository(owner: $owner, name: $name) {
|
|
734
|
+
object(expression: $expr) {
|
|
735
|
+
...on Blob {
|
|
736
|
+
text
|
|
737
|
+
isBinary
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}`;
|
|
742
|
+
const response = await __privateGet$3(this, _client).call(this, query, {
|
|
743
|
+
name: __privateGet$3(this, _repo).name,
|
|
744
|
+
owner: __privateGet$3(this, _org),
|
|
745
|
+
expr: `HEAD:${filename}`
|
|
746
|
+
});
|
|
747
|
+
return response.repository.object;
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
var __accessCheck$3 = (obj, member, msg) => {
|
|
751
|
+
if (!member.has(obj))
|
|
752
|
+
throw TypeError("Cannot " + msg);
|
|
753
|
+
};
|
|
754
|
+
var __privateGet$2 = (obj, member, getter) => {
|
|
755
|
+
__accessCheck$3(obj, member, "read from private field");
|
|
756
|
+
return getter ? getter.call(obj) : member.get(obj);
|
|
757
|
+
};
|
|
758
|
+
var __privateAdd$3 = (obj, member, value) => {
|
|
759
|
+
if (member.has(obj))
|
|
760
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
761
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
762
|
+
};
|
|
763
|
+
var __privateSet$2 = (obj, member, value, setter) => {
|
|
764
|
+
__accessCheck$3(obj, member, "write to private field");
|
|
765
|
+
setter ? setter.call(obj, value) : member.set(obj, value);
|
|
766
|
+
return value;
|
|
767
|
+
};
|
|
768
|
+
var __privateMethod$2 = (obj, member, method) => {
|
|
769
|
+
__accessCheck$3(obj, member, "access private method");
|
|
770
|
+
return method;
|
|
771
|
+
};
|
|
772
|
+
var _envToken$1, _scmIntegrations$1, _credentialsProvider$1, _getRequestHeaders$1, getRequestHeaders_fn$1, _getOrganizationRepositories, getOrganizationRepositories_fn;
|
|
773
|
+
const _GithubDiscoveryProvider = class {
|
|
774
|
+
constructor(envToken, integrations, credentialsProvider) {
|
|
775
|
+
__privateAdd$3(this, _getRequestHeaders$1);
|
|
776
|
+
__privateAdd$3(this, _getOrganizationRepositories);
|
|
777
|
+
__privateAdd$3(this, _envToken$1, void 0);
|
|
778
|
+
__privateAdd$3(this, _scmIntegrations$1, void 0);
|
|
779
|
+
__privateAdd$3(this, _credentialsProvider$1, void 0);
|
|
780
|
+
__privateSet$2(this, _envToken$1, envToken);
|
|
781
|
+
__privateSet$2(this, _scmIntegrations$1, integrations);
|
|
782
|
+
__privateSet$2(this, _credentialsProvider$1, credentialsProvider);
|
|
783
|
+
}
|
|
784
|
+
static fromConfig(config) {
|
|
785
|
+
const envToken = process.env.GITHUB_TOKEN || void 0;
|
|
786
|
+
const scmIntegrations = integration.ScmIntegrations.fromConfig(config);
|
|
787
|
+
const credentialsProvider = integration.DefaultGithubCredentialsProvider.fromIntegrations(scmIntegrations);
|
|
788
|
+
return new _GithubDiscoveryProvider(
|
|
789
|
+
envToken,
|
|
790
|
+
scmIntegrations,
|
|
791
|
+
credentialsProvider
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
name() {
|
|
795
|
+
return "GitHub";
|
|
796
|
+
}
|
|
797
|
+
async discover(url) {
|
|
798
|
+
if (!url.startsWith("https://github.com/")) {
|
|
799
|
+
return false;
|
|
800
|
+
}
|
|
801
|
+
const scmIntegration = __privateGet$2(this, _scmIntegrations$1).github.byUrl(url);
|
|
802
|
+
if (!scmIntegration) {
|
|
803
|
+
throw new Error(`No GitHub integration found for ${url}`);
|
|
804
|
+
}
|
|
805
|
+
const parsed = parseGitUrl__default["default"](url);
|
|
806
|
+
const { name, organization } = parsed;
|
|
807
|
+
const org = organization || name;
|
|
808
|
+
const client = graphql.graphql.defaults({
|
|
809
|
+
baseUrl: scmIntegration.config.apiBaseUrl,
|
|
810
|
+
headers: await __privateMethod$2(this, _getRequestHeaders$1, getRequestHeaders_fn$1).call(this, url)
|
|
811
|
+
});
|
|
812
|
+
const { repositories } = await __privateMethod$2(this, _getOrganizationRepositories, getOrganizationRepositories_fn).call(this, client, org);
|
|
813
|
+
return repositories.filter((repo) => repo.url.startsWith(url)).map((repo) => new GithubRepository(client, repo, org));
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
let GithubDiscoveryProvider = _GithubDiscoveryProvider;
|
|
817
|
+
_envToken$1 = new WeakMap();
|
|
818
|
+
_scmIntegrations$1 = new WeakMap();
|
|
819
|
+
_credentialsProvider$1 = new WeakMap();
|
|
820
|
+
_getRequestHeaders$1 = new WeakSet();
|
|
821
|
+
getRequestHeaders_fn$1 = async function(url) {
|
|
822
|
+
const credentials = await __privateGet$2(this, _credentialsProvider$1).getCredentials({
|
|
823
|
+
url
|
|
824
|
+
});
|
|
825
|
+
if (credentials.headers) {
|
|
826
|
+
return credentials.headers;
|
|
827
|
+
} else if (credentials.token) {
|
|
828
|
+
return { authorization: `token ${credentials.token}` };
|
|
829
|
+
}
|
|
830
|
+
if (__privateGet$2(this, _envToken$1)) {
|
|
831
|
+
return { authorization: `token ${__privateGet$2(this, _envToken$1)}` };
|
|
832
|
+
}
|
|
833
|
+
throw new Error(
|
|
834
|
+
"No token available for GitHub, please configure your integrations or set a GITHUB_TOKEN env variable"
|
|
835
|
+
);
|
|
836
|
+
};
|
|
837
|
+
_getOrganizationRepositories = new WeakSet();
|
|
838
|
+
getOrganizationRepositories_fn = async function(client, org) {
|
|
839
|
+
var _a, _b;
|
|
840
|
+
const query = `query repositories($org: String!, $cursor: String) {
|
|
841
|
+
repositoryOwner(login: $org) {
|
|
842
|
+
login
|
|
843
|
+
repositories(first: 100, after: $cursor) {
|
|
844
|
+
nodes {
|
|
845
|
+
name
|
|
846
|
+
url
|
|
847
|
+
description
|
|
848
|
+
isArchived
|
|
849
|
+
isFork
|
|
850
|
+
}
|
|
851
|
+
pageInfo {
|
|
852
|
+
hasNextPage
|
|
853
|
+
endCursor
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}`;
|
|
858
|
+
const result = [];
|
|
859
|
+
let cursor = void 0;
|
|
860
|
+
let hasNextPage = true;
|
|
861
|
+
while (hasNextPage) {
|
|
862
|
+
const response = await client(query, {
|
|
863
|
+
org,
|
|
864
|
+
cursor
|
|
865
|
+
});
|
|
866
|
+
const { repositories: connection } = (_a = response.repositoryOwner) != null ? _a : {};
|
|
867
|
+
if (!connection) {
|
|
868
|
+
throw new Error(`Found no repositories for ${org}`);
|
|
869
|
+
}
|
|
870
|
+
for (const repository of (_b = connection.nodes) != null ? _b : []) {
|
|
871
|
+
if (repository && !repository.isArchived && !repository.isFork) {
|
|
872
|
+
result.push(repository);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
cursor = connection.pageInfo.endCursor;
|
|
876
|
+
hasNextPage = connection.pageInfo.hasNextPage;
|
|
877
|
+
}
|
|
878
|
+
return {
|
|
879
|
+
repositories: result
|
|
880
|
+
};
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
var __accessCheck$2 = (obj, member, msg) => {
|
|
884
|
+
if (!member.has(obj))
|
|
885
|
+
throw TypeError("Cannot " + msg);
|
|
886
|
+
};
|
|
887
|
+
var __privateGet$1 = (obj, member, getter) => {
|
|
888
|
+
__accessCheck$2(obj, member, "read from private field");
|
|
889
|
+
return getter ? getter.call(obj) : member.get(obj);
|
|
890
|
+
};
|
|
891
|
+
var __privateAdd$2 = (obj, member, value) => {
|
|
892
|
+
if (member.has(obj))
|
|
893
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
894
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
895
|
+
};
|
|
896
|
+
var __privateSet$1 = (obj, member, value, setter) => {
|
|
897
|
+
__accessCheck$2(obj, member, "write to private field");
|
|
898
|
+
setter ? setter.call(obj, value) : member.set(obj, value);
|
|
899
|
+
return value;
|
|
900
|
+
};
|
|
901
|
+
var _path, _content;
|
|
902
|
+
class GitlabFile {
|
|
903
|
+
constructor(path, content) {
|
|
904
|
+
__privateAdd$2(this, _path, void 0);
|
|
905
|
+
__privateAdd$2(this, _content, void 0);
|
|
906
|
+
__privateSet$1(this, _path, path);
|
|
907
|
+
__privateSet$1(this, _content, content);
|
|
908
|
+
}
|
|
909
|
+
get path() {
|
|
910
|
+
return __privateGet$1(this, _path);
|
|
911
|
+
}
|
|
912
|
+
async text() {
|
|
913
|
+
return __privateGet$1(this, _content);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
_path = new WeakMap();
|
|
917
|
+
_content = new WeakMap();
|
|
918
|
+
|
|
919
|
+
var __accessCheck$1 = (obj, member, msg) => {
|
|
920
|
+
if (!member.has(obj))
|
|
921
|
+
throw TypeError("Cannot " + msg);
|
|
922
|
+
};
|
|
923
|
+
var __privateAdd$1 = (obj, member, value) => {
|
|
924
|
+
if (member.has(obj))
|
|
925
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
926
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
927
|
+
};
|
|
928
|
+
var __privateMethod$1 = (obj, member, method) => {
|
|
929
|
+
__accessCheck$1(obj, member, "access private method");
|
|
930
|
+
return method;
|
|
931
|
+
};
|
|
932
|
+
var _getFileContent, getFileContent_fn, _getMainBranch, getMainBranch_fn;
|
|
933
|
+
class GitlabProject {
|
|
934
|
+
constructor(project, apiBaseUrl, headers) {
|
|
935
|
+
this.project = project;
|
|
936
|
+
this.apiBaseUrl = apiBaseUrl;
|
|
937
|
+
this.headers = headers;
|
|
938
|
+
__privateAdd$1(this, _getFileContent);
|
|
939
|
+
__privateAdd$1(this, _getMainBranch);
|
|
940
|
+
}
|
|
941
|
+
get url() {
|
|
942
|
+
return this.project.web_url;
|
|
943
|
+
}
|
|
944
|
+
get name() {
|
|
945
|
+
return this.project.name;
|
|
946
|
+
}
|
|
947
|
+
get owner() {
|
|
948
|
+
return this.project.owner.username;
|
|
949
|
+
}
|
|
950
|
+
get description() {
|
|
951
|
+
return this.project.description;
|
|
952
|
+
}
|
|
953
|
+
async file(filename) {
|
|
954
|
+
const mainBranch = await __privateMethod$1(this, _getMainBranch, getMainBranch_fn).call(this);
|
|
955
|
+
const content = await __privateMethod$1(this, _getFileContent, getFileContent_fn).call(this, filename, mainBranch);
|
|
956
|
+
return new GitlabFile(filename, content);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
_getFileContent = new WeakSet();
|
|
960
|
+
getFileContent_fn = async function(path, mainBranch) {
|
|
961
|
+
const response = await fetch__default["default"](
|
|
962
|
+
`${this.apiBaseUrl}/projects/${this.project.id}/repository/files/${path}?ref=${mainBranch}`,
|
|
963
|
+
{ headers: this.headers }
|
|
964
|
+
);
|
|
965
|
+
const { content } = await response.json();
|
|
966
|
+
return Buffer.from(content, "base64").toString("ascii");
|
|
967
|
+
};
|
|
968
|
+
_getMainBranch = new WeakSet();
|
|
969
|
+
getMainBranch_fn = async function() {
|
|
970
|
+
var _a, _b;
|
|
971
|
+
const response = await fetch__default["default"](
|
|
972
|
+
`${this.apiBaseUrl}/projects/${this.project.id}/repository/branches`,
|
|
973
|
+
{ headers: this.headers }
|
|
974
|
+
);
|
|
975
|
+
const branches = await response.json();
|
|
976
|
+
return (_b = (_a = branches.find((branch) => branch.default)) == null ? void 0 : _a.name) != null ? _b : "main";
|
|
977
|
+
};
|
|
978
|
+
|
|
979
|
+
var __accessCheck = (obj, member, msg) => {
|
|
980
|
+
if (!member.has(obj))
|
|
981
|
+
throw TypeError("Cannot " + msg);
|
|
982
|
+
};
|
|
983
|
+
var __privateGet = (obj, member, getter) => {
|
|
984
|
+
__accessCheck(obj, member, "read from private field");
|
|
985
|
+
return getter ? getter.call(obj) : member.get(obj);
|
|
986
|
+
};
|
|
987
|
+
var __privateAdd = (obj, member, value) => {
|
|
988
|
+
if (member.has(obj))
|
|
989
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
990
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
991
|
+
};
|
|
992
|
+
var __privateSet = (obj, member, value, setter) => {
|
|
993
|
+
__accessCheck(obj, member, "write to private field");
|
|
994
|
+
setter ? setter.call(obj, value) : member.set(obj, value);
|
|
995
|
+
return value;
|
|
996
|
+
};
|
|
997
|
+
var __privateMethod = (obj, member, method) => {
|
|
998
|
+
__accessCheck(obj, member, "access private method");
|
|
999
|
+
return method;
|
|
1000
|
+
};
|
|
1001
|
+
var _envToken, _scmIntegrations, _credentialsProvider, _getRequestHeaders, getRequestHeaders_fn;
|
|
1002
|
+
const _GitlabDiscoveryProvider = class {
|
|
1003
|
+
constructor(envToken, integrations, credentialsProvider) {
|
|
1004
|
+
__privateAdd(this, _getRequestHeaders);
|
|
1005
|
+
__privateAdd(this, _envToken, void 0);
|
|
1006
|
+
__privateAdd(this, _scmIntegrations, void 0);
|
|
1007
|
+
__privateAdd(this, _credentialsProvider, void 0);
|
|
1008
|
+
__privateSet(this, _envToken, envToken);
|
|
1009
|
+
__privateSet(this, _scmIntegrations, integrations);
|
|
1010
|
+
__privateSet(this, _credentialsProvider, credentialsProvider);
|
|
1011
|
+
}
|
|
1012
|
+
static fromConfig(config) {
|
|
1013
|
+
const envToken = process.env.GITLAB_TOKEN || void 0;
|
|
1014
|
+
const scmIntegrations = integration.ScmIntegrations.fromConfig(config);
|
|
1015
|
+
const credentialsProvider = integration.DefaultGitlabCredentialsProvider.fromIntegrations(scmIntegrations);
|
|
1016
|
+
return new _GitlabDiscoveryProvider(
|
|
1017
|
+
envToken,
|
|
1018
|
+
scmIntegrations,
|
|
1019
|
+
credentialsProvider
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
name() {
|
|
1023
|
+
return "GitLab";
|
|
1024
|
+
}
|
|
1025
|
+
async discover(url) {
|
|
1026
|
+
const { origin, pathname } = new URL(url);
|
|
1027
|
+
const [, user] = pathname.split("/");
|
|
1028
|
+
const scmIntegration = __privateGet(this, _scmIntegrations).gitlab.byUrl(origin);
|
|
1029
|
+
if (!scmIntegration) {
|
|
1030
|
+
throw new Error(`No GitLab integration found for ${origin}`);
|
|
1031
|
+
}
|
|
1032
|
+
const headers = await __privateMethod(this, _getRequestHeaders, getRequestHeaders_fn).call(this, origin);
|
|
1033
|
+
const response = await fetch__default["default"](
|
|
1034
|
+
`${scmIntegration.config.apiBaseUrl}/users/${user}/projects`,
|
|
1035
|
+
{ headers }
|
|
1036
|
+
);
|
|
1037
|
+
if (!response.ok) {
|
|
1038
|
+
throw new Error(`${response.status} ${response.statusText}`);
|
|
1039
|
+
}
|
|
1040
|
+
const projects = await response.json();
|
|
1041
|
+
return projects.map(
|
|
1042
|
+
(project) => new GitlabProject(project, scmIntegration.config.apiBaseUrl, headers)
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
let GitlabDiscoveryProvider = _GitlabDiscoveryProvider;
|
|
1047
|
+
_envToken = new WeakMap();
|
|
1048
|
+
_scmIntegrations = new WeakMap();
|
|
1049
|
+
_credentialsProvider = new WeakMap();
|
|
1050
|
+
_getRequestHeaders = new WeakSet();
|
|
1051
|
+
getRequestHeaders_fn = async function(url) {
|
|
1052
|
+
const credentials = await __privateGet(this, _credentialsProvider).getCredentials({
|
|
1053
|
+
url
|
|
1054
|
+
});
|
|
1055
|
+
if (credentials.headers) {
|
|
1056
|
+
return credentials.headers;
|
|
1057
|
+
} else if (credentials.token) {
|
|
1058
|
+
return { authorization: `Bearer ${credentials.token}` };
|
|
1059
|
+
}
|
|
1060
|
+
if (__privateGet(this, _envToken)) {
|
|
1061
|
+
return { authorization: `Bearer ${__privateGet(this, _envToken)}` };
|
|
1062
|
+
}
|
|
1063
|
+
throw new Error(
|
|
1064
|
+
"No token available for GitLab, please set a GITLAB_TOKEN env variable"
|
|
1065
|
+
);
|
|
1066
|
+
};
|
|
1067
|
+
|
|
1068
|
+
async function discover(providerInfo) {
|
|
1069
|
+
tasks.Task.log(`
|
|
1070
|
+
Would you like to scan for - and create - Software Catalog entities?
|
|
1071
|
+
|
|
1072
|
+
You will need to select which SCM (Source Code Management) provider you are using,
|
|
1073
|
+
and then which repository or organization you want to scan.
|
|
1074
|
+
|
|
1075
|
+
This will generate a new file in the root of your app containing discovered entities,
|
|
1076
|
+
which will be included in the Software Catalog when you start up Backstage next time.
|
|
1077
|
+
|
|
1078
|
+
Note that this command requires an access token, which can be either added through the integration config or
|
|
1079
|
+
provided as an environment variable.
|
|
1080
|
+
`);
|
|
1081
|
+
const answers = await inquirer__default["default"].prompt([
|
|
1082
|
+
{
|
|
1083
|
+
type: "confirm",
|
|
1084
|
+
name: "shouldContinue",
|
|
1085
|
+
message: "Do you want to continue?"
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
type: "list",
|
|
1089
|
+
name: "provider",
|
|
1090
|
+
message: "Please select which SCM provider you want to use:",
|
|
1091
|
+
choices: ["GitHub", "GitLab"],
|
|
1092
|
+
default: providerInfo == null ? void 0 : providerInfo.provider,
|
|
1093
|
+
when: ({ shouldContinue }) => shouldContinue
|
|
1094
|
+
},
|
|
1095
|
+
{
|
|
1096
|
+
type: "input",
|
|
1097
|
+
name: "url",
|
|
1098
|
+
message: `Which repository do you want to scan?`,
|
|
1099
|
+
when: ({ shouldContinue }) => shouldContinue,
|
|
1100
|
+
filter: (input, { provider }) => {
|
|
1101
|
+
if (provider === "GitLab") {
|
|
1102
|
+
return `https://gitlab.com/${input}`;
|
|
1103
|
+
}
|
|
1104
|
+
if (provider === "GitHub") {
|
|
1105
|
+
return `https://github.com/${input}`;
|
|
1106
|
+
}
|
|
1107
|
+
return false;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
]);
|
|
1111
|
+
if (!answers.shouldContinue) {
|
|
1112
|
+
tasks.Task.log(
|
|
1113
|
+
chalk__default["default"].yellow(
|
|
1114
|
+
"If you change your mind, feel free to re-run this command."
|
|
1115
|
+
)
|
|
1116
|
+
);
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
const { fullConfig: config$1 } = await config.loadCliConfig({ args: [] });
|
|
1120
|
+
const discovery = new Discovery();
|
|
1121
|
+
if (answers.provider === "GitHub") {
|
|
1122
|
+
discovery.addProvider(GithubDiscoveryProvider.fromConfig(config$1));
|
|
1123
|
+
}
|
|
1124
|
+
if (answers.provider === "GitLab") {
|
|
1125
|
+
discovery.addProvider(GitlabDiscoveryProvider.fromConfig(config$1));
|
|
1126
|
+
}
|
|
1127
|
+
discovery.addAnalyzer(new BasicRepositoryAnalyzer());
|
|
1128
|
+
discovery.addAnalyzer(new PackageJsonAnalyzer());
|
|
1129
|
+
const { entities } = await discovery.run(answers.url);
|
|
1130
|
+
if (!entities.length) {
|
|
1131
|
+
tasks.Task.log(
|
|
1132
|
+
chalk__default["default"].yellow(`
|
|
1133
|
+
We could not find enough information to be able to generate any Software Catalog entities for you.
|
|
1134
|
+
Perhaps you can try again with a different repository?`)
|
|
1135
|
+
);
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
await tasks.Task.forItem("Creating", DISCOVERED_ENTITIES_FILE, async () => {
|
|
1139
|
+
const payload = [];
|
|
1140
|
+
for (const entity of entities) {
|
|
1141
|
+
payload.push("---\n", yaml__default["default"].stringify(entity));
|
|
1142
|
+
}
|
|
1143
|
+
await fs__default["default"].writeFile(DISCOVERED_ENTITIES_FILE, payload.join(""));
|
|
1144
|
+
});
|
|
1145
|
+
await tasks.Task.forItem(
|
|
1146
|
+
"Updating",
|
|
1147
|
+
APP_CONFIG_FILE,
|
|
1148
|
+
async () => await updateConfigFile(APP_CONFIG_FILE, {
|
|
1149
|
+
catalog: {
|
|
1150
|
+
locations: [
|
|
1151
|
+
{
|
|
1152
|
+
type: "file",
|
|
1153
|
+
target: DISCOVERED_ENTITIES_FILE
|
|
1154
|
+
}
|
|
1155
|
+
]
|
|
1156
|
+
}
|
|
1157
|
+
})
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
async function command() {
|
|
1162
|
+
const answers = await inquirer__default["default"].prompt([
|
|
1163
|
+
{
|
|
1164
|
+
type: "confirm",
|
|
1165
|
+
name: "shouldSetupAuth",
|
|
1166
|
+
message: "Do you want to set up Authentication for this project?",
|
|
1167
|
+
default: true
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
type: "confirm",
|
|
1171
|
+
name: "shouldSetupScaffolder",
|
|
1172
|
+
message: "Do you want to use Software Templates in this project?",
|
|
1173
|
+
default: true
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
type: "confirm",
|
|
1177
|
+
name: "shouldDiscoverEntities",
|
|
1178
|
+
message: "Do you want to discover entities and add them to the Software Catalog?",
|
|
1179
|
+
default: true
|
|
1180
|
+
}
|
|
1181
|
+
]);
|
|
1182
|
+
const { shouldSetupAuth, shouldSetupScaffolder, shouldDiscoverEntities } = answers;
|
|
1183
|
+
if (!shouldSetupAuth && !shouldSetupScaffolder && !shouldDiscoverEntities) {
|
|
1184
|
+
tasks.Task.log(
|
|
1185
|
+
chalk__default["default"].yellow(
|
|
1186
|
+
"If you change your mind, feel free to re-run this command."
|
|
1187
|
+
)
|
|
1188
|
+
);
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
let providerInfo;
|
|
1192
|
+
if (shouldSetupAuth) {
|
|
1193
|
+
providerInfo = await auth();
|
|
1194
|
+
}
|
|
1195
|
+
if (shouldSetupScaffolder) {
|
|
1196
|
+
await integrations(providerInfo);
|
|
1197
|
+
}
|
|
1198
|
+
if (shouldDiscoverEntities) {
|
|
1199
|
+
await discover(providerInfo);
|
|
1200
|
+
}
|
|
1201
|
+
tasks.Task.log();
|
|
1202
|
+
tasks.Task.log(
|
|
1203
|
+
`You can now start your app with ${chalk__default["default"].inverse(
|
|
1204
|
+
chalk__default["default"].italic("yarn dev")
|
|
1205
|
+
)}`
|
|
1206
|
+
);
|
|
1207
|
+
tasks.Task.log();
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
exports.command = command;
|
|
1211
|
+
//# sourceMappingURL=index-7b1afb9b.cjs.js.map
|