Merge pull request #16526 from backstage/goa/cli-admin-command

cli: Add `onboard` command
This commit is contained in:
Johan Haals
2023-03-16 11:14:35 +01:00
committed by GitHub
19 changed files with 840 additions and 32 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Add `onboard` command. While still in development, this command aims to guide users in setting up their Backstage App.
+1
View File
@@ -13,6 +13,7 @@ yarn.lock @backstage/maintainers @back
/docs/features/search @backstage/techdocs-core
/docs/features/techdocs @backstage/techdocs-core
/docs/plugins/integrating-search-into-plugins.md @backstage/techdocs-core
/packages/cli/src/commands/onboard @backstage/sharks
/packages/techdocs-cli @backstage/techdocs-core
/packages/techdocs-cli-embedded-app @backstage/techdocs-core
/plugins/adr @backstage/maintainers @kuangp
+1
View File
@@ -39,6 +39,7 @@
"@backstage/types": "workspace:^",
"@esbuild-kit/cjs-loader": "^2.4.1",
"@manypkg/get-packages": "^1.1.3",
"@octokit/oauth-app": "^4.2.0",
"@octokit/request": "^6.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
"@rollup/plugin-commonjs": "^23.0.0",
+8
View File
@@ -25,6 +25,13 @@ const configOption = [
Array<string>(),
] as const;
export function registerOnboardCommand(program: Command) {
program
.command('onboard', { hidden: true })
.description('Get help setting up your Backstage App.')
.action(lazy(() => import('./onboard').then(m => m.command)));
}
export function registerRepoCommand(program: Command) {
const command = program
.command('repo [command]')
@@ -360,6 +367,7 @@ export function registerCommands(program: Command) {
registerRepoCommand(program);
registerScriptCommand(program);
registerMigrateCommand(program);
registerOnboardCommand(program);
program
.command('versions:bump')
@@ -0,0 +1,22 @@
# onboard
## Local dev
# Create a new app
```bash
cd ..
npx @backstage/create-app
cd new-backstage-app
# Run the modded cli in the new app
../RELATIVE_PATH_TO_PROJECT/backstage/packages/cli/bin/backstage-cli onboard
? Do you want to set up Authentication for this project? (Y/n)
...
# Try the app with the new changes
yarn dev
```
@@ -0,0 +1,63 @@
/*
* 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.
*/
import inquirer from 'inquirer';
import { Task } from '../../../lib/tasks';
import { github, Answers as GitHubAnswers } from './providers/github';
import { gitlab, Answers as GitLabAnswers } from './providers/gitlab';
export { type GitHubAnswers, type GitLabAnswers };
export async function auth(): Promise<{
provider: string;
answers: GitHubAnswers | GitLabAnswers;
}> {
const answers = await inquirer.prompt<{
provider: string;
}>([
{
type: 'list',
name: 'provider',
message: 'Please select an authentication provider:',
choices: ['GitHub', 'GitLab'],
},
]);
const { provider } = answers;
let providerAnswers;
switch (provider) {
case 'GitHub': {
providerAnswers = await github();
break;
}
case '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();
return {
provider,
answers: providerAnswers,
};
}
@@ -0,0 +1,43 @@
/*
* 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.
*/
import * as fs from 'fs-extra';
import * as path from 'path';
import * as differ from 'diff';
import { PATCH_FOLDER } from '../files';
import { findPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const { targetRoot } = findPaths(__dirname);
export const patch = async (patchFile: string) => {
const patchContent = await fs.readFile(
path.join(PATCH_FOLDER, patchFile),
'utf8',
);
const targetName = patchContent.split('\n')[0].replace('--- a', '');
const targetFile = path.join(targetRoot, targetName);
const oldContent = await fs.readFile(targetFile, 'utf8');
const newContent = differ.applyPatch(oldContent, patchContent);
if (!newContent) {
throw new Error(
`Patch ${patchFile} was not applied correctly.
Did you change ${targetName} manually before running this command?`,
);
}
return await fs.writeFile(targetFile, newContent, 'utf8');
};
@@ -0,0 +1,25 @@
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -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}
+ provider={{
+ id: 'github-auth-provider',
+ title: 'GitHub',
+ message: 'Sign in using GitHub',
+ apiRef: githubAuthApiRef,
+ }}
+ />
+ ),
+ },
@@ -0,0 +1,25 @@
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -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 { gitlabAuthApiRef } from '@backstage/core-plugin-api';
@@ -38,0 +44,14 @@ const app = createApp({
+ components: {
+ SignInPage: props => (
+ <SignInPage
+ {...props}
+ provider={{
+ id: 'gitlab-auth-provider',
+ title: 'GitLab',
+ message: 'Sign in using GitLab',
+ apiRef: gitlabAuthApiRef,
+ }}
+ />
+ ),
+ },
@@ -0,0 +1,5 @@
--- a/packages/backend/src/plugins/auth.ts
+++ b/packages/backend/src/plugins/auth.ts
@@ -38 +38 @@ export default async function createPlugin(
- github: providers.github.create({
+ gitlab: providers.gitlab.create({
@@ -0,0 +1,147 @@
/*
* 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.
*/
import { OAuthApp } from '@octokit/oauth-app';
import chalk from 'chalk';
import * as fs from 'fs-extra';
import inquirer from 'inquirer';
import { Task } from '../../../../lib/tasks';
import { updateConfigFile } from '../../config';
import { APP_CONFIG_FILE, PATCH_FOLDER } from '../../files';
import { patch } from '../patch';
export type Answers = {
clientSecret: string;
clientId: string;
hasEnterprise: boolean;
enterpriseInstanceUrl?: string;
};
const validateCredentials = async (clientId: string, clientSecret: string) => {
try {
const app = new OAuthApp({
clientId,
clientSecret,
});
await app.createToken({
code: '%NOT-VALID-CODE%',
});
} catch (error) {
// @octokit/request returns a error.response object when a request is rejected.
// We can check it to see what kind of error we received.
// If error.response is successful we can double-check that the error itself was due to the bad code.
// If that's the case then we can assume that the client id and secret exists as we otherwise would
// have gotten a 400/404.
if (
error.response.status !== 200 &&
error.response.data.error !== 'bad_verification_code'
) {
throw new Error(`Validating GitHub Credentials failed.`);
}
}
};
const getConfig = (answers: Answers) => {
const { clientId, clientSecret, hasEnterprise, enterpriseInstanceUrl } =
answers;
return {
auth: {
providers: {
github: {
development: {
clientId,
clientSecret,
...(hasEnterprise && {
enterpriseInstanceUrl,
}),
},
},
},
},
};
};
export const github = async (): Promise<Answers> => {
Task.log(`
To add GitHub authentication, you must create an OAuth App from the GitHub developer settings: ${chalk.blue(
'https://github.com/settings/developers',
)}
The Homepage URL should point to Backstage's frontend, while the Authorization callback URL will point to the auth backend.
Settings for local development:
${chalk.cyan(`
Homepage URL: http://localhost:3000
Authorization callback URL: http://localhost:7007/api/auth/github/handler/frame`)}
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: 'clientId',
message: 'What is your Client Id?',
validate: (input: string) => (input.length ? true : false),
},
{
type: 'input',
name: 'clientSecret',
message: 'What is your Client Secret?',
validate: (input: string) => (input.length ? true : false),
},
{
type: 'confirm',
name: 'hasEnterprise',
message: 'Are you using GitHub Enterprise?',
},
{
type: 'input',
name: 'enterpriseInstanceUrl',
message: 'What is your URL for GitHub Enterprise?',
when: ({ hasEnterprise }) => hasEnterprise,
validate: (input: string) => Boolean(new URL(input)),
},
]);
const { clientId, clientSecret } = answers;
const config = getConfig(answers);
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),
);
const patches = await fs.readdir(PATCH_FOLDER);
for (const patchFile of patches.filter(p => p.includes('github'))) {
await Task.forItem('Patching', patchFile, async () => {
await patch(patchFile);
});
}
return answers;
};
@@ -0,0 +1,115 @@
/*
* 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.
*/
import chalk from 'chalk';
import * as fs from 'fs-extra';
import inquirer from 'inquirer';
import { Task } from '../../../../lib/tasks';
import { updateConfigFile } from '../../config';
import { APP_CONFIG_FILE, PATCH_FOLDER } from '../../files';
import { patch } from '../patch';
const getConfig = (answers: Answers) => {
const { clientId, clientSecret, hasAudience, audience } = answers;
return {
auth: {
providers: {
gitlab: {
development: {
clientId,
clientSecret,
...(hasAudience && {
audience,
}),
},
},
},
},
};
};
export type Answers = {
clientSecret: string;
clientId: string;
hasAudience: boolean;
audience?: string;
};
export const gitlab = async (): Promise<Answers> => {
Task.log(`
To add GitLab authentication, you must create an Application from the GitLab Settings: ${chalk.blue(
'https://gitlab.com/-/profile/applications',
)}
The Redirect URI should point to your Backstage backend auth handler.
Settings for local development:
${chalk.cyan(`
Name: Backstage (or your custom app name)
Redirect URI: http://localhost:7007/api/auth/gitlab/handler/frame
Scopes: read_api and read_user`)}
You can find the full documentation page here: ${chalk.blue(
'https://backstage.io/docs/auth/gitlab/provider',
)}
`);
const answers = await inquirer.prompt<Answers>([
{
type: 'input',
name: 'clientId',
message: 'What is your Application Id?',
validate: (input: string) => (input.length ? true : false),
},
{
type: 'input',
name: 'clientSecret',
message: 'What is your Application Secret?',
validate: (input: string) => (input.length ? true : false),
},
{
type: 'confirm',
name: 'hasAudience',
message: 'Do you have a self-hosted instance of GitLab?',
},
{
type: 'input',
name: 'audience',
message: 'What is the URL for your GitLab instance?',
when: ({ hasAudience }) => hasAudience,
validate: (input: string) => Boolean(new URL(input)),
},
]);
const config = getConfig(answers);
Task.log('Setting up GitLab Authentication for you...');
await Task.forItem(
'Updating',
APP_CONFIG_FILE,
async () => await updateConfigFile(APP_CONFIG_FILE, config),
);
const patches = await fs.readdir(PATCH_FOLDER);
for (const patchFile of patches.filter(p => p.includes('gitlab'))) {
await Task.forItem('Patching', patchFile, async () => {
await patch(patchFile);
});
}
return answers;
};
@@ -0,0 +1,69 @@
/*
* 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.
*/
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<void> {
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,
},
]);
const { shouldSetupAuth, shouldSetupScaffolder } = answers;
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();
}
@@ -0,0 +1,45 @@
/*
* 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.
*/
import * as fs from 'fs-extra';
import yaml from 'yaml';
const readYaml = async (file: string) => {
return yaml.parse(await fs.readFile(file, 'utf8'));
};
export const updateConfigFile = async <T>(file: string, config: T) => {
const staticContent =
'# Backstage override configuration for your local development environment \n';
const content = fs.existsSync(file)
? yaml.stringify(
{ ...(await readYaml(file)), ...config },
{
indent: 2,
},
)
: staticContent.concat(
yaml.stringify(
{ ...config },
{
indent: 2,
},
),
);
return await fs.writeFile(file, content, 'utf8');
};
@@ -0,0 +1,30 @@
/*
* 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.
*/
import { findPaths } from '@backstage/cli-common';
import * as path from 'path';
/* eslint-disable-next-line no-restricted-syntax */
const { targetRoot, ownDir } = findPaths(__dirname);
export const APP_CONFIG_FILE = path.join(targetRoot, 'app-config.local.yaml');
export const PATCH_FOLDER = path.join(
ownDir,
'src',
'commands',
'onboard',
'auth',
'patches',
);
@@ -0,0 +1,17 @@
/*
* 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 { command } from './command';
@@ -0,0 +1,136 @@
/*
* 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.
*/
import chalk from 'chalk';
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 = {
hasEnterprise: boolean;
enterpriseInstanceUrl: string;
apiBaseUrl: string;
};
const getConfig = ({
hasEnterprise,
apiBaseUrl,
host,
token,
}: {
hasEnterprise: boolean;
apiBaseUrl: string;
host: string;
token: string;
}) => ({
integrations: {
github: [
{
host,
token,
...(hasEnterprise && {
apiBaseUrl,
}),
},
],
},
});
export const github = async (providerAnswers?: GitHubAnswers) => {
// TODO(tudi2d): Is GitHub Enterprise a valid setup if there is no Authentication?
const answers = await inquirer.prompt<Answers>([
{
type: 'confirm',
name: 'hasEnterprise',
message: 'Are you using GitHub Enterprise?',
when: () => typeof providerAnswers === 'undefined',
},
{
type: 'input',
name: 'enterpriseInstanceUrl',
message: 'What is your URL for GitHub Enterprise?',
when: ({ hasEnterprise }) => hasEnterprise,
validate: (input: string) => Boolean(new URL(input)),
},
{
type: 'input',
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
},
]);
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(
`${host.origin}/settings/tokens/new`,
)}
Select the following scopes:
Reading software components:${chalk.cyan(`
- "repo"`)}
Reading organization data:${chalk.cyan(`
- "read:org"
- "read:user"
- "user:email"`)}
Publishing software templates:${chalk.cyan(`
- "repo"
- "workflow" (if templates include GitHub workflows)
`)}
You can find the full documentation page here: ${chalk.blue(
'https://backstage.io/docs/integrations/github/locations',
)}
`);
const { token } = await inquirer.prompt<{ token: string }>([
{
type: 'input',
name: 'token',
message:
'Please insert your personal access token to setup the GitHub Integration',
// TODO(tudi2d): validate
},
]);
const config = getConfig({
hasEnterprise: providerAnswers?.hasEnterprise ?? answers.hasEnterprise,
apiBaseUrl: host.origin + answers.apiBaseUrl,
host: host.hostname,
token,
});
Task.log('Setting up Software Templates using GitHub integration for you...');
await Task.forItem(
'Updating',
APP_CONFIG_FILE,
async () => await updateConfigFile(APP_CONFIG_FILE, config),
);
};
@@ -0,0 +1,75 @@
/*
* 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.
*/
import inquirer from 'inquirer';
import { Task } from '../../../lib/tasks';
import { GitHubAnswers, GitLabAnswers } from '../auth';
import { github } from './github';
enum Integration {
GITHUB = 'GitHub',
}
const Integrations: Integration[] = [Integration.GITHUB];
export async function integrations(providerInfo?: {
provider: string;
answers: GitHubAnswers | GitLabAnswers;
}): Promise<void> {
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();
}
+8 -32
View File
@@ -3652,6 +3652,7 @@ __metadata:
"@backstage/types": "workspace:^"
"@esbuild-kit/cjs-loader": ^2.4.1
"@manypkg/get-packages": ^1.1.3
"@octokit/oauth-app": ^4.2.0
"@octokit/request": ^6.0.0
"@pmmmwh/react-refresh-webpack-plugin": ^0.5.7
"@rollup/plugin-commonjs": ^23.0.0
@@ -12271,9 +12272,9 @@ __metadata:
languageName: node
linkType: hard
"@octokit/oauth-app@npm:^4.0.4, @octokit/oauth-app@npm:^4.0.6":
version: 4.0.6
resolution: "@octokit/oauth-app@npm:4.0.6"
"@octokit/oauth-app@npm:^4.0.4, @octokit/oauth-app@npm:^4.0.6, @octokit/oauth-app@npm:^4.2.0":
version: 4.2.0
resolution: "@octokit/oauth-app@npm:4.2.0"
dependencies:
"@octokit/auth-oauth-app": ^5.0.0
"@octokit/auth-oauth-user": ^2.0.0
@@ -12282,13 +12283,9 @@ __metadata:
"@octokit/oauth-authorization-url": ^5.0.0
"@octokit/oauth-methods": ^2.0.0
"@types/aws-lambda": ^8.10.83
aws-lambda: ^1.0.7
fromentries: ^1.3.1
universal-user-agent: ^6.0.0
dependenciesMeta:
aws-lambda:
optional: true
checksum: 0d7ceadda4668748a436f52f6c4ae3160a4a5c81a92592c0a6cfc75b0a065ed480880d8861efcabc6ca41b3ccf753d1c1044782a6c8d5fb481d6e8a0acf6d32e
checksum: 6cf5638d27e589daa577653177f2b135c0c346c8f74cc420d9fd0dc0f559a84040073eee147bd555a55bb5756bd0940e6dc4c3a6b3a508a818e3d5bee3837095
languageName: node
linkType: hard
@@ -17274,20 +17271,6 @@ __metadata:
languageName: node
linkType: hard
"aws-lambda@npm:^1.0.7":
version: 1.0.7
resolution: "aws-lambda@npm:1.0.7"
dependencies:
aws-sdk: ^2.814.0
commander: ^3.0.2
js-yaml: ^3.14.1
watchpack: ^2.0.0-beta.10
bin:
lambda: bin/lambda
checksum: 11316e87b5c4fc36e6bd0495742a3c0ed13befc9527a7b251a58180d141d9afd68b684f37aeb3b53d117d3c2f96747eace31826b683543f1edddc03f392865fd
languageName: node
linkType: hard
"aws-os-connection@npm:^0.2.0":
version: 0.2.0
resolution: "aws-os-connection@npm:0.2.0"
@@ -17334,7 +17317,7 @@ __metadata:
languageName: node
linkType: hard
"aws-sdk@npm:^2.1231.0, aws-sdk@npm:^2.814.0, aws-sdk@npm:^2.840.0, aws-sdk@npm:^2.948.0":
"aws-sdk@npm:^2.1231.0, aws-sdk@npm:^2.840.0, aws-sdk@npm:^2.948.0":
version: 2.1279.0
resolution: "aws-sdk@npm:2.1279.0"
dependencies:
@@ -19220,13 +19203,6 @@ __metadata:
languageName: node
linkType: hard
"commander@npm:^3.0.2":
version: 3.0.2
resolution: "commander@npm:3.0.2"
checksum: 6d14ad030d1904428139487ed31febcb04c1604db2b8d9fae711f60ee6718828dc0e11602249e91c8a97b0e721e9c6d53edbc166bad3cde1596851d59a8f824d
languageName: node
linkType: hard
"commander@npm:^4.0.0":
version: 4.1.1
resolution: "commander@npm:4.1.1"
@@ -27072,7 +27048,7 @@ __metadata:
languageName: node
linkType: hard
"js-yaml@npm:^3.10.0, js-yaml@npm:^3.13.0, js-yaml@npm:^3.13.1, js-yaml@npm:^3.14.1, js-yaml@npm:^3.6.1, js-yaml@npm:^3.8.3":
"js-yaml@npm:^3.10.0, js-yaml@npm:^3.13.0, js-yaml@npm:^3.13.1, js-yaml@npm:^3.6.1, js-yaml@npm:^3.8.3":
version: 3.14.1
resolution: "js-yaml@npm:3.14.1"
dependencies:
@@ -38488,7 +38464,7 @@ __metadata:
languageName: node
linkType: hard
"watchpack@npm:^2.0.0-beta.10, watchpack@npm:^2.4.0":
"watchpack@npm:^2.4.0":
version: 2.4.0
resolution: "watchpack@npm:2.4.0"
dependencies: