diff --git a/packages/cli/src/commands/onboard/auth/github/index.ts b/packages/cli/src/commands/onboard/auth/github/index.ts index 7e1e63a8fe..36c6b02038 100644 --- a/packages/cli/src/commands/onboard/auth/github/index.ts +++ b/packages/cli/src/commands/onboard/auth/github/index.ts @@ -23,7 +23,7 @@ import { updateConfigFile } from '../../config'; import { APP_CONFIG_FILE, PATCH_FOLDER } from '../../files'; import { patch } from '../patch'; -type Answers = { +export type Answers = { clientSecret: string; clientId: string; hasEnterprise: boolean; @@ -76,7 +76,7 @@ const getConfig = (answers: Answers) => { }; }; -export const github = async () => { +export const github = async (): Promise => { Task.log(` To add GitHub authentication, you must create an OAuth App from the GitHub developer settings: ${chalk.blue( 'https://github.com/settings/developers', @@ -142,4 +142,6 @@ export const github = async () => { await patch(patchFile); }); } + + return answers; }; diff --git a/packages/cli/src/commands/onboard/auth/gitlab/index.ts b/packages/cli/src/commands/onboard/auth/gitlab/index.ts index 1940f66017..bc5491111c 100644 --- a/packages/cli/src/commands/onboard/auth/gitlab/index.ts +++ b/packages/cli/src/commands/onboard/auth/gitlab/index.ts @@ -42,14 +42,14 @@ const getConfig = (answers: Answers) => { }; }; -type Answers = { +export type Answers = { clientSecret: string; clientId: string; hasAudience: boolean; audience?: string; }; -export const gitlab = async () => { +export const gitlab = async (): Promise => { Task.log(` To add GitLab authentication, you must create an Application from the GitLab Settings: ${chalk.blue( 'https://gitlab.com/-/profile/applications', @@ -110,4 +110,6 @@ export const gitlab = async () => { await patch(patchFile); }); } + + return answers; }; diff --git a/packages/cli/src/commands/onboard/auth/index.ts b/packages/cli/src/commands/onboard/auth/index.ts index df9471e1db..5a299e2fb5 100644 --- a/packages/cli/src/commands/onboard/auth/index.ts +++ b/packages/cli/src/commands/onboard/auth/index.ts @@ -14,44 +14,50 @@ * limitations under the License. */ -import chalk from 'chalk'; import inquirer from 'inquirer'; import { Task } from '../../../lib/tasks'; -import { github } from './github'; -import { gitlab } from './gitlab'; +import { github, Answers as GitHubAnswers } from './github'; +import { gitlab, Answers as GitLabAnswers } from './gitlab'; -export async function auth(): Promise { +export { type GitHubAnswers, type GitLabAnswers }; + +export async function auth(): Promise<{ + provider: string; + answers: GitHubAnswers | GitLabAnswers; +}> { const answers = await inquirer.prompt<{ - provider?: string; + provider: string; }>([ { type: 'list', name: 'provider', - message: 'Please select a provider:', + message: 'Please select an authentication provider:', choices: ['GitHub', 'GitLab'], }, ]); const { provider } = answers; + let providerAnswers; switch (provider) { case 'GitHub': { - await github(); + providerAnswers = await github(); break; } case 'GitLab': { - await gitlab(); + providerAnswers = await gitlab(); break; } default: throw new Error(`Provider ${provider} not implemented yet.`); } + Task.log(); Task.log(`Done setting up ${provider} Authentication!`); Task.log(); - Task.log( - `You can now start your app with ${chalk.inverse( - chalk.italic('yarn dev'), - )}`, - ); + + return { + provider, + answers: providerAnswers, + }; } diff --git a/packages/cli/src/commands/onboard/command.ts b/packages/cli/src/commands/onboard/command.ts index 1cfaabed12..5cf25953ab 100644 --- a/packages/cli/src/commands/onboard/command.ts +++ b/packages/cli/src/commands/onboard/command.ts @@ -16,55 +16,54 @@ import chalk from 'chalk'; import inquirer from 'inquirer'; +import { Task } from '../../lib/tasks'; import { auth } from './auth'; import { integrations } from './integrations'; export async function command(): Promise { - try { - // TODO(tudi2d): Is there a way to make this cleaner? - const answers = await inquirer.prompt<{ - shouldSetupAuth: boolean; - }>([ - { - type: 'confirm', - name: 'shouldSetupAuth', - message: 'Do you want to set up Authentication for this project?', - default: true, - }, - ]); + const answers = await inquirer.prompt<{ + shouldSetupAuth: boolean; + shouldSetupScaffolder: boolean; + }>([ + { + type: 'confirm', + name: 'shouldSetupAuth', + message: 'Do you want to set up Authentication for this project?', + default: true, + }, + { + type: 'confirm', + name: 'shouldSetupScaffolder', + message: 'Do you want to use Software Templates in this project?', + default: true, + }, + ]); - if (!answers.shouldSetupAuth) { - console.log( - chalk.yellow( - 'If you change your mind, feel free to re-run this command.', - ), - ); - } else { - await auth(); - } + const { shouldSetupAuth, shouldSetupScaffolder } = answers; - // TODO(tudi2d): Select "Core Features" here such that we can determin if the integrations need to be setup for either Catalog, Scaffollder or both - await inquirer - .prompt<{ - setupScaffolder: boolean; - }>([ - { - type: 'confirm', - name: 'setupScaffolder', - message: 'Do you want to setup Sofware Templates?', - default: true, - }, - ]) - .then(async ({ setupScaffolder }) => - setupScaffolder - ? await integrations() - : console.log( - chalk.yellow( - 'If you change your mind, feel free to re-run this command.', - ), - ), - ); - } catch (err) { - process.exit(-1); + let providerInfo; + if (shouldSetupAuth) { + providerInfo = await auth(); } + + if (shouldSetupScaffolder) { + await integrations(providerInfo); + } + + if (!shouldSetupAuth && !shouldSetupScaffolder) { + Task.log( + chalk.yellow( + 'If you change your mind, feel free to re-run this command.', + ), + ); + return; + } + + Task.log(); + Task.log( + `You can now start your app with ${chalk.inverse( + chalk.italic('yarn dev'), + )}`, + ); + Task.log(); } diff --git a/packages/cli/src/commands/onboard/integrations/github/index.ts b/packages/cli/src/commands/onboard/integrations/github/index.ts index ac4fd37406..883a119c00 100644 --- a/packages/cli/src/commands/onboard/integrations/github/index.ts +++ b/packages/cli/src/commands/onboard/integrations/github/index.ts @@ -19,76 +19,74 @@ import inquirer from 'inquirer'; import { Task } from '../../../../lib/tasks'; import { updateConfigFile } from '../../config'; import { APP_CONFIG_FILE } from '../../files'; +import { GitHubAnswers } from '../../auth'; type Answers = { - isEnterprise: boolean; - host: string; - apiBaseUrl?: string; - token: string; + hasEnterprise: boolean; + enterpriseInstanceUrl: string; + apiBaseUrl: string; }; -const getConfig = ({ isEnterprise, host, apiBaseUrl, token }: Answers) => ({ +const getConfig = ({ + hasEnterprise, + apiBaseUrl, + host, + token, +}: { + hasEnterprise: boolean; + apiBaseUrl: string; + host: string; + token: string; +}) => ({ integrations: { - github: isEnterprise - ? [ - { - host, - apiBaseUrl, - token, - }, - ] - : [ - { - host, - token, - }, - ], + github: [ + { + host, + token, + ...(hasEnterprise && { + apiBaseUrl, + }), + }, + ], }, }); -export const github = async () => { - let host = 'github.com'; - +export const github = async (providerAnswers?: GitHubAnswers) => { // TODO(tudi2d): Is GitHub Enterprise a valid setup if there is no Authentication? - const { - isEnterprise, - host: _host, - apiBaseUrl, - } = await inquirer.prompt<{ - isEnterprise: Answers['isEnterprise']; - apiBaseUrl: Answers['apiBaseUrl']; - host: Answers['host']; - }>([ + const answers = await inquirer.prompt([ { type: 'confirm', - name: 'isEnterprise', + name: 'hasEnterprise', message: 'Are you using GitHub Enterprise?', + when: () => typeof providerAnswers === 'undefined', }, { type: 'input', - name: 'apiBaseUrl', - when: ({ isEnterprise: _isEnterprise }) => _isEnterprise, - message: - 'What is your GitHub Enterprise REST API URL (e.g. https://ghe.example.net/api/v3)?', - // TODO(tudi2d): Fetch API using OAuth Token if Auth was set up + name: 'enterpriseInstanceUrl', + message: 'What is your URL for GitHub Enterprise?', + when: ({ hasEnterprise }) => hasEnterprise, validate: (input: string) => Boolean(new URL(input)), }, { type: 'input', - name: 'host', - when: ({ isEnterprise: _isEnterprise }) => _isEnterprise, - message: 'What is your GitHub Enterprise Host (e.g. ghe.example.net)?', - // TODO(tudi2d): validate: Must the host be part of the REST API URL? + name: 'apiBaseUrl', + message: 'What is your GitHub Enterprise API path?', + default: '/api/v3', + when: ({ hasEnterprise }) => + hasEnterprise || providerAnswers?.hasEnterprise, + // TODO(tudi2d): Fetch API using OAuth Token if Auth was set up }, ]); - if (isEnterprise) { - host = _host; - } + const host = new URL( + providerAnswers?.enterpriseInstanceUrl ?? + answers?.enterpriseInstanceUrl ?? + 'http://github.com', + ); Task.log(` To create new repositories in GitHub using Software Templates you first need to create a personal access token: ${chalk.blue( - `https://${host}/settings/tokens/new`, + `${host.origin}/settings/tokens/new`, )} Select the following scopes: @@ -111,9 +109,7 @@ export const github = async () => { )} `); - const { token } = await inquirer.prompt<{ - token: Answers['token']; - }>([ + const { token } = await inquirer.prompt<{ token: string }>([ { type: 'input', name: 'token', @@ -124,13 +120,13 @@ export const github = async () => { ]); const config = getConfig({ + hasEnterprise: providerAnswers?.hasEnterprise ?? answers.hasEnterprise, + apiBaseUrl: host.origin + answers.apiBaseUrl, + host: host.hostname, token, - isEnterprise, - host, - apiBaseUrl, }); - Task.log('Setting up GitHub Integration for you...'); + Task.log('Setting up Software Templates using GitHub integration for you...'); await Task.forItem( 'Updating', diff --git a/packages/cli/src/commands/onboard/integrations/index.ts b/packages/cli/src/commands/onboard/integrations/index.ts index f3ac6754d6..5cb2f95b38 100644 --- a/packages/cli/src/commands/onboard/integrations/index.ts +++ b/packages/cli/src/commands/onboard/integrations/index.ts @@ -15,6 +15,8 @@ */ import inquirer from 'inquirer'; +import { Task } from '../../../lib/tasks'; +import { GitHubAnswers, GitLabAnswers } from '../auth'; import { github } from './github'; enum Integration { @@ -23,27 +25,51 @@ enum Integration { const Integrations: Integration[] = [Integration.GITHUB]; -export async function integrations(): Promise { - await inquirer - .prompt<{ - integration?: Integration; - }>([ - { - // TODO(tudi2d): Let's start with one, but it should be multiple choice in the future - type: 'list', - name: 'integration', - message: 'Please select an integration:', - choices: Integrations, - }, - ]) - .then(async ({ integration }) => { - if (integration) { - switch (integration) { - case Integration.GITHUB: - await github(); - break; - default: - } - } - }); +export async function integrations(providerInfo?: { + provider: string; + answers: GitHubAnswers | GitLabAnswers; +}): Promise { + const answers = await inquirer.prompt<{ + integration?: Integration; + shouldUsePreviousProvider: boolean; + }>([ + { + type: 'confirm', + name: 'shouldUsePreviousProvider', + message: `Do you want to keep using ${providerInfo?.provider} as your provider when setting up Software Templates?`, + when: () => + providerInfo?.provider && + Object.values(Integrations).includes( + providerInfo!.provider as Integration, + ), + }, + { + // TODO(tudi2d): Let's start with one, but it should be multiple choice in the future + type: 'list', + name: 'integration', + message: 'Please select an integration provider:', + choices: Integrations, + when: ({ shouldUsePreviousProvider }) => !shouldUsePreviousProvider, + }, + ]); + + if (answers.shouldUsePreviousProvider) { + answers.integration = providerInfo!.provider as Integration; + } + + switch (answers.integration) { + case Integration.GITHUB: { + const providerAnswers = + providerInfo?.provider === 'GitHub' + ? (providerInfo!.answers as GitHubAnswers) + : undefined; + await github(providerAnswers); + break; + } + default: + } + + Task.log(); + Task.log(`Done setting up ${answers.integration} Integration!`); + Task.log(); }