cli: Use .patch files instead

Signed-off-by: Marcus Eide <eide@spotify.com>
This commit is contained in:
Marcus Eide
2023-02-20 14:44:48 +01:00
committed by Philipp Hugenroth
parent 0a60451b6f
commit ed89e40965
7 changed files with 100 additions and 98 deletions
+17 -3
View File
@@ -18,19 +18,33 @@ import { findPaths } from '@backstage/cli-common';
import * as path from 'path';
/* eslint-disable-next-line no-restricted-syntax */
const { targetRoot, resolveTargetRoot } = findPaths(__dirname);
const { targetRoot, ownDir, resolveTargetRoot } = findPaths(__dirname);
export const APP_CONFIG_FILE = path.join(targetRoot, 'app-config.local.yaml');
export const ENV_CONFIG_FILE = path.join(targetRoot, '.env.local');
export const USER_ENTITY_FILE = path.join(targetRoot, 'user-info.yaml');
export const APP_TSX_FILE = path.join(
const APP_TSX_FILE = path.join(
resolveTargetRoot('packages/app'),
'src',
'App.tsx',
);
export const AUTH_BACKEND_PLUGIN_FILE = path.join(
const AUTH_BACKEND_PLUGIN_FILE = path.join(
resolveTargetRoot('packages/backend'),
'src',
'plugins',
'auth.ts',
);
export const PATCH_FOLDER = path.join(
ownDir,
'src',
'commands',
'admin',
'auth',
'patches',
);
export const patchMap: Record<string, string> = {
'App.tsx': APP_TSX_FILE,
'auth.ts': AUTH_BACKEND_PLUGIN_FILE,
};
@@ -1,76 +0,0 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const addSignInPageDiff =
'@@ -32,11 +32,27 @@\n' +
" import { AppRouter, FlatRoutes } from '@backstage/core-app-api';\n" +
" import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';\n" +
" import { RequirePermission } from '@backstage/plugin-permission-react';\n" +
" import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha';\n" +
"+import { githubAuthApiRef } from '@backstage/core-plugin-api';\n" +
"+import { SignInPage } from '@backstage/core-components';\n" +
' \n' +
' const app = createApp({\n' +
' apis,\n' +
'+ components: {\n' +
'+ SignInPage: props => (\n' +
'+ <SignInPage\n' +
'+ {...props}\n' +
'+ auto\n' +
'+ provider={{\n' +
"+ id: 'github-auth-provider',\n" +
"+ title: 'GitHub',\n" +
"+ message: 'Sign in using GitHub',\n" +
'+ apiRef: githubAuthApiRef,\n' +
'+ }}\n' +
'+ />\n' +
'+ ),\n' +
'+ },\n' +
' bindRoutes({ bind }) {\n' +
' bind(catalogPlugin.externalRoutes, {\n' +
' createComponent: scaffolderPlugin.routes.root,\n' +
' viewTechDoc: techdocsPlugin.routes.docRoot,\n';
export const replaceSignInResolverDiff =
'@@ -36,18 +36,18 @@\n' +
' //\n' +
' // https://backstage.io/docs/auth/identity-resolver\n' +
' github: providers.github.create({\n' +
' signIn: {\n' +
'- resolver(_, ctx) {\n' +
"- const userRef = 'user:default/guest'; // Must be a full entity reference\n" +
'- return ctx.issueToken({\n' +
'- claims: {\n' +
"- sub: userRef, // The user's own identity\n" +
'- ent: [userRef], // A list of identities that the user claims ownership through\n' +
'- },\n' +
'- });\n' +
'- },\n' +
'- // resolver: providers.github.resolvers.usernameMatchingUserEntityName(),\n' +
'+ // resolver(_, ctx) {\n' +
"+ // const userRef = 'user:default/guest'; // Must be a full entity reference\n" +
'+ // return ctx.issueToken({\n' +
'+ // claims: {\n' +
"+ // sub: userRef, // The user's own identity\n" +
'+ // ent: [userRef], // A list of identities that the user claims ownership through\n' +
'+ // },\n' +
'+ // });\n' +
'+ // },\n' +
'+ resolver: providers.github.resolvers.usernameMatchingUserEntityName(),\n' +
' },\n' +
' }),\n' +
' },\n' +
' });\n';
@@ -18,17 +18,18 @@ import { OAuthApp } from '@octokit/oauth-app';
import chalk from 'chalk';
import inquirer from 'inquirer';
import fetch from 'node-fetch';
import * as fs from 'fs-extra';
import * as path from 'path';
import { Task } from '../../../../lib/tasks';
import { addUserEntity, updateConfigFile, updateEnvFile } from '../config';
import {
APP_CONFIG_FILE,
APP_TSX_FILE,
AUTH_BACKEND_PLUGIN_FILE,
ENV_CONFIG_FILE,
patchMap,
PATCH_FOLDER,
USER_ENTITY_FILE,
} from '../files';
import { patch } from '../patch';
import { addSignInPageDiff, replaceSignInResolverDiff } from './diffs';
const validateCredentials = async (clientId: string, clientSecret: string) => {
try {
@@ -50,7 +51,7 @@ const validateCredentials = async (clientId: string, clientSecret: string) => {
error.response.status !== 200 &&
error.response.data.error !== 'bad_verification_code'
) {
throw new Error(`Validating Github Credentials failed.`);
throw new Error(`Validating GitHub Credentials failed.`);
}
}
};
@@ -109,13 +110,14 @@ export const oauth = async (useEnvForSecrets: boolean) => {
You can find the full documentation page here: ${chalk.blue(
'https://backstage.io/docs/auth/github/provider',
)}`);
)}
`);
const answers = await inquirer.prompt<Answers>([
{
type: 'input',
name: 'username',
message: 'What is your Github username?',
message: 'What is your GitHub username?',
validate: async (input: string) => {
const response = await fetch(`https://api.github.com/users/${input}`);
if (!response.ok) {
@@ -139,30 +141,53 @@ export const oauth = async (useEnvForSecrets: boolean) => {
{
type: 'confirm',
name: 'hasEnterprise',
message: 'Are you using Github Enterprise?',
message: 'Are you using GitHub Enterprise?',
},
{
type: 'input',
name: 'enterpriseInstanceUrl',
message: 'What is your URL for Github Enterprise?',
message: 'What is your URL for GitHub Enterprise?',
when: ({ hasEnterprise }) => hasEnterprise,
validate: (input: string) => Boolean(new URL(input)),
},
]);
const { username, clientId, clientSecret } = answers;
await validateCredentials(clientId, clientSecret);
const config = getConfig(answers, useEnvForSecrets);
await updateConfigFile(APP_CONFIG_FILE, config);
Task.log('Setting up GitHub Authentication for you...');
await Task.forItem(
'Validating',
'credentials',
async () => await validateCredentials(clientId, clientSecret),
);
await Task.forItem(
'Updating',
APP_CONFIG_FILE,
async () => await updateConfigFile(APP_CONFIG_FILE, config),
);
if (useEnvForSecrets) {
await updateEnvFile(ENV_CONFIG_FILE, clientId, clientSecret);
await Task.forItem(
'Updating',
ENV_CONFIG_FILE,
async () => await updateEnvFile(ENV_CONFIG_FILE, clientId, clientSecret),
);
}
await Task.forItem(
'Creating',
USER_ENTITY_FILE,
async () => await addUserEntity(USER_ENTITY_FILE, username),
);
await addUserEntity(USER_ENTITY_FILE, username);
const patches = await fs.readdir(PATCH_FOLDER);
for (const patchFile of patches) {
const target = patchFile
.replace(/[0-9]+-github\./, '')
.replace('.patch', '');
await patch(APP_TSX_FILE, addSignInPageDiff);
await patch(AUTH_BACKEND_PLUGIN_FILE, replaceSignInResolverDiff);
await Task.forItem('Pactching', target, async () => {
await patch(patchMap[target], path.join(PATCH_FOLDER, patchFile));
});
}
};
@@ -43,5 +43,7 @@ export async function auth(): Promise<void> {
}
Task.log(`Done setting up ${provider}!`);
Task.log(`You can now start you app with ${chalk.inverse('yarn dev')}`);
Task.log(
`You can now start you app with ${chalk.inverse(chalk.italic('yarn dev'))}`,
);
}
@@ -17,9 +17,10 @@
import * as fs from 'fs-extra';
import * as differ from 'diff';
export const patch = async (file: string, diff: string) => {
export const patch = async (file: string, patchFile: string) => {
const patchContent = await fs.readFile(patchFile, 'utf8');
const oldContent = await fs.readFile(file, 'utf8');
const newContent = differ.applyPatch(oldContent, diff);
const newContent = differ.applyPatch(oldContent, patchContent);
return await fs.writeFile(file, newContent, 'utf8');
};
@@ -0,0 +1,24 @@
@@ -30 +30,5 @@ import { Root } from './components/Root';
-import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components';
+import {
+ AlertDisplay,
+ OAuthRequestDialog,
+ SignInPage,
+} from '@backstage/core-components';
@@ -35,0 +40 @@ import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/
+import { githubAuthApiRef } from '@backstage/core-plugin-api';
@@ -38,0 +44,14 @@ const app = createApp({
+ components: {
+ SignInPage: props => (
+ <SignInPage
+ {...props}
+ auto
+ provider={{
+ id: 'github-auth-provider',
+ title: 'GitHub',
+ message: 'Sign in using GitHub',
+ apiRef: githubAuthApiRef,
+ }}
+ />
+ ),
+ },
@@ -0,0 +1,12 @@
@@ -40,10 +40 @@ export default async function createPlugin(
- resolver(_, ctx) {
- const userRef = 'user:default/guest'; // Must be a full entity reference
- return ctx.issueToken({
- claims: {
- sub: userRef, // The user's own identity
- ent: [userRef], // A list of identities that the user claims ownership through
- },
- });
- },
- // resolver: providers.github.resolvers.usernameMatchingUserEntityName(),
+ resolver: providers.github.resolvers.usernameMatchingUserEntityName(),