@fractary/faber-cli 1.4.1 ā 1.4.3
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6BpC;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAehD;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6BpC;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAehD;AAqkBD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C"}
|
|
@@ -22,6 +22,74 @@ export function createAuthSetupCommand() {
|
|
|
22
22
|
await runSetup(options);
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Manual configuration for existing GitHub App
|
|
27
|
+
*/
|
|
28
|
+
async function manualAppConfiguration(configPath, org, repo) {
|
|
29
|
+
const os = await import('os');
|
|
30
|
+
const path = await import('path');
|
|
31
|
+
console.log(chalk.bold('ā'.repeat(60)));
|
|
32
|
+
console.log(chalk.bold('š Manual GitHub App Configuration'));
|
|
33
|
+
console.log(chalk.bold('ā'.repeat(60)));
|
|
34
|
+
console.log();
|
|
35
|
+
console.log('You will need the following information from your GitHub App:');
|
|
36
|
+
console.log(' ⢠App ID (from app settings page)');
|
|
37
|
+
console.log(' ⢠Installation ID (from installations page URL)');
|
|
38
|
+
console.log(' ⢠Private key file (.pem)\n');
|
|
39
|
+
const rl = readline.createInterface({
|
|
40
|
+
input: process.stdin,
|
|
41
|
+
output: process.stdout,
|
|
42
|
+
});
|
|
43
|
+
const appIdInput = await rl.question('App ID: ');
|
|
44
|
+
const installationIdInput = await rl.question('Installation ID: ');
|
|
45
|
+
const privateKeyPath = await rl.question('Private key path (e.g., ~/.github/faber-app.pem): ');
|
|
46
|
+
rl.close();
|
|
47
|
+
if (!appIdInput || !installationIdInput || !privateKeyPath) {
|
|
48
|
+
console.error(chalk.red('\nā All fields are required\n'));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
// Validate App ID is a positive integer
|
|
52
|
+
const appId = parseInt(appIdInput, 10);
|
|
53
|
+
if (isNaN(appId) || appId <= 0) {
|
|
54
|
+
console.error(chalk.red('\nā App ID must be a positive integer\n'));
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
// Validate Installation ID is a positive integer
|
|
58
|
+
const installationId = parseInt(installationIdInput, 10);
|
|
59
|
+
if (isNaN(installationId) || installationId <= 0) {
|
|
60
|
+
console.error(chalk.red('\nā Installation ID must be a positive integer\n'));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
// Expand tilde in path
|
|
64
|
+
const expandedKeyPath = privateKeyPath.replace(/^~/, os.homedir());
|
|
65
|
+
// Verify private key exists
|
|
66
|
+
try {
|
|
67
|
+
await fs.access(expandedKeyPath);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error(chalk.red(`\nā Private key file not found: ${expandedKeyPath}\n`));
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
// Create config
|
|
74
|
+
const config = {
|
|
75
|
+
github: {
|
|
76
|
+
organization: org,
|
|
77
|
+
project: repo,
|
|
78
|
+
app: {
|
|
79
|
+
id: appId.toString(),
|
|
80
|
+
installation_id: installationId.toString(),
|
|
81
|
+
private_key_path: privateKeyPath,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
// Save config
|
|
86
|
+
const configDir = path.dirname(configPath);
|
|
87
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
88
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
89
|
+
console.log(chalk.green(`\nā Configuration saved to ${configPath}\n`));
|
|
90
|
+
console.log('Test the configuration with:');
|
|
91
|
+
console.log(chalk.cyan(` fractary-faber work issue fetch 1\n`));
|
|
92
|
+
}
|
|
25
93
|
/**
|
|
26
94
|
* Run the setup flow
|
|
27
95
|
*/
|
|
@@ -68,6 +136,22 @@ async function runSetup(options) {
|
|
|
68
136
|
}
|
|
69
137
|
console.log();
|
|
70
138
|
}
|
|
139
|
+
// Ask user if they want to create new or configure existing app
|
|
140
|
+
const rl0 = readline.createInterface({
|
|
141
|
+
input: process.stdin,
|
|
142
|
+
output: process.stdout,
|
|
143
|
+
});
|
|
144
|
+
console.log(chalk.bold('Choose setup method:'));
|
|
145
|
+
console.log(' 1. Create a new GitHub App (guided)');
|
|
146
|
+
console.log(' 2. Configure an existing GitHub App (manual)\n');
|
|
147
|
+
const method = (await rl0.question('Enter 1 or 2: ')).trim();
|
|
148
|
+
rl0.close();
|
|
149
|
+
console.log();
|
|
150
|
+
if (method === '2') {
|
|
151
|
+
// Manual configuration for existing app
|
|
152
|
+
await manualAppConfiguration(configPath, org, repo);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
71
155
|
// Step 3: Generate manifest
|
|
72
156
|
const manifest = generateAppManifest({ organization: org, repository: repo });
|
|
73
157
|
if (options.showManifest) {
|
|
@@ -147,7 +231,8 @@ async function runSetup(options) {
|
|
|
147
231
|
console.log(' 1. Review the app permissions');
|
|
148
232
|
console.log(' 2. Click the green "Create GitHub App ā" button');
|
|
149
233
|
console.log(' 3. GitHub will ask you to confirm - click "Create GitHub App" again');
|
|
150
|
-
console.log(' 4. After creation,
|
|
234
|
+
console.log(' 4. After creation, GitHub will redirect to example.com');
|
|
235
|
+
console.log(chalk.yellow(' (This is expected! The code you need will be in the URL)\n'));
|
|
151
236
|
const rl1 = readline.createInterface({
|
|
152
237
|
input: process.stdin,
|
|
153
238
|
output: process.stdout,
|
|
@@ -160,7 +245,10 @@ async function runSetup(options) {
|
|
|
160
245
|
console.log(chalk.bold('š STEP 2: Copy the code from the redirect URL'));
|
|
161
246
|
console.log(chalk.bold('ā'.repeat(60)));
|
|
162
247
|
console.log();
|
|
163
|
-
console.log('After creating the app, GitHub will redirect
|
|
248
|
+
console.log('After creating the app, GitHub will redirect to example.com');
|
|
249
|
+
console.log(chalk.yellow('(Don\'t worry - example.com is just a placeholder!)'));
|
|
250
|
+
console.log();
|
|
251
|
+
console.log('The URL will look like:');
|
|
164
252
|
console.log(chalk.gray('https://github.com/settings/apps/your-app?code=XXXXXXXXXXXXX'));
|
|
165
253
|
console.log();
|
|
166
254
|
console.log('Copy the entire code from the URL bar and paste it below:\n');
|
|
@@ -215,15 +303,68 @@ async function runSetup(options) {
|
|
|
215
303
|
installationId = await getInstallationId(conversionResponse.id.toString(), conversionResponse.pem, org);
|
|
216
304
|
}
|
|
217
305
|
catch (error) {
|
|
218
|
-
|
|
219
|
-
|
|
306
|
+
// App created but not installed yet - guide user through installation
|
|
307
|
+
console.log(chalk.yellow('\nā ļø App created but not yet installed on your organization\n'));
|
|
308
|
+
console.log(chalk.bold('ā'.repeat(60)));
|
|
309
|
+
console.log(chalk.bold('š STEP 3: Install the App on Your Organization'));
|
|
310
|
+
console.log(chalk.bold('ā'.repeat(60)));
|
|
311
|
+
console.log();
|
|
312
|
+
const installUrl = `https://github.com/apps/${conversionResponse.slug}/installations/new`;
|
|
313
|
+
console.log('The app needs to be installed on your GitHub organization/repositories.');
|
|
314
|
+
console.log('Opening installation page in your browser...\n');
|
|
315
|
+
// Convert to Windows path if WSL
|
|
316
|
+
const os = await import('os');
|
|
317
|
+
const isWsl = process.platform === 'linux' && os.release().toLowerCase().includes('microsoft');
|
|
318
|
+
// Try to open installation URL
|
|
319
|
+
const { execFile } = await import('child_process');
|
|
320
|
+
const { promisify } = await import('util');
|
|
321
|
+
const execFileAsync = promisify(execFile);
|
|
322
|
+
console.log(chalk.cyan(`Installation URL: ${installUrl}\n`));
|
|
323
|
+
try {
|
|
324
|
+
if (process.platform === 'darwin') {
|
|
325
|
+
await execFileAsync('open', [installUrl]);
|
|
326
|
+
}
|
|
327
|
+
else if (process.platform === 'win32') {
|
|
328
|
+
await execFileAsync('cmd', ['/c', 'start', '', installUrl]);
|
|
329
|
+
}
|
|
330
|
+
else if (!isWsl) {
|
|
331
|
+
await execFileAsync('xdg-open', [installUrl]);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
// WSL - can't auto-open, show instructions
|
|
335
|
+
console.log(chalk.yellow('š” Copy the URL above and open it in your Windows browser\n'));
|
|
336
|
+
}
|
|
220
337
|
}
|
|
221
|
-
|
|
222
|
-
console.
|
|
338
|
+
catch (openError) {
|
|
339
|
+
console.log(chalk.yellow('š” Copy the URL above and open it in your browser\n'));
|
|
340
|
+
}
|
|
341
|
+
console.log('In your browser:');
|
|
342
|
+
console.log(' 1. Select which repositories to give access to:');
|
|
343
|
+
console.log(' - "All repositories" (easiest) OR');
|
|
344
|
+
console.log(` - "Only select repositories" ā choose "${repo}"`);
|
|
345
|
+
console.log(' 2. Click the green "Install" button\n');
|
|
346
|
+
const rl2 = readline.createInterface({
|
|
347
|
+
input: process.stdin,
|
|
348
|
+
output: process.stdout,
|
|
349
|
+
});
|
|
350
|
+
await rl2.question('Press Enter after you have installed the app...');
|
|
351
|
+
rl2.close();
|
|
352
|
+
// Try fetching installation ID again
|
|
353
|
+
console.log(chalk.gray('\nVerifying installation...'));
|
|
354
|
+
try {
|
|
355
|
+
installationId = await getInstallationId(conversionResponse.id.toString(), conversionResponse.pem, org);
|
|
356
|
+
console.log(chalk.green(`ā Installation verified! Installation ID: ${chalk.cyan(installationId)}\n`));
|
|
357
|
+
}
|
|
358
|
+
catch (retryError) {
|
|
359
|
+
console.error(chalk.red('\nā Still unable to find installation.\n'));
|
|
360
|
+
console.log('Please ensure you:');
|
|
361
|
+
console.log(' 1. Clicked "Install" on the installation page');
|
|
362
|
+
console.log(' 2. Selected at least one repository');
|
|
363
|
+
console.log(` 3. Installed on the "${org}" organization\n`);
|
|
364
|
+
console.log(`Visit: ${chalk.cyan(installUrl)}`);
|
|
365
|
+
console.log('\nAfter installing, run this command again to complete setup.\n');
|
|
366
|
+
process.exit(1);
|
|
223
367
|
}
|
|
224
|
-
console.log('The app was created but could not find the installation.');
|
|
225
|
-
console.log(`Please install the app on your organization: ${chalk.cyan(`https://github.com/apps/${conversionResponse.slug}/installations/new`)}`);
|
|
226
|
-
process.exit(1);
|
|
227
368
|
}
|
|
228
369
|
console.log(chalk.green(`ā Installation ID: ${chalk.cyan(installationId)}\n`));
|
|
229
370
|
// Step 8: Save private key
|