diff --git a/packages/cli/src/commands/onboard/auth/github/index.ts b/packages/cli/src/commands/onboard/auth/github/index.ts index c1789fa64d..ce53d23ad3 100644 --- a/packages/cli/src/commands/onboard/auth/github/index.ts +++ b/packages/cli/src/commands/onboard/auth/github/index.ts @@ -20,10 +20,30 @@ import * as fs from 'fs-extra'; import inquirer from 'inquirer'; import fetch from 'node-fetch'; import { Task } from '../../../../lib/tasks'; -import { addUserEntity, updateConfigFile } from '../config'; -import { APP_CONFIG_FILE, PATCH_FOLDER, USER_ENTITY_FILE } from '../files'; +import { addUserEntity, updateConfigFile } from '../../config'; +import { APP_CONFIG_FILE, PATCH_FOLDER, USER_ENTITY_FILE } from '../../files'; import { patch } from '../patch'; +type Answers = { + username: string; + clientSecret: string; + clientId: string; + hasEnterprise: boolean; + enterpriseInstanceUrl?: string; +}; + +const catalogUserLocation = { + catalog: { + locations: [ + { + type: 'file', + target: '../../user-info.yaml', + rules: [{ allow: ['User'] }], + }, + ], + }, +}; + const validateCredentials = async (clientId: string, clientSecret: string) => { try { const app = new OAuthApp({ @@ -70,14 +90,6 @@ const getConfig = (answers: Answers) => { }; }; -type Answers = { - username: string; - clientSecret: string; - clientId: string; - hasEnterprise: boolean; - enterpriseInstanceUrl?: string; -}; - export const github = async () => { Task.log(` To add GitHub authentication, you must create an OAuth App from the GitHub developer settings: ${chalk.blue( @@ -147,7 +159,11 @@ export const github = async () => { await Task.forItem( 'Updating', APP_CONFIG_FILE, - async () => await updateConfigFile(APP_CONFIG_FILE, config), + async () => + await updateConfigFile(APP_CONFIG_FILE, { + ...config, + ...catalogUserLocation, + }), ); await Task.forItem( 'Creating', diff --git a/packages/cli/src/commands/onboard/auth/gitlab/index.ts b/packages/cli/src/commands/onboard/auth/gitlab/index.ts index 4fa77f6b02..9e2a674a9a 100644 --- a/packages/cli/src/commands/onboard/auth/gitlab/index.ts +++ b/packages/cli/src/commands/onboard/auth/gitlab/index.ts @@ -18,8 +18,8 @@ import chalk from 'chalk'; import * as fs from 'fs-extra'; import inquirer from 'inquirer'; import { Task } from '../../../../lib/tasks'; -import { addUserEntity, updateConfigFile } from '../config'; -import { APP_CONFIG_FILE, PATCH_FOLDER, USER_ENTITY_FILE } from '../files'; +import { addUserEntity, updateConfigFile } from '../../config'; +import { APP_CONFIG_FILE, PATCH_FOLDER, USER_ENTITY_FILE } from '../../files'; import { patch } from '../patch'; const getConfig = (answers: Answers) => { diff --git a/packages/cli/src/commands/onboard/auth/patch.ts b/packages/cli/src/commands/onboard/auth/patch.ts index 2ebca2aab2..d004eeb157 100644 --- a/packages/cli/src/commands/onboard/auth/patch.ts +++ b/packages/cli/src/commands/onboard/auth/patch.ts @@ -17,7 +17,7 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import * as differ from 'diff'; -import { PATCH_FOLDER } from './files'; +import { PATCH_FOLDER } from '../files'; import { findPaths } from '@backstage/cli-common'; /* eslint-disable-next-line no-restricted-syntax */ diff --git a/packages/cli/src/commands/onboard/auth/config.ts b/packages/cli/src/commands/onboard/config.ts similarity index 68% rename from packages/cli/src/commands/onboard/auth/config.ts rename to packages/cli/src/commands/onboard/config.ts index 01afc5f96c..b5a2336a0a 100644 --- a/packages/cli/src/commands/onboard/auth/config.ts +++ b/packages/cli/src/commands/onboard/config.ts @@ -18,44 +18,14 @@ import { UserEntity } from '@backstage/catalog-model'; import * as fs from 'fs-extra'; import yaml from 'yaml'; -type AuthConfig = { - auth: { - providers: { - [key: string]: { - development: { - clientId: string; - clientSecret: string; - enterpriseInstanceUrl?: string; - audience?: string; - }; - }; - }; - }; -}; - -const catalogUserLocation = { - catalog: { - locations: [ - { - type: 'file', - target: '../../user-info.yaml', - rules: [{ allow: ['User'] }], - }, - ], - }, -}; - const readYaml = async (file: string) => { return yaml.parse(await fs.readFile(file, 'utf8')); }; -export const updateConfigFile = async ( - file: string, - authConfig: AuthConfig, -) => { +export const updateConfigFile = async (file: string, config: T) => { const content = fs.existsSync(file) - ? { ...(await readYaml(file)), ...authConfig, ...catalogUserLocation } - : { ...authConfig, ...catalogUserLocation }; + ? { ...(await readYaml(file)), ...config } + : { ...config }; return await fs.writeFile( file, diff --git a/packages/cli/src/commands/onboard/auth/files.ts b/packages/cli/src/commands/onboard/files.ts similarity index 100% rename from packages/cli/src/commands/onboard/auth/files.ts rename to packages/cli/src/commands/onboard/files.ts diff --git a/packages/cli/src/commands/onboard/integrations/github/index.ts b/packages/cli/src/commands/onboard/integrations/github/index.ts index 73416699e4..1a0d7a5ef5 100644 --- a/packages/cli/src/commands/onboard/integrations/github/index.ts +++ b/packages/cli/src/commands/onboard/integrations/github/index.ts @@ -17,37 +17,82 @@ import chalk from 'chalk'; import inquirer from 'inquirer'; import { Task } from '../../../../lib/tasks'; +import { updateConfigFile } from '../../config'; +import { APP_CONFIG_FILE } from '../../files'; + +type Answers = { + isEnterprise: boolean; + host: string; + apiBaseUrl?: string; + token: string; +}; + +const getConfig = ({ isEnterprise, host, apiBaseUrl, token }: Answers) => ({ + integrations: { + github: isEnterprise + ? [ + { + host, + apiBaseUrl, + token, + }, + ] + : [ + { + host, + token, + }, + ], + }, +}); export const github = async () => { - const host = 'https://github.com'; - const answers = await inquirer.prompt<{ hasEnterprise: boolean }>([ + let host = 'github.com'; + let apiBaseUrl: string | undefined; + + // TODO(tudi2d): Is GitHub Enterprise a valid setup if there is no Authentication? + const { isEnterprise } = await inquirer.prompt<{ + isEnterprise: Answers['isEnterprise']; + }>([ { type: 'confirm', - name: 'hasEnterprise', + name: 'isEnterprise', message: 'Are you using GitHub Enterprise?', }, ]); - if (answers.hasEnterprise) { - await inquirer.prompt<{ hasEnterprise: boolean }>([ - { - type: 'confirm', - name: 'hasEnterprise', - message: 'Are you using GitHub Enterprise?', - }, - { - type: 'input', - name: 'enterpriseInstanceUrl', - message: 'What is your GitHub Enterprise URL (e.g. ghe.example.net)?', - when: ({ hasEnterprise }) => hasEnterprise, - validate: (input: string) => Boolean(new URL(input)), - }, - ]); + if (isEnterprise) { + try { + const answers = await inquirer.prompt< + Pick + >([ + { + type: 'input', + name: 'apiBaseUrl', + 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 + validate: (input: string) => Boolean(new URL(input)), + }, + { + type: 'input', + name: 'host', + 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? + }, + ]); + + host = answers.host; + apiBaseUrl = answers.apiBaseUrl; + } catch (err) { + return; + } } Task.log(` To create new repositories in GitHub using Software Templates you first need to create a personal access token: ${chalk.blue( - `${host}/settings/tokens/new', + `https://${host}/settings/tokens/new`, )} Select the following scopes: @@ -68,6 +113,32 @@ export const github = async () => { You can find the full documentation page here: ${chalk.blue( 'https://backstage.io/docs/integrations/github/locations', )} - `, - )}`); + `); + + const { token } = await inquirer.prompt<{ + token: Answers['token']; + }>([ + { + type: 'input', + name: 'token', + message: + 'Please insert your personal access token to setup the GitHub Integration', + // TODO(tudi2d): validate: Must the host be part of the REST API URL? + }, + ]); + + const config = getConfig({ + token, + isEnterprise, + host, + apiBaseUrl, + }); + + Task.log('Setting up GitHub Integration for you...'); + + await Task.forItem( + 'Updating', + APP_CONFIG_FILE, + async () => await updateConfigFile(APP_CONFIG_FILE, config), + ); }; diff --git a/packages/cli/src/commands/onboard/integrations/index.ts b/packages/cli/src/commands/onboard/integrations/index.ts index 2b55ab2bba..f3ac6754d6 100644 --- a/packages/cli/src/commands/onboard/integrations/index.ts +++ b/packages/cli/src/commands/onboard/integrations/index.ts @@ -29,7 +29,7 @@ export async function integrations(): Promise { integration?: Integration; }>([ { - // TODO(tudi2d): Should be multiple choice + // 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:',