GitHub app installation

This commit is contained in:
Eric Allam
2023-02-10 13:07:35 +00:00
parent 94154bd96c
commit ddfce4e3c7
16 changed files with 825 additions and 25 deletions
+6
View File
@@ -49,6 +49,12 @@ const EnvironmentSchema = z.object({
PULSAR_AUDIENCE: z.string().optional(),
PULSAR_DEBUG: z.string().optional(),
INTERNAL_TRIGGER_API_KEY: z.string().optional(),
GITHUB_APP_NAME: z.string().optional(),
GITHUB_APP_ID: z.string().optional(),
GITHUB_APP_CLIENT_ID: z.string().optional(),
GITHUB_APP_CLIENT_SECRET: z.string().optional(),
GITHUB_APP_PRIVATE_KEY: z.string().optional(),
GITHUB_APP_WEBHOOK_SECRET: z.string().optional(),
});
export type Environment = z.infer<typeof EnvironmentSchema>;
@@ -0,0 +1,18 @@
import invariant from "tiny-invariant";
import { Container } from "~/components/layout/Container";
import { Title } from "~/components/primitives/text/Title";
import { useCurrentEnvironment } from "~/hooks/useEnvironments";
import { useCurrentOrganization } from "~/hooks/useOrganizations";
export default function NewWorkflowPage() {
const environment = useCurrentEnvironment();
const currentOrganization = useCurrentOrganization();
invariant(currentOrganization, "Organization must be defined");
invariant(environment, "Environment must be defined");
return (
<Container>
<Title>Deploy a new workflow</Title>
</Container>
);
}
@@ -0,0 +1,22 @@
import { LoaderArgs, redirect } from "@remix-run/server-runtime";
import { z } from "zod";
import { StartAppInstallation } from "~/services/github/startAppInstallation.server";
import { requireUserId } from "~/services/session.server";
const ParamsSchema = z.object({
organizationSlug: z.string(),
});
export async function loader({ request, params }: LoaderArgs) {
const userId = await requireUserId(request);
const { organizationSlug } = ParamsSchema.parse(params);
const service = new StartAppInstallation();
const redirectTo = await service.call({
userId,
organizationSlug,
});
return redirect(redirectTo ?? `/orgs/${organizationSlug}`);
}
@@ -0,0 +1,25 @@
import { EmitterWebhookEventName } from "@octokit/webhooks";
import { ActionArgs } from "@remix-run/server-runtime";
import { webhooks } from "~/services/github/githubApp.server";
export async function action({ request }: ActionArgs) {
if (!webhooks) {
return new Response("", { status: 200 });
}
const payload = await request.text();
const headers = Object.fromEntries(request.headers.entries());
const id = headers["x-github-delivery"];
const name = headers["x-github-event"];
const signature = headers["x-hub-signature"];
await webhooks.verifyAndReceive({
id,
name: name as EmitterWebhookEventName,
payload,
signature,
});
return new Response("", { status: 200 });
}
@@ -0,0 +1,27 @@
import { LoaderArgs, redirect } from "@remix-run/server-runtime";
import { z } from "zod";
import { AppInstallationCallback } from "~/services/github/appInstallationCallback.server";
import { requireUserId } from "~/services/session.server";
const ParamSchema = z.object({
code: z.string(),
state: z.string(),
installation_id: z.string(),
});
export async function loader({ request }: LoaderArgs) {
await requireUserId(request);
const url = new URL(request.url);
const params = Object.fromEntries(url.searchParams.entries());
const service = new AppInstallationCallback();
const authorization = await service.call(ParamSchema.parse(params));
if (authorization) {
return redirect(`/orgs/${authorization.organization.slug}/workflows/start`);
} else {
return redirect(`/`);
}
}
@@ -0,0 +1,96 @@
import type { PrismaClient } from "~/db.server";
import { prisma } from "~/db.server";
import {
getAppInstallation,
oauthApp,
octokit,
} from "~/services/github/githubApp.server";
export class AppInstallationCallback {
#prismaClient: PrismaClient;
constructor(prismaClient: PrismaClient = prisma) {
this.#prismaClient = prismaClient;
}
public async call({
code,
state,
installation_id,
}: {
code: string;
state: string;
installation_id: string;
}) {
if (!oauthApp || !octokit) {
return;
}
const attempt =
await this.#prismaClient.gitHubAppAuthorizationAttempt.findUnique({
where: {
id: state,
},
});
if (!attempt) {
return;
}
const installation = await getAppInstallation({
installation_id: Number(installation_id),
});
if (!installation) {
return;
}
const { authentication } = await oauthApp.createToken({ code, state });
const authorization =
await this.#prismaClient.gitHubAppAuthorization.create({
data: {
user: {
connect: {
id: attempt.userId,
},
},
organization: {
connect: {
id: attempt.organizationId,
},
},
token: authentication.token,
// @ts-ignore
tokenExpiresAt: new Date(authentication.expiresAt),
// @ts-ignore
refreshToken: authentication.refreshToken,
// @ts-ignore
refreshTokenExpiresAt: new Date(authentication.refreshTokenExpiresAt),
installationId: installation.id,
account: installation.account ?? {},
permissions: installation.permissions,
repositorySelection: installation.repository_selection,
accessTokensUrl: installation.access_tokens_url,
repositoriesUrl: installation.repositories_url,
htmlUrl: installation.html_url,
events: installation.events,
},
include: {
organization: {
select: {
slug: true,
},
},
},
});
await this.#prismaClient.gitHubAppAuthorizationAttempt.delete({
where: {
id: attempt.id,
},
});
return authorization;
}
}
@@ -0,0 +1,168 @@
import { Webhooks, EmitterWebhookEvent } from "@octokit/webhooks";
import { Octokit } from "@octokit/core";
import { createAppAuth } from "@octokit/auth-app";
import { OAuthApp } from "@octokit/oauth-app";
import { createUnauthenticatedAuth } from "@octokit/auth-unauthenticated";
import { env } from "~/env.server";
import { Options } from "@octokit/oauth-app/dist-types/types";
import type { Endpoints } from "@octokit/types";
export const octokit = env.GITHUB_APP_PRIVATE_KEY
? new Octokit({
authStrategy: createAppAuth,
auth: {
appId: env.GITHUB_APP_ID,
privateKey: Buffer.from(env.GITHUB_APP_PRIVATE_KEY, "base64").toString(
"utf8"
),
clientId: env.GITHUB_APP_CLIENT_ID,
clientSecret: env.GITHUB_APP_CLIENT_SECRET,
},
log: {
debug: console.log,
info: console.log,
warn: console.warn,
error: console.error,
},
})
: undefined;
export const oauthApp = createOauthApp();
export const webhooks = createWebhooks();
declare global {
var __github_webhooks__:
| Webhooks<EmitterWebhookEvent & { octokit: Octokit }>
| undefined;
var __github_oauth_app__: OAuthApp<Options<"github-app">> | undefined;
}
function createOauthApp() {
if (typeof global.__github_oauth_app__ !== "undefined") {
return global.__github_oauth_app__;
}
if (typeof env.GITHUB_APP_CLIENT_ID === "undefined") {
return;
}
if (typeof env.GITHUB_APP_CLIENT_SECRET === "undefined") {
return;
}
global.__github_oauth_app__ = new OAuthApp({
clientId: env.GITHUB_APP_CLIENT_ID,
clientSecret: env.GITHUB_APP_CLIENT_SECRET,
clientType: "github-app",
});
global.__github_oauth_app__.on("token", async (event) => {});
return __github_oauth_app__;
}
function createWebhooks() {
if (typeof global.__github_webhooks__ !== "undefined") {
return global.__github_webhooks__;
}
if (
typeof env.GITHUB_APP_WEBHOOK_SECRET === "undefined" ||
typeof octokit === "undefined"
) {
return;
}
global.__github_webhooks__ = __webhooks(
octokit,
env.GITHUB_APP_WEBHOOK_SECRET
);
global.__github_webhooks__.on("push", ({ octokit, payload }) => {});
global.__github_webhooks__.onAny((event) => {});
return global.__github_webhooks__;
}
function __webhooks(
appOctokit: Octokit,
secret: string
// Explict return type for better debugability and performance,
// see https://github.com/octokit/app.js/pull/201
): Webhooks<EmitterWebhookEvent & { octokit: Octokit }> {
return new Webhooks({
secret,
transform: async (event) => {
if (
!("installation" in event.payload) ||
typeof event.payload.installation !== "object"
) {
const octokit = new (appOctokit.constructor as typeof Octokit)({
authStrategy: createUnauthenticatedAuth,
auth: {
reason: `"installation" key missing in webhook event payload`,
},
});
return {
...event,
octokit,
};
}
const installationId = event.payload.installation.id;
const octokit = (await appOctokit.auth({
type: "installation",
installationId,
factory(auth: any) {
return new auth.octokit.constructor({
...auth.octokitOptions,
authStrategy: createAppAuth,
...{
auth: {
...auth,
installationId,
},
},
});
},
})) as Octokit;
// set `x-github-delivery` header on all requests sent in response to the current
// event. This allows GitHub Support to correlate the request with the event.
// This is not documented and not considered public API, the header may change.
// Once we document this as best practice on https://docs.github.com/en/rest/guides/best-practices-for-integrators
// we will make it official
/* istanbul ignore next */
octokit.hook.before("request", (options) => {
options.headers["x-github-delivery"] = event.id;
});
return {
...event,
octokit,
};
},
});
}
type GetAppInstallationEndpoint =
Endpoints["GET /app/installations/{installation_id}"];
export async function getAppInstallation({
installation_id,
}: GetAppInstallationEndpoint["parameters"]) {
if (typeof octokit === "undefined") {
return;
}
const response = await octokit.request(
"GET /app/installations/{installation_id}",
{
installation_id,
}
);
return response.data;
}
@@ -0,0 +1,42 @@
import type { PrismaClient } from "~/db.server";
import { prisma } from "~/db.server";
import { env } from "~/env.server";
export class StartAppInstallation {
#prismaClient: PrismaClient;
constructor(prismaClient: PrismaClient = prisma) {
this.#prismaClient = prismaClient;
}
public async call({
userId,
organizationSlug,
}: {
userId: string;
organizationSlug: string;
}) {
if (!env.GITHUB_APP_NAME) {
return;
}
const organization = await this.#prismaClient.organization.findUnique({
where: {
slug: organizationSlug,
},
});
if (!organization) {
return;
}
const attempt = await prisma.gitHubAppAuthorizationAttempt.create({
data: {
organizationId: organization.id,
userId,
},
});
return `https://github.com/apps/${env.GITHUB_APP_NAME}/installations/new?state=${attempt.id}`;
}
}
+10 -1
View File
@@ -58,6 +58,12 @@
"@lezer/highlight": "^1.1.2",
"@nangohq/pizzly-frontend": "^0.3.7",
"@nangohq/pizzly-node": "^0.4.1",
"@octokit/app": "^13.1.2",
"@octokit/auth-app": "^4.0.9",
"@octokit/auth-unauthenticated": "^3.0.4",
"@octokit/core": "^4.2.0",
"@octokit/oauth-app": "^4.2.0",
"@octokit/webhooks": "^10.4.0",
"@prisma/client": "^4.3.0",
"@remix-run/express": "^1.7.2",
"@remix-run/node": "^1.7.2",
@@ -134,6 +140,7 @@
},
"devDependencies": {
"@faker-js/faker": "^7.6.0",
"@octokit/types": "^9.0.0",
"@remix-run/dev": "^1.7.2",
"@remix-run/eslint-config": "^1.7.2",
"@swc/core": "^1.3.4",
@@ -166,6 +173,7 @@
"@types/slug": "^5.0.3",
"@vitejs/plugin-react": "^2.0.1",
"@vitest/coverage-c8": "^0.23.4",
"auth-unauthenticated": "link:@types/@octokit/auth-unauthenticated",
"autoprefixer": "^10.4.7",
"c8": "^7.11.3",
"cli-ux": "^6.0.9",
@@ -194,7 +202,8 @@
"typescript": "^4.8.4",
"vite": "^3.1.4",
"vite-tsconfig-paths": "^3.5.1",
"vitest": "^0.23.4"
"vitest": "^0.23.4",
"webhooks": "link:@types/@octokit/webhooks"
},
"engines": {
"node": ">=16.0.0"
@@ -0,0 +1,18 @@
-- CreateTable
CREATE TABLE "GitHubAppAuthorization" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"token" TEXT NOT NULL,
"tokenExpiresAt" TIMESTAMP(3) NOT NULL,
"refreshToken" TEXT NOT NULL,
"refreshTokenExpiresAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "GitHubAppAuthorization_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "GitHubAppAuthorization" ADD CONSTRAINT "GitHubAppAuthorization_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "GitHubAppAuthorization" ADD CONSTRAINT "GitHubAppAuthorization_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,5 @@
-- CreateEnum
CREATE TYPE "GitHubAppAuthorizationStatus" AS ENUM ('PENDING', 'AUTHORIZED');
-- AlterTable
ALTER TABLE "GitHubAppAuthorization" ADD COLUMN "status" "GitHubAppAuthorizationStatus" NOT NULL DEFAULT 'PENDING';
@@ -0,0 +1,20 @@
/*
Warnings:
- You are about to drop the column `status` on the `GitHubAppAuthorization` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "GitHubAppAuthorization" DROP COLUMN "status";
-- DropEnum
DROP TYPE "GitHubAppAuthorizationStatus";
-- CreateTable
CREATE TABLE "GitHubAppAuthorizationAttempt" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
CONSTRAINT "GitHubAppAuthorizationAttempt_pkey" PRIMARY KEY ("id")
);
@@ -0,0 +1,25 @@
/*
Warnings:
- A unique constraint covering the columns `[installationId]` on the table `GitHubAppAuthorization` will be added. If there are existing duplicate values, this will fail.
- Added the required column `accessTokensUrls` to the `GitHubAppAuthorization` table without a default value. This is not possible if the table is not empty.
- Added the required column `account` to the `GitHubAppAuthorization` table without a default value. This is not possible if the table is not empty.
- Added the required column `htmlUrl` to the `GitHubAppAuthorization` table without a default value. This is not possible if the table is not empty.
- Added the required column `installationId` to the `GitHubAppAuthorization` table without a default value. This is not possible if the table is not empty.
- Added the required column `permissions` to the `GitHubAppAuthorization` table without a default value. This is not possible if the table is not empty.
- Added the required column `repositoriesUrl` to the `GitHubAppAuthorization` table without a default value. This is not possible if the table is not empty.
- Added the required column `repositorySelection` to the `GitHubAppAuthorization` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "GitHubAppAuthorization" ADD COLUMN "accessTokensUrls" TEXT NOT NULL,
ADD COLUMN "account" JSONB NOT NULL,
ADD COLUMN "events" TEXT[],
ADD COLUMN "htmlUrl" TEXT NOT NULL,
ADD COLUMN "installationId" INTEGER NOT NULL,
ADD COLUMN "permissions" JSONB NOT NULL,
ADD COLUMN "repositoriesUrl" TEXT NOT NULL,
ADD COLUMN "repositorySelection" TEXT NOT NULL;
-- CreateIndex
CREATE UNIQUE INDEX "GitHubAppAuthorization_installationId_key" ON "GitHubAppAuthorization"("installationId");
@@ -0,0 +1,10 @@
/*
Warnings:
- You are about to drop the column `accessTokensUrls` on the `GitHubAppAuthorization` table. All the data in the column will be lost.
- Added the required column `accessTokensUrl` to the `GitHubAppAuthorization` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "GitHubAppAuthorization" DROP COLUMN "accessTokensUrls",
ADD COLUMN "accessTokensUrl" TEXT NOT NULL;
+42 -10
View File
@@ -28,7 +28,8 @@ model User {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organizations Organization[]
organizations Organization[]
gitHubAppAuthorizations GitHubAppAuthorization[]
}
enum AuthenticationMethod {
@@ -44,15 +45,16 @@ model Organization {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
users User[]
workflows Workflow[]
environments RuntimeEnvironment[]
apiConnections APIConnection[]
events TriggerEvent[]
externalSources ExternalSource[]
eventRules EventRule[]
schedulerSources SchedulerSource[]
internalSources InternalSource[]
users User[]
workflows Workflow[]
environments RuntimeEnvironment[]
apiConnections APIConnection[]
events TriggerEvent[]
externalSources ExternalSource[]
eventRules EventRule[]
schedulerSources SchedulerSource[]
internalSources InternalSource[]
gitHubAppAuthorizations GitHubAppAuthorization[]
}
model APIConnection {
@@ -556,3 +558,33 @@ model FetchResponse {
createdAt DateTime @default(now())
}
model GitHubAppAuthorizationAttempt {
id String @id @default(cuid())
userId String
organizationId String
}
model GitHubAppAuthorization {
id String @id @default(cuid())
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
userId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
organizationId String
installationId Int @unique
account Json
permissions Json
repositorySelection String
accessTokensUrl String
repositoriesUrl String
htmlUrl String
events String[]
token String
tokenExpiresAt DateTime
refreshToken String
refreshTokenExpiresAt DateTime
}
+291 -14
View File
@@ -50,6 +50,13 @@ importers:
'@lezer/highlight': ^1.1.2
'@nangohq/pizzly-frontend': ^0.3.7
'@nangohq/pizzly-node': ^0.4.1
'@octokit/app': ^13.1.2
'@octokit/auth-app': ^4.0.9
'@octokit/auth-unauthenticated': ^3.0.4
'@octokit/core': ^4.2.0
'@octokit/oauth-app': ^4.2.0
'@octokit/types': ^9.0.0
'@octokit/webhooks': ^10.4.0
'@prisma/client': ^4.3.0
'@remix-run/dev': ^1.7.2
'@remix-run/eslint-config': ^1.7.2
@@ -98,6 +105,7 @@ importers:
'@uiw/react-codemirror': ^4.13.2
'@vitejs/plugin-react': ^2.0.1
'@vitest/coverage-c8': ^0.23.4
auth-unauthenticated: link:@types/@octokit/auth-unauthenticated
autoprefixer: ^10.4.7
bcryptjs: ^2.4.3
c8: ^7.11.3
@@ -181,6 +189,7 @@ importers:
vite: ^3.1.4
vite-tsconfig-paths: ^3.5.1
vitest: ^0.23.4
webhooks: link:@types/@octokit/webhooks
zod: ^3.20.2
zod-error: ^1.1.0
dependencies:
@@ -202,6 +211,12 @@ importers:
'@lezer/highlight': 1.1.3
'@nangohq/pizzly-frontend': 0.3.7
'@nangohq/pizzly-node': 0.4.1
'@octokit/app': 13.1.2
'@octokit/auth-app': 4.0.9
'@octokit/auth-unauthenticated': 3.0.4
'@octokit/core': 4.2.0
'@octokit/oauth-app': 4.2.0
'@octokit/webhooks': 10.5.1
'@prisma/client': 4.8.1_prisma@4.8.1
'@remix-run/express': 1.12.0_express@4.18.2
'@remix-run/node': 1.12.0
@@ -277,6 +292,7 @@ importers:
zod-error: 1.1.0
devDependencies:
'@faker-js/faker': 7.6.0
'@octokit/types': 9.0.0
'@remix-run/dev': 1.12.0
'@remix-run/eslint-config': 1.12.0_ol4nhuzbuflsbzk2mijpqykzba
'@swc/core': 1.3.26
@@ -309,6 +325,7 @@ importers:
'@types/slug': 5.0.3
'@vitejs/plugin-react': 2.2.0_vite@3.2.5
'@vitest/coverage-c8': 0.23.4_happy-dom@6.0.4
auth-unauthenticated: link:@types/@octokit/auth-unauthenticated
autoprefixer: 10.4.13_postcss@8.4.21
c8: 7.12.0
cli-ux: 6.0.9
@@ -337,6 +354,7 @@ importers:
vite: 3.2.5_@types+node@18.11.18
vite-tsconfig-paths: 3.6.0_vite@3.2.5
vitest: 0.23.4_happy-dom@6.0.4
webhooks: link:@types/@octokit/webhooks
apps/wss:
specifiers:
@@ -4494,25 +4512,206 @@ packages:
engines: {node: '>=12.0.0'}
dev: true
/@octokit/openapi-types/14.0.0:
resolution: {integrity: sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==}
dev: false
/@octokit/request-error/3.0.2:
resolution: {integrity: sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg==}
/@octokit/app/13.1.2:
resolution: {integrity: sha512-Kf+h5sa1SOI33hFsuHvTsWj1jUrjp1x4MuiJBq7U/NicfEGa6nArPUoDnyfP/YTmcQ5cQ5yvOgoIBkbwPg6kzQ==}
engines: {node: '>= 14'}
dependencies:
'@octokit/types': 8.1.0
'@octokit/auth-app': 4.0.9
'@octokit/auth-unauthenticated': 3.0.4
'@octokit/core': 4.2.0
'@octokit/oauth-app': 4.2.0
'@octokit/plugin-paginate-rest': 6.0.0_@octokit+core@4.2.0
'@octokit/types': 9.0.0
'@octokit/webhooks': 10.5.1
transitivePeerDependencies:
- encoding
dev: false
/@octokit/auth-app/4.0.9:
resolution: {integrity: sha512-VFpKIXhHO+kVJtane5cEvdYPtjDKCOI0uKsRrsZfJP+uEu7rcPbQCLCcRKgyT+mUIzGr1IIOmwP/lFqSip1dXA==}
engines: {node: '>= 14'}
dependencies:
'@octokit/auth-oauth-app': 5.0.5
'@octokit/auth-oauth-user': 2.1.1
'@octokit/request': 6.2.3
'@octokit/request-error': 3.0.3
'@octokit/types': 9.0.0
'@types/lru-cache': 5.1.1
deprecation: 2.3.1
lru-cache: 6.0.0
universal-github-app-jwt: 1.1.1
universal-user-agent: 6.0.0
transitivePeerDependencies:
- encoding
dev: false
/@octokit/auth-oauth-app/5.0.5:
resolution: {integrity: sha512-UPX1su6XpseaeLVCi78s9droxpGtBWIgz9XhXAx9VXabksoF0MyI5vaa1zo1njyYt6VaAjFisC2A2Wchcu2WmQ==}
engines: {node: '>= 14'}
dependencies:
'@octokit/auth-oauth-device': 4.0.4
'@octokit/auth-oauth-user': 2.1.1
'@octokit/request': 6.2.3
'@octokit/types': 9.0.0
'@types/btoa-lite': 1.0.0
btoa-lite: 1.0.0
universal-user-agent: 6.0.0
transitivePeerDependencies:
- encoding
dev: false
/@octokit/auth-oauth-device/4.0.4:
resolution: {integrity: sha512-Xl85BZYfqCMv+Uvz33nVVUjE7I/PVySNaK6dRRqlkvYcArSr9vRcZC9KVjXYObGRTCN6mISeYdakAZvWEN4+Jw==}
engines: {node: '>= 14'}
dependencies:
'@octokit/oauth-methods': 2.0.5
'@octokit/request': 6.2.3
'@octokit/types': 9.0.0
universal-user-agent: 6.0.0
transitivePeerDependencies:
- encoding
dev: false
/@octokit/auth-oauth-user/2.1.1:
resolution: {integrity: sha512-JgqnNNPf9CaWLxWm9uh2WgxcaVYhxBR09NVIPTiMU2dVZ3FObOHs3njBiLNw+zq84k+rEdm5Y7AsiASrZ84Apg==}
engines: {node: '>= 14'}
dependencies:
'@octokit/auth-oauth-device': 4.0.4
'@octokit/oauth-methods': 2.0.5
'@octokit/request': 6.2.3
'@octokit/types': 9.0.0
btoa-lite: 1.0.0
universal-user-agent: 6.0.0
transitivePeerDependencies:
- encoding
dev: false
/@octokit/auth-token/3.0.3:
resolution: {integrity: sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==}
engines: {node: '>= 14'}
dependencies:
'@octokit/types': 9.0.0
dev: false
/@octokit/auth-unauthenticated/3.0.4:
resolution: {integrity: sha512-AT74XGBylcLr4lmUp1s6mjSUgphGdlse21Qjtv5DzpX1YOl5FXKwvNcZWESdhyBbpDT8VkVyLFqa/7a7eqpPNw==}
engines: {node: '>= 14'}
dependencies:
'@octokit/request-error': 3.0.3
'@octokit/types': 9.0.0
dev: false
/@octokit/core/4.2.0:
resolution: {integrity: sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==}
engines: {node: '>= 14'}
dependencies:
'@octokit/auth-token': 3.0.3
'@octokit/graphql': 5.0.5
'@octokit/request': 6.2.3
'@octokit/request-error': 3.0.3
'@octokit/types': 9.0.0
before-after-hook: 2.2.3
universal-user-agent: 6.0.0
transitivePeerDependencies:
- encoding
dev: false
/@octokit/endpoint/7.0.5:
resolution: {integrity: sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA==}
engines: {node: '>= 14'}
dependencies:
'@octokit/types': 9.0.0
is-plain-object: 5.0.0
universal-user-agent: 6.0.0
dev: false
/@octokit/graphql/5.0.5:
resolution: {integrity: sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==}
engines: {node: '>= 14'}
dependencies:
'@octokit/request': 6.2.3
'@octokit/types': 9.0.0
universal-user-agent: 6.0.0
transitivePeerDependencies:
- encoding
dev: false
/@octokit/oauth-app/4.2.0:
resolution: {integrity: sha512-gyGclT77RQMkVUEW3YBeAKY+LBSc5u3eC9Wn/Uwt3WhuKuu9mrV18EnNpDqmeNll+mdV02yyBROU29Tlili6gg==}
engines: {node: '>= 14'}
dependencies:
'@octokit/auth-oauth-app': 5.0.5
'@octokit/auth-oauth-user': 2.1.1
'@octokit/auth-unauthenticated': 3.0.4
'@octokit/core': 4.2.0
'@octokit/oauth-authorization-url': 5.0.0
'@octokit/oauth-methods': 2.0.5
'@types/aws-lambda': 8.10.110
fromentries: 1.3.2
universal-user-agent: 6.0.0
transitivePeerDependencies:
- encoding
dev: false
/@octokit/oauth-authorization-url/5.0.0:
resolution: {integrity: sha512-y1WhN+ERDZTh0qZ4SR+zotgsQUE1ysKnvBt1hvDRB2WRzYtVKQjn97HEPzoehh66Fj9LwNdlZh+p6TJatT0zzg==}
engines: {node: '>= 14'}
dev: false
/@octokit/oauth-methods/2.0.5:
resolution: {integrity: sha512-yQP6B5gE3axNxuM3U9KqWs/ErAQ+WLPaPgC/7EjsZsQibkf8sjdAfF8/y/EJW+Dd05XQvadX4WhQZPMnO1SE1A==}
engines: {node: '>= 14'}
dependencies:
'@octokit/oauth-authorization-url': 5.0.0
'@octokit/request': 6.2.3
'@octokit/request-error': 3.0.3
'@octokit/types': 9.0.0
btoa-lite: 1.0.0
transitivePeerDependencies:
- encoding
dev: false
/@octokit/openapi-types/16.0.0:
resolution: {integrity: sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==}
/@octokit/plugin-paginate-rest/6.0.0_@octokit+core@4.2.0:
resolution: {integrity: sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw==}
engines: {node: '>= 14'}
peerDependencies:
'@octokit/core': '>=4'
dependencies:
'@octokit/core': 4.2.0
'@octokit/types': 9.0.0
dev: false
/@octokit/request-error/3.0.3:
resolution: {integrity: sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==}
engines: {node: '>= 14'}
dependencies:
'@octokit/types': 9.0.0
deprecation: 2.3.1
once: 1.4.0
dev: false
/@octokit/types/8.1.0:
resolution: {integrity: sha512-N4nLjzkiWBqVQqljTTsCrbvHGoWdWfcCeZjbHdggw7a9HbJMnxbK8A+UWdqwR4out30JarlSa3eqKyVK0n5aBg==}
/@octokit/request/6.2.3:
resolution: {integrity: sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA==}
engines: {node: '>= 14'}
dependencies:
'@octokit/openapi-types': 14.0.0
'@octokit/endpoint': 7.0.5
'@octokit/request-error': 3.0.3
'@octokit/types': 9.0.0
is-plain-object: 5.0.0
node-fetch: 2.6.7
universal-user-agent: 6.0.0
transitivePeerDependencies:
- encoding
dev: false
/@octokit/types/9.0.0:
resolution: {integrity: sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==}
dependencies:
'@octokit/openapi-types': 16.0.0
/@octokit/webhooks-methods/3.0.2:
resolution: {integrity: sha512-Vlnv5WBscf07tyAvfDbp7pTkMZUwk7z7VwEF32x6HqI+55QRwBTcT+D7DDjZXtad/1dU9E32x0HmtDlF9VIRaQ==}
engines: {node: '>= 14'}
@@ -4526,7 +4725,7 @@ packages:
resolution: {integrity: sha512-6yGcfVbQGIkRm/vlU4Ld4r0YNOT6ly5Kp+t6R3UAT0amxGASFtZLzOJry0UQfvvidCxK5qT9jHXU0QUp6alatA==}
engines: {node: '>= 14'}
dependencies:
'@octokit/request-error': 3.0.2
'@octokit/request-error': 3.0.3
'@octokit/webhooks-methods': 3.0.2
'@octokit/webhooks-types': 6.7.0
aggregate-error: 3.1.0
@@ -5392,6 +5591,10 @@ packages:
resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==}
dev: true
/@types/aws-lambda/8.10.110:
resolution: {integrity: sha512-r6egf2Cwv/JaFTTrF9OXFVUB3j/SXTgM9BwrlbBRjWAa2Tu6GWoDoLflppAZ8uSfbUJdXvC7Br3DjuN9pQ2NUQ==}
dev: false
/@types/bcryptjs/2.4.2:
resolution: {integrity: sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==}
dev: true
@@ -5403,6 +5606,10 @@ packages:
'@types/node': 18.11.18
dev: true
/@types/btoa-lite/1.0.0:
resolution: {integrity: sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg==}
dev: false
/@types/cacheable-request/6.0.3:
resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
dependencies:
@@ -5564,6 +5771,12 @@ packages:
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
dev: true
/@types/jsonwebtoken/9.0.1:
resolution: {integrity: sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==}
dependencies:
'@types/node': 18.11.18
dev: false
/@types/keyv/3.1.4:
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
dependencies:
@@ -5574,6 +5787,10 @@ packages:
resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==}
dev: true
/@types/lru-cache/5.1.1:
resolution: {integrity: sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==}
dev: false
/@types/marked/4.0.8:
resolution: {integrity: sha512-HVNzMT5QlWCOdeuBsgXP8EZzKUf0+AXzN+sLmjvaB3ZlLqO+e4u0uXrdw9ub69wBKFs+c6/pA4r9sy6cCDvImw==}
dev: true
@@ -6575,6 +6792,10 @@ packages:
resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==}
dev: false
/before-after-hook/2.2.3:
resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
dev: false
/better-path-resolve/1.0.0:
resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==}
engines: {node: '>=4'}
@@ -6715,10 +6936,18 @@ packages:
update-browserslist-db: 1.0.10_browserslist@4.21.4
dev: true
/btoa-lite/1.0.0:
resolution: {integrity: sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==}
dev: false
/buffer-crc32/0.2.13:
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
dev: true
/buffer-equal-constant-time/1.0.1:
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
dev: false
/buffer-from/1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
@@ -7941,6 +8170,12 @@ packages:
safer-buffer: 2.1.2
dev: true
/ecdsa-sig-formatter/1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
dependencies:
safe-buffer: 5.2.1
dev: false
/editorconfig/0.15.3:
resolution: {integrity: sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==}
hasBin: true
@@ -9348,6 +9583,10 @@ packages:
resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==}
dev: true
/fromentries/1.3.2:
resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==}
dev: false
/fs-constants/1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
dev: true
@@ -10449,6 +10688,11 @@ packages:
dependencies:
isobject: 3.0.1
/is-plain-object/5.0.0:
resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
engines: {node: '>=0.10.0'}
dev: false
/is-reference/3.0.1:
resolution: {integrity: sha512-baJJdQLiYaJdvFbJqXrcGv3WU3QCzBlUcI5QhbesIm6/xPsvmO+2CDoi/GMOFBQEQm+PXkwOPrp9KK5ozZsp2w==}
dependencies:
@@ -10851,6 +11095,16 @@ packages:
resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==}
dev: false
/jsonwebtoken/9.0.0:
resolution: {integrity: sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==}
engines: {node: '>=12', npm: '>=6'}
dependencies:
jws: 3.2.2
lodash: 4.17.21
ms: 2.1.3
semver: 7.3.8
dev: false
/jsprim/2.0.2:
resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==}
engines: {'0': node >=0.6.0}
@@ -10880,6 +11134,21 @@ packages:
resolution: {integrity: sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==}
engines: {node: '>=8'}
/jwa/1.4.1:
resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==}
dependencies:
buffer-equal-constant-time: 1.0.1
ecdsa-sig-formatter: 1.0.11
safe-buffer: 5.2.1
dev: false
/jws/3.2.2:
resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==}
dependencies:
jwa: 1.4.1
safe-buffer: 5.2.1
dev: false
/keyv/3.1.0:
resolution: {integrity: sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==}
dependencies:
@@ -11166,7 +11435,6 @@ packages:
engines: {node: '>=10'}
dependencies:
yallist: 4.0.0
dev: true
/lru-cache/7.14.1:
resolution: {integrity: sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==}
@@ -13930,7 +14198,6 @@ packages:
hasBin: true
dependencies:
lru-cache: 6.0.0
dev: true
/send/0.18.0:
resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
@@ -15305,6 +15572,17 @@ packages:
unist-util-visit-parents: 5.1.1
dev: true
/universal-github-app-jwt/1.1.1:
resolution: {integrity: sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==}
dependencies:
'@types/jsonwebtoken': 9.0.1
jsonwebtoken: 9.0.0
dev: false
/universal-user-agent/6.0.0:
resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==}
dev: false
/universalify/0.1.2:
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
engines: {node: '>= 4.0.0'}
@@ -15879,7 +16157,6 @@ packages:
/yallist/4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
dev: true
/yaml/1.10.2:
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}