From 72ff236fa2072f823a47f498f2cf99d677bde1f0 Mon Sep 17 00:00:00 2001 From: Reuven Gonzales Date: Fri, 5 Apr 2024 16:38:48 -0700 Subject: [PATCH] Update external prs app to handle `ossd` PR validation (#1182) * Update external-prs app to namespace * Update gh workflows to use new namespacing --- .../workflows/external-prs-handle-comment.yml | 2 +- .../workflows/refresh-test-credentials.yml | 9 +- .github/workflows/test-deploy-clean.yml | 2 +- .github/workflows/test-deploy-owners.yml | 3 +- .github/workflows/test-deploy/action.yml | 6 +- ops/external-prs/README.md | 44 ++ ops/external-prs/package.json | 8 + ops/external-prs/src/base.ts | 9 + ops/external-prs/src/cli.ts | 429 +-------------- ops/external-prs/src/github.ts | 18 +- ops/external-prs/src/index.ts | 53 +- ops/external-prs/src/{ => oso}/deploy.ts | 6 +- ops/external-prs/src/oso/index.ts | 394 ++++++++++++++ ops/external-prs/src/ossd/compare.ts | 85 +++ ops/external-prs/src/ossd/index.ts | 183 +++++++ ops/external-prs/test.out | 5 + ops/external-prs/test_profiles.yaml | 13 + pnpm-lock.yaml | 494 +++++++++++++++++- 18 files changed, 1276 insertions(+), 487 deletions(-) create mode 100644 ops/external-prs/src/base.ts rename ops/external-prs/src/{ => oso}/deploy.ts (97%) create mode 100644 ops/external-prs/src/oso/index.ts create mode 100644 ops/external-prs/src/ossd/compare.ts create mode 100644 ops/external-prs/src/ossd/index.ts create mode 100644 ops/external-prs/test.out create mode 100644 ops/external-prs/test_profiles.yaml diff --git a/.github/workflows/external-prs-handle-comment.yml b/.github/workflows/external-prs-handle-comment.yml index c710e23dc..0c47f9f73 100644 --- a/.github/workflows/external-prs-handle-comment.yml +++ b/.github/workflows/external-prs-handle-comment.yml @@ -30,7 +30,7 @@ jobs: - name: Parse the comment to see if it's a deploy comment id: parse_comment run: | - cd ops/external-prs && pnpm tools parse-comment ${{ github.repository }} ${{ github.event.comment.id }} $GITHUB_OUTPUT + cd ops/external-prs && pnpm tools oso parse-comment --repo ${{ github.repository }} ${{ github.event.comment.id }} $GITHUB_OUTPUT # - name: Deploy if the deployment is eligible # run: | diff --git a/.github/workflows/refresh-test-credentials.yml b/.github/workflows/refresh-test-credentials.yml index 5658caee9..f567b3cde 100644 --- a/.github/workflows/refresh-test-credentials.yml +++ b/.github/workflows/refresh-test-credentials.yml @@ -8,6 +8,8 @@ # This workflow will run every 30 mins to hopefully ensure that credentials # don't expire even if this script errors. name: refresh-test-credentials +env: + PR_TOOLS_REPO: ${{ github.repository }} # For now this only runs on a schedule once a day. Once we have made some of the # plugin workflows more incremental we will run this on _every_ commit to main @@ -54,7 +56,8 @@ jobs: run: | cd ops/external-prs && bash scripts/rotate-service-account.sh oso-test-dummy@oso-pull-requests.iam.gserviceaccount.com dummy.json && - pnpm tools refresh-gcp-credentials --secret=false ${{ github.repository }} testing dummy.json GOOGLE_TEST_DUMMY_CREDENTIALS_JSON + pnpm tools oso refresh-gcp-credentials --secret=false testing dummy.json GOOGLE_TEST_DUMMY_CREDENTIALS_JSON + # These credentials are intended to be secret - name: Refresh credentials for the bigquery-admin user on the external-prs-app environment @@ -62,14 +65,14 @@ jobs: run: | cd ops/external-prs && bash scripts/rotate-service-account.sh bigquery-admin@oso-pull-requests.iam.gserviceaccount.com bigquery-admin.json && - pnpm tools refresh-gcp-credentials ${{ github.repository }} external-prs-app bigquery-admin.json GOOGLE_BQ_ADMIN_CREDENTIALS_JSON + pnpm tools oso refresh-gcp-credentials external-prs-app bigquery-admin.json GOOGLE_BQ_ADMIN_CREDENTIALS_JSON - name: Refresh credentials for the bigquery-admin user on the deploy environment shell: bash run: | cd ops/external-prs && bash scripts/rotate-service-account.sh bigquery-admin@oso-pull-requests.iam.gserviceaccount.com bigquery-admin.json && - pnpm tools refresh-gcp-credentials ${{ github.repository }} deploy bigquery-admin.json GOOGLE_BQ_ADMIN_CREDENTIALS_JSON + pnpm tools refresh-gcp-credentials oso deploy bigquery-admin.json GOOGLE_BQ_ADMIN_CREDENTIALS_JSON rebuild-docker-public-vars: name: rebuild-docker-public-vars diff --git a/.github/workflows/test-deploy-clean.yml b/.github/workflows/test-deploy-clean.yml index 97a5020b0..e77a112be 100644 --- a/.github/workflows/test-deploy-clean.yml +++ b/.github/workflows/test-deploy-clean.yml @@ -39,5 +39,5 @@ jobs: - name: Clean up datasets that are older than 1 day run: | - cd ops/external-ops && pnpm tools test-deploy clean --project-id ${{ vars.GOOGLE_PROJECT_ID }} opensource-observer/oso 86400 + cd ops/external-ops && pnpm tools oso test-deploy clean --project-id ${{ vars.GOOGLE_PROJECT_ID }} --repo opensource-observer/oso 86400 \ No newline at end of file diff --git a/.github/workflows/test-deploy-owners.yml b/.github/workflows/test-deploy-owners.yml index 907ca2d40..0ad739868 100644 --- a/.github/workflows/test-deploy-owners.yml +++ b/.github/workflows/test-deploy-owners.yml @@ -11,6 +11,7 @@ env: # should not be set to a legitimate value for testing. This will use up API # quota otherwise DUNE_API_KEY: "none" + PR_TOOLS_REPO: ${{ github.repository }} # Trigger the workflow when: on: @@ -40,7 +41,7 @@ jobs: - name: Initialize check run: | cd ops/external-prs && - pnpm tools initialize-check ${{ github.repository }} ${{ github.event.pull_request.head.sha }} ${{ github.event.pull_request.user.login }} + pnpm tools oso initialize-check ${{ github.event.pull_request.head.sha }} ${{ github.event.pull_request.user.login }} - name: Author association debug run: | diff --git a/.github/workflows/test-deploy/action.yml b/.github/workflows/test-deploy/action.yml index 2afac462b..66a5df73c 100644 --- a/.github/workflows/test-deploy/action.yml +++ b/.github/workflows/test-deploy/action.yml @@ -43,7 +43,7 @@ runs: # - name: Evaluate if this is eligible for deployment # id: is_eligible # run: | - # cd ops/external-prs && pnpm tools test-deploy is-eligible $GITHUB_OUTPUT + # cd ops/external-prs && pnpm tools oso test-deploy is-eligible $GITHUB_OUTPUT - name: checkout the PR uses: actions/checkout@v3 @@ -72,6 +72,8 @@ runs: shell: bash run: | mkdir -p $HOME/.dbt && - cd ops/external-prs && pnpm tools test-deploy --project-id ${{ inputs.google_project_id }} setup ${{ github.repository }} ${{ inputs.pr }} ${{ inputs.sha }} $HOME/.dbt/profiles.yml ${{ inputs.gcp_service_account_path }} pr-clone + cd ops/external-prs && pnpm tools oso test-deploy --project-id ${{ inputs.google_project_id }} setup ${{ inputs.pr }} ${{ inputs.sha }} $HOME/.dbt/profiles.yml ${{ inputs.gcp_service_account_path }} pr-clone + env: + PR_TOOLS_REPO: ${{ github.repository }} \ No newline at end of file diff --git a/ops/external-prs/README.md b/ops/external-prs/README.md index 9d98bd2a3..50455fb7b 100644 --- a/ops/external-prs/README.md +++ b/ops/external-prs/README.md @@ -3,3 +3,47 @@ A Github App that allows us to accept external PRs by enabling PR checks by an out of band process. We need this because some of the checks require _some_ form of authentication. + +## Usage + +This is generally only supposed to be used on Github Actions. However, if you're +developing this locally it's useful to know the available commands. + +### General commands + +```bash +# Initialize a check with a given check name +# If the user has write access then it automatically is listed as "queued" +# If the user does not have write access then it automatically fails and waits for approval +pnpm external-prs initialize-check {sha} {login} {check-name} +``` + +### OSO Specific + +```bash +# Handle oso comments +pnpm external-prs oso parse-comment {comment} {output} + +# Refresh gcp credentials for the test deployment infrastructure +pnpm external-prs oso refresh-gcp-credentials {environment} {creds-path} {name} + +# Test deployment sub commands +pnpm external-prs oso test-deploy --help + +# Test deployment setup +pnpm external-prs oso test-deploy setup {pr} {sha} {profile-path} {service-account-path} {checkout-path} + +# Test deployment teardown +pnpm external-prs oso test-deploy teardown {pr} + +# Test deployment clean +pnpm external-prs oso test-deploy clean {ttl-seconds} +``` + +### OSS-Directory Specific + +```bash +# Handle PR validations +pnpm external-prs ossd validate-prs {pr} {sha} {main-path} {pr-path} + +``` diff --git a/ops/external-prs/package.json b/ops/external-prs/package.json index 76b7d7c12..b2a16cdb5 100644 --- a/ops/external-prs/package.json +++ b/ops/external-prs/package.json @@ -5,6 +5,9 @@ "author": "Kariba Labs", "license": "Apache-2.0", "private": true, + "bin": { + "external-prs": "./dist/src/cli.js" + }, "main": "./dist/src/index.js", "types": "./dist/src/index.d.ts", "type": "module", @@ -31,12 +34,17 @@ }, "dependencies": { "@google-cloud/bigquery": "^7.5.1", + "@types/columnify": "^1.5.4", "@types/libsodium-wrappers": "^0.7.13", "@types/yargs": "^17.0.32", "chalk": "^5.3.0", + "columnify": "^1.6.0", "dayjs": "^1.11.10", + "duckdb": "^0.10.1", "libsodium-wrappers": "^0.7.13", "octokit": "^3.1.0", + "oss-directory": "^0.0.10", + "tmp-promise": "^3.0.3", "ts-dedent": "^2.2.0", "winston": "^3.11.0", "yaml": "^2.3.1", diff --git a/ops/external-prs/src/base.ts b/ops/external-prs/src/base.ts new file mode 100644 index 000000000..b07d8b00a --- /dev/null +++ b/ops/external-prs/src/base.ts @@ -0,0 +1,9 @@ +import { Repo } from "./github.js"; +import { App } from "octokit"; + +export interface BaseArgs { + githubAppPrivateKey: string; + githubAppId: string; + repo: Repo; + app: App; +} diff --git a/ops/external-prs/src/cli.ts b/ops/external-prs/src/cli.ts index 2eee1a745..5d2f10c5b 100644 --- a/ops/external-prs/src/cli.ts +++ b/ops/external-prs/src/cli.ts @@ -1,80 +1,28 @@ import yargs from "yargs"; -import { Argv, ArgumentsCamelCase } from "yargs"; +import { ArgumentsCamelCase } from "yargs"; import { hideBin } from "yargs/helpers"; -import { App, Octokit } from "octokit"; -import * as fsPromise from "fs/promises"; -import _sodium from "libsodium-wrappers"; -import { BigQuery } from "@google-cloud/bigquery"; -import { dedent } from "ts-dedent"; -import { URL } from "url"; +import { App } from "octokit"; import { logger } from "./utils/logger.js"; import { handleError } from "./utils/error.js"; import dotenv from "dotenv"; import { CheckStatus, setCheckStatus, CheckConclusion } from "./checks.js"; -import { Repo, getOctokitFor } from "./github.js"; -import { PRTestDeployCoordinator } from "./deploy.js"; +import { Repo, getOctokitFor, getRepoPermissions } from "./github.js"; +import { osoSubcommands } from "./oso/index.js"; +import { BaseArgs } from "./base.js"; +import { ossdSubcommands } from "./ossd/index.js"; dotenv.config(); -interface BaseArgs { - githubAppPrivateKey: string; - githubAppId: string; - repo: Repo; - app: App; -} - type BeforeClientArgs = ArgumentsCamelCase<{ "github-app-private-key": unknown; "github-app-id": unknown; }>; -interface ParseCommentArgs extends BaseArgs { - comment: number; - output: string; - login: string; -} - interface InitializePRCheck extends BaseArgs { sha: string; login: string; -} - -interface RefreshGCPCredentials extends BaseArgs { - environment: string; - credsPath: string; - secret: boolean; - name: string; -} - -interface TestDeployArgs extends BaseArgs { - coordinator: PRTestDeployCoordinator; -} - -interface TestDeploySetupArgs extends TestDeployArgs { - pr: number; - sha: string; - profilePath: string; - serviceAccountPath: string; - projectId: string; - checkoutPath: string; -} - -interface TestDeployPeriodicCleaningArgs extends TestDeployArgs { - ttlSeconds: number; -} - -interface TestDeployTeardownArgs extends TestDeployArgs { - pr: number; -} - -async function getRepoPermissions(octo: Octokit, repo: Repo, login: string) { - const res = await octo.rest.repos.getCollaboratorPermissionLevel({ - owner: repo.owner, - repo: repo.name, - username: login, - }); - return res.data.permission; + checkName: string; } async function initializePrCheck(args: InitializePRCheck) { @@ -96,7 +44,7 @@ async function initializePrCheck(args: InitializePRCheck) { // If this user has write then we can show this as being queued if (["admin", "write"].indexOf(permissions) !== -1) { await setCheckStatus(octo, args.repo.owner, args.repo.name, { - name: "test-deploy", + name: args.checkName, head_sha: args.sha, status: CheckStatus.Queued, output: { @@ -107,322 +55,24 @@ async function initializePrCheck(args: InitializePRCheck) { } else { // The user is not a writer. Show that this needs to be approved. await setCheckStatus(octo, args.repo.owner, args.repo.name, { - name: "test-deploy", + name: args.checkName, head_sha: args.sha, status: CheckStatus.Completed, conclusion: CheckConclusion.ActionRequired, output: { - title: "Deployment approval required to deploy", - summary: - "Deployment pproval required to deploy. A valid user must comment `/test-deploy ${sha}` on the PR.", + title: `Approval required for ${args.checkName}`, + summary: `Approval required for the ${args.checkName} check.`, }, }); } } -async function parseDeployComment(args: ParseCommentArgs) { - logger.info({ - message: "checking for a /deploy-test message", - repo: args.repo, - commentId: args.comment, - }); - - const app = args.app; - - const octo = await getOctokitFor(app, args.repo); - if (!octo) { - throw new Error("No repo found"); - } - - const noDeploy = async () => { - const output = dedent` - deploy=false - `; - - // Write the deploy commit sha to the - await fsPromise.writeFile(args.output, output); - }; - - const comment = await octo.rest.issues.getComment({ - repo: args.repo.name, - owner: args.repo.owner, - comment_id: args.comment, - }); - logger.info("gathered the comment", { - commentId: comment.data.id, - authorAssosication: comment.data.author_association, - author: comment.data.user?.login, - }); - - const login = comment.data.user?.login; - if (!login) { - logger.error("No user for this comment (which is unexpected)"); - throw new Error("No user for the comment"); - } - - // Get the author's permissions - const permissions = await getRepoPermissions(octo, args.repo, login); - - logger.info({ - message: "gathered the commentor's permissions", - login: login, - permissions: permissions, - }); - // If this user doesn't have permissions then we don't deploy - if (["admin", "write"].indexOf(permissions) === -1) { - return await noDeploy(); - } - - const body = comment.data.body || ""; - const match = body.match(/^\/([a-z-]+)\s+([0-9a-f]{6,40})$/); - if (!match) { - logger.error("command not found"); - return await noDeploy(); - } - if (match.length < 2) { - logger.error({ - message: "proper command not found", - matches: match, - }); - return await noDeploy(); - } - const command = match[1]; - const sha = match[2]; - - if (["test-deploy", "deploy-test"].indexOf(command) === -1) { - logger.error({ - message: "invalid command", - command: command, - }); - return await noDeploy(); - } - - logger.debug({ - message: "command found", - command: command, - }); - - const issueUrl = comment.data.issue_url; - const url = new URL(issueUrl); - const issueNumber = parseInt(url.pathname.split("/").slice(-1)[0]); - - const issue = await octo.rest.issues.get({ - issue_number: issueNumber, - repo: args.repo.name, - owner: args.repo.owner, - }); - - logger.debug({ - message: "deployment configuration", - deploy: true, - sha: sha, - pr: issueNumber, - issueAuthor: issue.data.user?.login, - commentAuthor: comment.data.user?.login, - }); - - // Output for GITHUB_OUTPUT for now. - const output = dedent` - deploy=true - sha=${sha} - pr=${issueNumber} - issue_author=${issue.data.user?.login} - comment_author=${comment.data.user?.login} - `; - - // Write the deploy commit sha to the - await fsPromise.writeFile(args.output, output); - - // Update the check for this PR - await setCheckStatus(octo, args.repo.owner, args.repo.name, { - name: "test-deploy", - head_sha: sha, - status: CheckStatus.Queued, - output: { - title: "Test Deployment", - summary: "Queued for deployment", - }, - }); -} - -async function fileToBase64(filePath: string): Promise { - try { - const fileBuffer = await fsPromise.readFile(filePath); - const base64String = fileBuffer.toString("base64"); - return base64String; - } catch (error) { - logger.error("Error reading file:", error); - throw error; - } -} - -async function refreshCredentials(args: RefreshGCPCredentials) { - logger.info({ - message: "setting up credentials", - environment: args.environment, - name: args.name, - }); - - const app = args.app; - - const octo = await getOctokitFor(app, args.repo); - if (!octo) { - throw new Error("No repo found"); - } - - const repo = await octo.rest.repos.get({ - repo: args.repo.name, - owner: args.repo.owner, - }); - - const creds = await fileToBase64(args.credsPath); - - if (args.secret) { - // The github secret must use libsodium's crypto_box_seal for the - // `encrypted_value` - await _sodium.ready; - - const pkey = await octo.rest.actions.getEnvironmentPublicKey({ - repository_id: repo.data.id, - environment_name: args.environment, - }); - - const messageBytes = Buffer.from(creds); - const keyBytes = Buffer.from(pkey.data.key, "base64"); - const encryptedBytes = _sodium.crypto_box_seal(messageBytes, keyBytes); - const ciphertext = Buffer.from(encryptedBytes).toString("base64"); - - await octo.rest.actions.createOrUpdateEnvironmentSecret({ - repository_id: repo.data.id, - environment_name: args.environment, - secret_name: args.name, - encrypted_value: ciphertext, - key_id: pkey.data.key_id, - }); - } else { - try { - const currentVar = await octo.rest.actions.getEnvironmentVariable({ - repository_id: repo.data.id, - environment_name: args.environment, - name: args.name, - }); - if (currentVar) { - await octo.rest.actions.deleteEnvironmentVariable({ - repository_id: repo.data.id, - environment_name: args.environment, - name: args.name, - }); - } - } catch (e) { - logger.info("no existing var found"); - } - await octo.rest.actions.createEnvironmentVariable({ - repository_id: repo.data.id, - environment_name: args.environment, - name: args.name, - value: creds, - }); - } -} - -async function testDeploySetup(args: TestDeploySetupArgs) { - return args.coordinator.setup( - args.pr, - args.sha, - args.profilePath, - args.serviceAccountPath, - args.checkoutPath, - ); -} - -async function testDeployTeardown(args: TestDeployTeardownArgs) { - return args.coordinator.teardown(args.pr); -} - -async function testDeployPeriodicCleaning( - args: TestDeployPeriodicCleaningArgs, -) { - return args.coordinator.clean(args.ttlSeconds); -} - -function testDeployGroup(group: Argv) { - group - .option("project-id", { - description: "The google project id to deploy into", - type: "string", - demandOption: true, - }) - .middleware(async (args: ArgumentsCamelCase) => { - logger.info({ - message: "settings up Pull Request Test Deploy Coordinator", - }); - - const projectId = args.projectId as string; - const bq = new BigQuery({ projectId: projectId }); - const app = args.app as App; - const repo = args.repo as Repo; - - const octo = await getOctokitFor(app, repo); - - args.coordinator = new PRTestDeployCoordinator( - repo, - app, - octo as Octokit, - bq, - projectId, - ); - }) - .command( - "setup ", - "subcommand for a setting up a test deployment", - (yags) => { - yags.positional("pr", { - description: "The PR", - }); - yags.positional("sha", { - description: "the sha to deploy", - }); - yags.positional("profile-path", { - description: "the profile path to write to", - }); - yags.positional("service-account-path", { - description: "the profile path to write to", - }); - yags.positional("checkout-path", { - description: "the path to the checked out code", - }); - }, - (args) => handleError(testDeploySetup(args)), - ) - .command( - "teardown ", - "subcommand for a setting up a test deployment", - (yags) => { - yags.positional("pr", { - description: "The PR", - }); - }, - (args) => handleError(testDeployTeardown(args)), - ) - .command( - "clean ", - "subcommand for cleaning test deployments", - (yags) => { - yags.positional("ttl-seconds", { - type: "number", - description: "TTL in seconds", - }); - }, - (args) => handleError(testDeployPeriodicCleaning(args)), - ) - .demandCommand(); -} - const cli = yargs(hideBin(process.argv)) .env("PR_TOOLS") - .positional("repo", { + .option("repo", { type: "string", description: "The repo in the style owner/repo_name", + demandOption: true, }) .coerce("repo", (v: string): Repo => { const splitName = v.split("/"); @@ -456,8 +106,14 @@ const cli = yargs(hideBin(process.argv)) const { data } = await app.octokit.request("/app"); logger.debug(`Authenticated as ${data.name}`); }) + .command("oso", "oso related commands", (yags) => { + osoSubcommands(yags); + }) + .command("ossd", "oss-directory related commands", (yags) => { + ossdSubcommands(yags); + }) .command( - "initialize-check ", + "initialize-check ", "subcommand for initializing a check", (yags) => { yags.positional("sha", { @@ -468,51 +124,12 @@ const cli = yargs(hideBin(process.argv)) type: "string", description: "The login for the PR", }); - }, - (args) => handleError(initializePrCheck(args)), - ) - .command( - "parse-comment ", - "subcommand for parsing a deploy comment", - (yags) => { - yags.positional("comment", { - type: "number", - description: "Comment ID", - }); - yags.positional("output", { + yags.positional("check-name", { type: "string", - description: "The output file", + description: "The check-name to use", }); }, - (args) => handleError(parseDeployComment(args)), - ) - .command( - "refresh-gcp-credentials ", - "Refresh creds", - (yags) => { - yags.positional("environment", { - type: "string", - }); - yags.positional("creds-path", { - type: "string", - }); - yags.positional("name", { - type: "string", - }); - yags.option("secret", { - type: "boolean", - default: true, - }); - yags.boolean("secret"); - }, - (args) => handleError(refreshCredentials(args)), - ) - .command( - "test-deploy", - "Test deployment commands", - (yags) => { - testDeployGroup(yags); - }, + (args) => handleError(initializePrCheck(args)), ) .demandCommand() .strict() diff --git a/ops/external-prs/src/github.ts b/ops/external-prs/src/github.ts index 4f3e92ff8..5426d9260 100644 --- a/ops/external-prs/src/github.ts +++ b/ops/external-prs/src/github.ts @@ -5,10 +5,7 @@ export interface Repo { owner: string; } -export async function getOctokitFor( - app: App, - repo: Repo, -): Promise { +export async function getOctokitFor(app: App, repo: Repo): Promise { for await (const { installation } of app.eachInstallation.iterator()) { for await (const { octokit, repository } of app.eachRepository.iterator({ installationId: installation.id, @@ -20,3 +17,16 @@ export async function getOctokitFor( } throw new Error("invalid repo for this github app"); } + +export async function getRepoPermissions( + octo: Octokit, + repo: Repo, + login: string, +) { + const res = await octo.rest.repos.getCollaboratorPermissionLevel({ + owner: repo.owner, + repo: repo.name, + username: login, + }); + return res.data.permission; +} diff --git a/ops/external-prs/src/index.ts b/ops/external-prs/src/index.ts index c68853374..992da2942 100644 --- a/ops/external-prs/src/index.ts +++ b/ops/external-prs/src/index.ts @@ -1,52 +1 @@ -import { App } from "octokit"; -import dotenv from "dotenv"; - -dotenv.config(); - -async function main() { - const APP_TO_CHECK = process.env.APP_TO_CHECK; - const SHA_TO_CHECK = process.env.SHA_TO_CHECK; - - const buf = Buffer.from(process.env.APP_PRIVATE_KEY!, "base64"); // Ta-da - - const app = new App({ - appId: process.env.APP_ID!, - privateKey: buf.toString("utf-8"), - }); - - const { data } = await app.octokit.request("/app"); - console.log(`Authenticated as ${data.name}`); - - for await (const { installation } of app.eachInstallation.iterator()) { - for await (const { octokit, repository } of app.eachRepository.iterator({ - installationId: installation.id, - })) { - console.log(repository.name); - if (repository.name !== APP_TO_CHECK) { - continue; - } - const resp = await octokit.request( - "POST /repos/{owner}/{repo}/check-runs", - { - owner: repository.owner.login, - repo: repository.name, - data: { - name: "test-deployment2", - head_sha: SHA_TO_CHECK, - status: "completed", - conclusion: "success", - output: { - title: "test-deployment2", - summary: "This is some summary", - }, - }, - }, - ); - console.log(resp); - } - } -} - -main().catch((e) => { - console.log(e); -}); +export * from "./checks.js"; diff --git a/ops/external-prs/src/deploy.ts b/ops/external-prs/src/oso/deploy.ts similarity index 97% rename from ops/external-prs/src/deploy.ts rename to ops/external-prs/src/oso/deploy.ts index 989850b02..ff6aedbf9 100644 --- a/ops/external-prs/src/deploy.ts +++ b/ops/external-prs/src/oso/deploy.ts @@ -6,10 +6,10 @@ import { exec } from "child_process"; import * as util from "util"; import * as path from "path"; -import { Repo } from "./github.js"; -import { logger } from "./utils/logger.js"; +import { Repo } from "../github.js"; +import { logger } from "../utils/logger.js"; import { App, Octokit } from "octokit"; -import { CheckConclusion, CheckStatus, setCheckStatus } from "./checks.js"; +import { CheckConclusion, CheckStatus, setCheckStatus } from "../checks.js"; const execPromise = util.promisify(exec); diff --git a/ops/external-prs/src/oso/index.ts b/ops/external-prs/src/oso/index.ts new file mode 100644 index 000000000..45a638c0f --- /dev/null +++ b/ops/external-prs/src/oso/index.ts @@ -0,0 +1,394 @@ +import { Argv, ArgumentsCamelCase } from "yargs"; +import { App, Octokit } from "octokit"; +import * as fsPromise from "fs/promises"; +import _sodium from "libsodium-wrappers"; +import { BigQuery } from "@google-cloud/bigquery"; +import { dedent } from "ts-dedent"; +import { URL } from "url"; + +import { logger } from "../utils/logger.js"; +import { handleError } from "../utils/error.js"; +import { CheckStatus, setCheckStatus } from "../checks.js"; +import { Repo, getOctokitFor, getRepoPermissions } from "../github.js"; +import { PRTestDeployCoordinator } from "./deploy.js"; +import { BaseArgs } from "../base.js"; + +interface ParseCommentArgs extends BaseArgs { + comment: number; + output: string; + login: string; +} + +interface RefreshGCPCredentials extends BaseArgs { + environment: string; + credsPath: string; + secret: boolean; + name: string; +} + +interface TestDeployArgs extends BaseArgs { + coordinator: PRTestDeployCoordinator; +} + +interface TestDeploySetupArgs extends TestDeployArgs { + pr: number; + sha: string; + profilePath: string; + serviceAccountPath: string; + projectId: string; + checkoutPath: string; +} + +interface TestDeployPeriodicCleaningArgs extends TestDeployArgs { + ttlSeconds: number; +} + +interface TestDeployTeardownArgs extends TestDeployArgs { + pr: number; +} + +export function osoSubcommands(yargs: Argv) { + yargs + .command( + "parse-comment ", + "subcommand for parsing a deploy comment", + (yags) => { + yags.positional("comment", { + type: "number", + description: "Comment ID", + }); + yags.positional("output", { + type: "string", + description: "The output file", + }); + }, + (args) => handleError(parseDeployComment(args)), + ) + .command( + "refresh-gcp-credentials ", + "Refresh creds", + (yags) => { + yags.positional("environment", { + type: "string", + }); + yags.positional("creds-path", { + type: "string", + }); + yags.positional("name", { + type: "string", + }); + yags.option("secret", { + type: "boolean", + default: true, + }); + yags.boolean("secret"); + }, + (args) => handleError(refreshCredentials(args)), + ) + .command( + "test-deploy", + "Test deployment commands", + (yags) => { + testDeployGroup(yags); + }, + ) + .demandCommand(); +} + +async function parseDeployComment(args: ParseCommentArgs) { + logger.info({ + message: "checking for a /deploy-test message", + repo: args.repo, + commentId: args.comment, + }); + + const app = args.app; + + const octo = await getOctokitFor(app, args.repo); + if (!octo) { + throw new Error("No repo found"); + } + + const noDeploy = async () => { + const output = dedent` + deploy=false + `; + + // Write the deploy commit sha to the + await fsPromise.writeFile(args.output, output); + }; + + const comment = await octo.rest.issues.getComment({ + repo: args.repo.name, + owner: args.repo.owner, + comment_id: args.comment, + }); + logger.info("gathered the comment", { + commentId: comment.data.id, + authorAssosication: comment.data.author_association, + author: comment.data.user?.login, + }); + + const login = comment.data.user?.login; + if (!login) { + logger.error("No user for this comment (which is unexpected)"); + throw new Error("No user for the comment"); + } + + // Get the author's permissions + const permissions = await getRepoPermissions(octo, args.repo, login); + + logger.info({ + message: "gathered the commentor's permissions", + login: login, + permissions: permissions, + }); + // If this user doesn't have permissions then we don't deploy + if (["admin", "write"].indexOf(permissions) === -1) { + return await noDeploy(); + } + + const body = comment.data.body || ""; + const match = body.match(/^\/([a-z-]+)\s+([0-9a-f]{6,40})$/); + if (!match) { + logger.error("command not found"); + return await noDeploy(); + } + if (match.length < 2) { + logger.error({ + message: "proper command not found", + matches: match, + }); + return await noDeploy(); + } + const command = match[1]; + const sha = match[2]; + + if (["test-deploy", "deploy-test"].indexOf(command) === -1) { + logger.error({ + message: "invalid command", + command: command, + }); + return await noDeploy(); + } + + logger.debug({ + message: "command found", + command: command, + }); + + const issueUrl = comment.data.issue_url; + const url = new URL(issueUrl); + const issueNumber = parseInt(url.pathname.split("/").slice(-1)[0]); + + const issue = await octo.rest.issues.get({ + issue_number: issueNumber, + repo: args.repo.name, + owner: args.repo.owner, + }); + + logger.debug({ + message: "deployment configuration", + deploy: true, + sha: sha, + pr: issueNumber, + issueAuthor: issue.data.user?.login, + commentAuthor: comment.data.user?.login, + }); + + // Output for GITHUB_OUTPUT for now. + const output = dedent` + deploy=true + sha=${sha} + pr=${issueNumber} + issue_author=${issue.data.user?.login} + comment_author=${comment.data.user?.login} + `; + + // Write the deploy commit sha to the + await fsPromise.writeFile(args.output, output); + + // Update the check for this PR + await setCheckStatus(octo, args.repo.owner, args.repo.name, { + name: "test-deploy", + head_sha: sha, + status: CheckStatus.Queued, + output: { + title: "Test Deployment", + summary: "Queued for deployment", + }, + }); +} + +async function fileToBase64(filePath: string): Promise { + try { + const fileBuffer = await fsPromise.readFile(filePath); + const base64String = fileBuffer.toString("base64"); + return base64String; + } catch (error) { + logger.error("Error reading file:", error); + throw error; + } +} + +async function refreshCredentials(args: RefreshGCPCredentials) { + logger.info({ + message: "setting up credentials", + environment: args.environment, + name: args.name, + }); + + const app = args.app; + + const octo = await getOctokitFor(app, args.repo); + if (!octo) { + throw new Error("No repo found"); + } + + const repo = await octo.rest.repos.get({ + repo: args.repo.name, + owner: args.repo.owner, + }); + + const creds = await fileToBase64(args.credsPath); + + if (args.secret) { + // The github secret must use libsodium's crypto_box_seal for the + // `encrypted_value` + await _sodium.ready; + + const pkey = await octo.rest.actions.getEnvironmentPublicKey({ + repository_id: repo.data.id, + environment_name: args.environment, + }); + + const messageBytes = Buffer.from(creds); + const keyBytes = Buffer.from(pkey.data.key, "base64"); + const encryptedBytes = _sodium.crypto_box_seal(messageBytes, keyBytes); + const ciphertext = Buffer.from(encryptedBytes).toString("base64"); + + await octo.rest.actions.createOrUpdateEnvironmentSecret({ + repository_id: repo.data.id, + environment_name: args.environment, + secret_name: args.name, + encrypted_value: ciphertext, + key_id: pkey.data.key_id, + }); + } else { + try { + const currentVar = await octo.rest.actions.getEnvironmentVariable({ + repository_id: repo.data.id, + environment_name: args.environment, + name: args.name, + }); + if (currentVar) { + await octo.rest.actions.deleteEnvironmentVariable({ + repository_id: repo.data.id, + environment_name: args.environment, + name: args.name, + }); + } + } catch (e) { + logger.info("no existing var found"); + } + await octo.rest.actions.createEnvironmentVariable({ + repository_id: repo.data.id, + environment_name: args.environment, + name: args.name, + value: creds, + }); + } +} + +async function testDeploySetup(args: TestDeploySetupArgs) { + return args.coordinator.setup( + args.pr, + args.sha, + args.profilePath, + args.serviceAccountPath, + args.checkoutPath, + ); +} + +async function testDeployTeardown(args: TestDeployTeardownArgs) { + return args.coordinator.teardown(args.pr); +} + +async function testDeployPeriodicCleaning( + args: TestDeployPeriodicCleaningArgs, +) { + return args.coordinator.clean(args.ttlSeconds); +} + +function testDeployGroup(group: Argv) { + group + .option("project-id", { + description: "The google project id to deploy into", + type: "string", + demandOption: true, + }) + .middleware(async (args: ArgumentsCamelCase) => { + logger.info({ + message: "settings up Pull Request Test Deploy Coordinator", + }); + + const projectId = args.projectId as string; + const bq = new BigQuery({ projectId: projectId }); + const app = args.app as App; + const repo = args.repo as Repo; + + const octo = await getOctokitFor(app, repo); + + args.coordinator = new PRTestDeployCoordinator( + repo, + app, + octo as Octokit, + bq, + projectId, + ); + }) + .command( + "setup ", + "subcommand for a setting up a test deployment", + (yags) => { + yags.positional("pr", { + description: "The PR", + }); + yags.positional("sha", { + description: "the sha to deploy", + }); + yags.positional("profile-path", { + description: "the profile path to write to", + }); + yags.positional("service-account-path", { + description: "the profile path to write to", + }); + yags.positional("checkout-path", { + description: "the path to the checked out code", + }); + }, + (args) => handleError(testDeploySetup(args)), + ) + .command( + "teardown ", + "subcommand for a setting up a test deployment", + (yags) => { + yags.positional("pr", { + description: "The PR", + }); + }, + (args) => handleError(testDeployTeardown(args)), + ) + .command( + "clean ", + "subcommand for cleaning test deployments", + (yags) => { + yags.positional("ttl-seconds", { + type: "number", + description: "TTL in seconds", + }); + }, + (args) => handleError(testDeployPeriodicCleaning(args)), + ) + .demandCommand(); +} diff --git a/ops/external-prs/src/ossd/compare.ts b/ops/external-prs/src/ossd/compare.ts new file mode 100644 index 000000000..d26e3c25f --- /dev/null +++ b/ops/external-prs/src/ossd/compare.ts @@ -0,0 +1,85 @@ +// import * as duckdb from "duckdb"; +// import * as fs from "fs"; +// import * as path from "path"; + +// /** +// * Creates two tables that are comparable via duckdb through a variety of +// * queries. This allows us to do deeper comparative analysis of a given pull +// * request +// */ +// class DirectoryCompare { +// private mainDataPath: string; +// private prDataPath: string; +// private workPath: string; + +// constructor(workPath: string, mainDataPath: string, prDataPath: string) { +// this.mainDataPath = mainDataPath; +// this.prDataPath = prDataPath; +// this.workPath = workPath; +// } + +// // A context +// async withComparison(compareFn: (db: duckdb.Database) => Promise): Promise { +// return await tmp.withDir( +// async (t) => { +// // Load the data +// const main = await loadData(this.mainDataPath); +// const pr = await loadData(this.prDataPath); +// const db = new duckdb.Database(':memory:'); + +// const tablesToCompare: { [table: string]: Project[] | Collection[] } = { +// "main_projects": main.projects, +// "main_collections": main.collections, +// "pr_projects": pr.projects, +// "pr_collections": pr.collections, +// } + +// for (const table in tablesToCompare) { +// // Dump the data into the work path as JSONL files +// const dumpPath = path.resolve(path.join(t.path, `${table}.json`)); +// await this.jsonlExport(dumpPath, tablesToCompare[table]); + +// // Load that data into duckdb +// db.run(` +// CREATE TABLE ${table} AS +// SELECT * +// FROM read_json_auto('${dumpPath}'); +// `); +// } + +// // Run the comparison +// return await compareFn(db); +// }, +// { unsafeCleanup: true }, +// ) +// } + +// jsonlExport(path: string, arr: Array): Promise { +// return new Promise((resolve, reject) => { +// const stream = fs.createWriteStream(path, 'utf-8'); +// for (const item of arr) { +// stream.write(JSON.stringify(item)); +// stream.write('\n'); +// } +// stream.close((err) => { +// if (err) { +// return reject(err); +// } +// return resolve(); +// }) +// }) +// } +// } + +// async function updateSummary(db: duckdb.Database) { +// // New Collections +// db.run() + +// // Removed Collections +// // New Projects +// // Removed Projects +// // New Artifacts +// // Removed Artifacts +// // New Artifact Associations +// // Removed Artifact Associations +// } diff --git a/ops/external-prs/src/ossd/index.ts b/ops/external-prs/src/ossd/index.ts new file mode 100644 index 000000000..b7ebbb22b --- /dev/null +++ b/ops/external-prs/src/ossd/index.ts @@ -0,0 +1,183 @@ +import tmp from "tmp-promise"; +import { Argv } from "yargs"; +import { handleError } from "../utils/error.js"; +import { logger } from "../utils/logger.js"; +import { BaseArgs } from "../base.js"; +import { loadData, Project, Collection } from "oss-directory"; +import duckdb from "duckdb"; +import * as util from "util"; +import * as fs from "fs"; +import * as path from "path"; +import * as repl from "repl"; +import columnify from "columnify"; + +// async withComparison(compareFn: (db: duckdb.Database) => Promise): Promise { +// return await tmp.withDir( +// async (t) => { +// // Load the data +// const main = await loadData(this.mainDataPath); +// const pr = await loadData(this.prDataPath); +// const db = new duckdb.Database(':memory:'); + +// const tablesToCompare: { [table: string]: Project[] | Collection[] } = { +// "main_projects": main.projects, +// "main_collections": main.collections, +// "pr_projects": pr.projects, +// "pr_collections": pr.collections, +// } + +// for (const table in tablesToCompare) { +// // Dump the data into the work path as JSONL files +// const dumpPath = path.resolve(path.join(t.path, `${table}.json`)); +// await this.jsonlExport(dumpPath, tablesToCompare[table]); + +// // Load that data into duckdb +// db.run(` +// CREATE TABLE ${table} AS +// SELECT * +// FROM read_json_auto('${dumpPath}'); +// `); +// } + +// // Run the comparison +// return await compareFn(db); +// }, +// { unsafeCleanup: true }, +// ) +// } + +function jsonlExport(path: string, arr: Array): Promise { + return new Promise((resolve, reject) => { + const stream = fs.createWriteStream(path, "utf-8"); + for (const item of arr) { + stream.write(JSON.stringify(item)); + stream.write("\n"); + } + stream.close((err) => { + if (err) { + return reject(err); + } + return resolve(); + }); + }); +} + +export function ossdSubcommands(yargs: Argv) { + yargs.command( + "validate-pr ", + "validate changes for an OSSD PR", + (yags) => { + yags.positional("pr", { + type: "number", + description: "pr number", + }); + yags.positional("sha", { + type: "string", + description: "The sha of the pull request", + }); + yags.positional("main-path", { + type: "string", + description: "The path to the main branch checkout", + }); + yags.positional("path-path", { + type: "string", + description: "The path to the pr checkout", + }); + yags.option("repl", { + type: "boolean", + description: "Start a repl for exploration on the data", + default: false, + }); + yags.boolean("repl"); + }, + (args) => handleError(validatePR(args)), + ); +} + +interface ValidatePRArgs extends BaseArgs { + pr: string; + sha: string; + mainPath: string; + prPath: string; + repl: boolean; +} + +async function validatePR(args: ValidatePRArgs) { + logger.info({ + message: "validating the pull request", + repo: args.repo, + sha: args.sha, + pr: args.pr, + }); + + //const app = args.app; + + const main = await loadData(args.mainPath); + const pr = await loadData(args.prPath); + + const db = new duckdb.Database(":memory:"); + + const tablesToCompare: { [table: string]: Project[] | Collection[] } = { + main_projects: main.projects, + main_collections: main.collections, + pr_projects: pr.projects, + pr_collections: pr.collections, + }; + + const dbAll = util.promisify(db.all.bind(db)); + return tmp.withDir( + async (t) => { + for (const table in tablesToCompare) { + const dumpPath = path.resolve(path.join(t.path, `${table}.json`)); + await jsonlExport(dumpPath, tablesToCompare[table]); + // Dump the data into the work path as JSONL files + //const arrowTable = arrow.tableFromJSON(JSON.parse(JSON.stringify(tablesToCompare[table]))); + + const res = await dbAll(` + CREATE TABLE ${table} AS + SELECT * + FROM read_json_auto('${dumpPath}'); + `); + logger.info({ + message: "created table", + tableName: table, + queryResponse: res, + }); + } + const server = repl.start(); + if (args.repl) { + server.context.$db = { + // Setup raw access to the duckdb api via db + raw: db, + // Setup a convenience command that runs queries + q: async (query: string) => { + const res = await dbAll(query); + console.log(columnify(res as Record[])); + return res; + }, + }; + await new Promise((resolve, reject) => { + server.on("exit", () => { + resolve(); + }); + server.on("SIGINT", () => { + reject(new Error("SIGINT?")); + }); + }); + } + + // List all the new blockchain artifacts + // List all the new code artifacts + // List all the new package artifacts + + // List all the removed blockchain artifacts + // List all the removed code artifacts + // List all the removed package artifacts + + // List all the changed blockchain artifacts + // List all the changed code artifacts + // List all the changed package artifacts + }, + { unsafeCleanup: true }, + ); +} diff --git a/ops/external-prs/test.out b/ops/external-prs/test.out new file mode 100644 index 000000000..07b24e19f --- /dev/null +++ b/ops/external-prs/test.out @@ -0,0 +1,5 @@ +deploy=true +sha=ba596ee5d4adeb728365d106dc3392e2becb689a +pr=1071 +issue_author=oso-lets-go +comment_author=ravenac95 \ No newline at end of file diff --git a/ops/external-prs/test_profiles.yaml b/ops/external-prs/test_profiles.yaml new file mode 100644 index 000000000..94677fae4 --- /dev/null +++ b/ops/external-prs/test_profiles.yaml @@ -0,0 +1,13 @@ +opensource_observer: + target: playground + outputs: + playground: + type: bigquery + dataset: pr_1071 + job_execution_time_seconds: 300 + job_retries: 1 + location: US + method: service-account + keyfile: /home/runner/work/oso/oso/gha-creds-aa5a4b1c1c73a543.json + project: oso-pull-requests + threads: 32 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e286ef1cc..1b218cb8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -345,6 +345,9 @@ importers: '@google-cloud/bigquery': specifier: ^7.5.1 version: 7.5.1 + '@types/columnify': + specifier: ^1.5.4 + version: 1.5.4 '@types/libsodium-wrappers': specifier: ^0.7.13 version: 0.7.13 @@ -354,15 +357,27 @@ importers: chalk: specifier: ^5.3.0 version: 5.3.0 + columnify: + specifier: ^1.6.0 + version: 1.6.0 dayjs: specifier: ^1.11.10 version: 1.11.10 + duckdb: + specifier: ^0.10.1 + version: 0.10.1 libsodium-wrappers: specifier: ^0.7.13 version: 0.7.13 octokit: specifier: ^3.1.0 version: 3.1.0 + oss-directory: + specifier: ^0.0.10 + version: 0.0.10(ts-node@10.9.1)(typescript@5.3.3) + tmp-promise: + specifier: ^3.0.3 + version: 3.0.3 ts-dedent: specifier: ^2.2.0 version: 2.2.0 @@ -4180,6 +4195,10 @@ packages: tslib: 2.6.2 dev: false + /@gar/promisify@1.1.3: + resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} + dev: false + /@google-cloud/bigquery@7.5.1: resolution: {integrity: sha512-ocye5Bt2eNQMoLKy814TVTp9XrXLoyD18mGwsjmZR3mEHv5m9oxycjG4P77c+hbN9YcKxg+EOtAclULKB9pOVg==} engines: {node: '>=14.0.0'} @@ -5255,6 +5274,24 @@ packages: '@lukeed/csprng': 1.1.0 dev: false + /@mapbox/node-pre-gyp@1.0.11: + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + dependencies: + detect-libc: 2.0.3 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.6.12 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.5.4 + tar: 6.2.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /@mdx-js/mdx@3.0.0: resolution: {integrity: sha512-Icm0TBKBLYqroYbNW3BPnzMGn+7mwpQOK310aZ7+fkCtiU3aqv2cdcX+nd0Ydo3wI5Rx8bX2Z2QmGb/XcAClCw==} dependencies: @@ -6057,6 +6094,23 @@ packages: '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.1 dev: false + /@npmcli/fs@2.1.2: + resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + '@gar/promisify': 1.1.3 + semver: 7.5.4 + dev: false + + /@npmcli/move-file@2.0.1: + resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This functionality has been moved to @npmcli/fs + dependencies: + mkdirp: 1.0.4 + rimraf: 3.0.2 + dev: false + /@octokit/app@14.0.0: resolution: {integrity: sha512-g/zDXttroZ9Se08shK0d0d/j0cgSA+h4WV7qGUevNEM0piNBkIlfb4Fm6bSwCNAZhNf72mBgERmYOoxicPkqdw==} engines: {node: '>= 18'} @@ -8903,6 +8957,10 @@ packages: resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} dev: false + /@types/columnify@1.5.4: + resolution: {integrity: sha512-YPEVzmy3kJupUee1ueLuvGspy6U2JHcxt6rYvRsSCEgVC54+KdBFjQ6NG/0koZk69e1bfXwSusgChwdFhvEXMw==} + dev: false + /@types/command-line-args@5.2.0: resolution: {integrity: sha512-UuKzKpJJ/Ief6ufIaIzr3A/0XnluX7RvFgwkV89Yzvm77wCh1kFaFmqN8XEnGcN62EuHdedQjEMb8mYxFLGPyA==} dev: false @@ -9834,6 +9892,13 @@ packages: transitivePeerDependencies: - supports-color + /agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} + dependencies: + humanize-ms: 1.2.1 + dev: false + /aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} @@ -9996,10 +10061,30 @@ packages: normalize-path: 3.0.0 picomatch: 2.3.1 + /aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + dev: false + /arch@2.2.0: resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} dev: false + /are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + dev: false + + /are-we-there-yet@3.0.1: + resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + dev: false + /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -10700,6 +10785,32 @@ packages: engines: {node: '>= 0.8'} dev: false + /cacache@16.1.3: + resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + '@npmcli/fs': 2.1.2 + '@npmcli/move-file': 2.0.1 + chownr: 2.0.0 + fs-minipass: 2.1.0 + glob: 8.1.0 + infer-owner: 1.0.4 + lru-cache: 7.18.3 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + mkdirp: 1.0.4 + p-map: 4.0.0 + promise-inflight: 1.0.1 + rimraf: 3.0.2 + ssri: 9.0.1 + tar: 6.2.0 + unique-filename: 2.0.1 + transitivePeerDependencies: + - bluebird + dev: false + /cacheable-lookup@7.0.0: resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} engines: {node: '>=14.16'} @@ -10941,7 +11052,6 @@ packages: /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} - dev: true /chrome-trace-event@1.0.3: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} @@ -11106,7 +11216,6 @@ packages: /clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} - dev: true /clone@2.1.2: resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} @@ -11159,6 +11268,11 @@ packages: simple-swizzle: 0.2.2 dev: false + /color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + dev: false + /color@3.2.1: resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} dependencies: @@ -11180,6 +11294,14 @@ packages: text-hex: 1.0.0 dev: false + /columnify@1.6.0: + resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==} + engines: {node: '>=8.0.0'} + dependencies: + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: false + /combine-promises@1.2.0: resolution: {integrity: sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==} engines: {node: '>=10'} @@ -11314,6 +11436,10 @@ packages: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} dev: false + /console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + dev: false + /constant-case@3.0.4: resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} dependencies: @@ -11992,7 +12118,6 @@ packages: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: clone: 1.0.4 - dev: true /defer-to-connect@2.0.1: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} @@ -12043,6 +12168,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + /delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + dev: false + /depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} @@ -12084,6 +12213,11 @@ packages: engines: {node: '>=8'} dev: true + /detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + dev: false + /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -12297,6 +12431,19 @@ packages: resolution: {integrity: sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==} engines: {node: '>=4'} + /duckdb@0.10.1: + resolution: {integrity: sha512-g62Fcg0qKgYM2A0X1pBSBEf+vKJHAzeRf4A7UtyYpUwvXbnKp0mEZ2rGXTP+/dXObyTDu+lfnyxIlORkcd097w==} + requiresBuild: true + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + node-addon-api: 7.1.0 + node-gyp: 9.4.1 + transitivePeerDependencies: + - bluebird + - encoding + - supports-color + dev: false + /duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} dev: false @@ -12375,6 +12522,14 @@ packages: engines: {node: '>= 0.8'} dev: false + /encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + requiresBuild: true + dependencies: + iconv-lite: 0.6.3 + dev: false + optional: true + /end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: @@ -12432,6 +12587,10 @@ packages: engines: {node: '>=6'} dev: false + /err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + dev: false + /error-class-utils@4.0.0: resolution: {integrity: sha512-przSMatQ20oCvuiKYOX9hHDsXMA2AhFrGRCiR7RwoixDzu7ChsWx41XshLBpGKc+qgC2FOSvOX69FNFaBYArhg==} engines: {node: '>=18.18.0'} @@ -13208,6 +13367,10 @@ packages: jest-util: 29.7.0 dev: true + /exponential-backoff@3.1.1: + resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} + dev: false + /express@4.18.2: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} engines: {node: '>= 0.10.0'} @@ -13737,7 +13900,6 @@ packages: engines: {node: '>= 8'} dependencies: minipass: 3.3.6 - dev: true /fs-monkey@1.0.5: resolution: {integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==} @@ -13774,6 +13936,35 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true + /gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + dev: false + + /gauge@4.0.4: + resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + dev: false + /gaxios@6.3.0: resolution: {integrity: sha512-p+ggrQw3fBwH2F5N/PAI4k/G/y1art5OxKpb2J2chwNNHM4hHuAOtivjPuirMF4KNKwTTUal/lPfL2+7h2mEcg==} engines: {node: '>=14'} @@ -13936,6 +14127,17 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.0.1 + once: 1.4.0 + dev: false + /global-dirs@3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} engines: {node: '>=10'} @@ -14199,7 +14401,7 @@ packages: solc: 0.7.3(debug@4.3.4) source-map-support: 0.5.21 stacktrace-parser: 0.1.10 - ts-node: 10.9.1(@types/node@20.12.3)(typescript@5.3.3) + ts-node: 10.9.1(@types/node@20.11.17)(typescript@5.3.3) tsort: 0.0.1 typescript: 5.3.3 undici: 5.28.2 @@ -14242,6 +14444,10 @@ packages: dependencies: has-symbols: 1.0.3 + /has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + dev: false + /has-yarn@3.0.0: resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -14665,6 +14871,12 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + /humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + dependencies: + ms: 2.1.3 + dev: false + /husky@8.0.3: resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} engines: {node: '>=14'} @@ -14687,7 +14899,6 @@ packages: requiresBuild: true dependencies: safer-buffer: 2.1.2 - dev: true /icss-utils@5.1.0(postcss@8.4.33): resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} @@ -14765,6 +14976,10 @@ packages: engines: {node: '>=18'} dev: false + /infer-owner@1.0.4: + resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} + dev: false + /infima@0.2.0-alpha.43: resolution: {integrity: sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==} engines: {node: '>=12'} @@ -14890,6 +15105,14 @@ packages: fp-ts: 1.19.3 dev: false + /ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + dependencies: + jsbn: 1.1.0 + sprintf-js: 1.1.3 + dev: false + /ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -15095,6 +15318,10 @@ packages: engines: {node: '>=8'} dev: true + /is-lambda@1.0.1: + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} + dev: false + /is-lower-case@2.0.2: resolution: {integrity: sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==} dependencies: @@ -15958,6 +16185,10 @@ packages: dependencies: argparse: 2.0.1 + /jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + dev: false + /jsdom@20.0.3: resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} engines: {node: '>=14'} @@ -16501,6 +16732,11 @@ packages: dependencies: yallist: 4.0.0 + /lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + dev: false + /lru-queue@0.1.0: resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} dependencies: @@ -16521,6 +16757,13 @@ packages: hasBin: true dev: true + /make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + dependencies: + semver: 6.3.1 + dev: false + /make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -16531,6 +16774,31 @@ packages: /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + /make-fetch-happen@10.2.1: + resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + agentkeepalive: 4.5.0 + cacache: 16.1.3 + http-cache-semantics: 4.1.1 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-lambda: 1.0.1 + lru-cache: 7.18.3 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-fetch: 2.1.2 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 0.6.3 + promise-retry: 2.0.1 + socks-proxy-agent: 7.0.0 + ssri: 9.0.1 + transitivePeerDependencies: + - bluebird + - supports-color + dev: false + /makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} dependencies: @@ -17322,17 +17590,54 @@ packages: /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + /minipass-collect@1.0.2: + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + dev: false + + /minipass-fetch@2.1.2: + resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + minipass: 3.3.6 + minipass-sized: 1.0.3 + minizlib: 2.1.2 + optionalDependencies: + encoding: 0.1.13 + dev: false + + /minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + dev: false + + /minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + dependencies: + minipass: 3.3.6 + dev: false + + /minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + dependencies: + minipass: 3.3.6 + dev: false + /minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} dependencies: yallist: 4.0.0 - dev: true /minipass@5.0.0: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} - dev: true /minipass@7.0.4: resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} @@ -17344,7 +17649,6 @@ packages: dependencies: minipass: 3.3.6 yallist: 4.0.0 - dev: true /mkdirp@0.3.0: resolution: {integrity: sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==} @@ -17355,7 +17659,6 @@ packages: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true - dev: true /mnemonist@0.38.5: resolution: {integrity: sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==} @@ -17561,6 +17864,11 @@ packages: resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} dev: false + /node-addon-api@7.1.0: + resolution: {integrity: sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==} + engines: {node: ^16 || ^18 || >= 20} + dev: false + /node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -17606,6 +17914,27 @@ packages: hasBin: true dev: false + /node-gyp@9.4.1: + resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} + engines: {node: ^12.13 || ^14.13 || >=16} + hasBin: true + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + make-fetch-happen: 10.2.1 + nopt: 6.0.0 + npmlog: 6.0.2 + rimraf: 3.0.2 + semver: 7.5.4 + tar: 6.2.0 + which: 2.0.2 + transitivePeerDependencies: + - bluebird + - supports-color + dev: false + /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} dev: true @@ -17620,6 +17949,22 @@ packages: abbrev: 1.1.1 dev: false + /nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: false + + /nopt@6.0.0: + resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: false + /normalize-exception@3.0.0: resolution: {integrity: sha512-SMZtWSLjls45KBgwvS2jWyXLtOI9j90JyQ6tJstl91Gti4W7QwZyF/nWwlFRz/Cx4Gy70DAtLT0EzXYXcPJJUw==} engines: {node: '>=16.17.0'} @@ -17680,6 +18025,25 @@ packages: dependencies: path-key: 4.0.0 + /npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + dev: false + + /npmlog@6.0.2: + resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + are-we-there-yet: 3.0.1 + console-control-strings: 1.1.0 + gauge: 4.0.4 + set-blocking: 2.0.0 + dev: false + /nprogress@0.2.0: resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} dev: false @@ -17898,6 +18262,34 @@ packages: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} + /oss-directory@0.0.10(ts-node@10.9.1)(typescript@5.3.3): + resolution: {integrity: sha512-pkfyLDAAQ2/ATda1cBvVxQbdGCsCc2D4ZvBPaladQYKnDjtIOmV/l4eks9zSGyLF1NlflHdfp89DVBniySTo4w==} + engines: {node: '>=16'} + hasBin: true + dependencies: + '@ethereum-attestation-service/eas-sdk': 1.4.0(ts-node@10.9.1)(typescript@5.3.3) + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) + chalk: 5.3.0 + dayjs: 1.11.10 + dotenv: 16.4.1 + ethers: 6.10.0 + glob: 10.3.10 + lodash: 4.17.21 + simple-git: 3.22.0 + tmp-promise: 3.0.3 + ts-adt: 2.1.2 + winston: 3.11.0 + yaml: 2.3.4 + yargs: 17.7.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - ts-node + - typescript + - utf-8-validate + dev: false + /oss-directory@0.0.7(ts-node@10.9.1)(typescript@5.3.3): resolution: {integrity: sha512-xIgkdK8IiI2ho5BX12HnspYDYO+c1CluASJdlIXGyaIWPHVftu6U3l3CeyZq+mMbPQFpvGgJCHCSoC+Ajo8dbw==} engines: {node: '>=16'} @@ -18827,6 +19219,23 @@ packages: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} dev: false + /promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + dev: false + + /promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + dev: false + /promise@7.3.1: resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} dependencies: @@ -19798,6 +20207,11 @@ packages: - supports-color dev: false + /retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + dev: false + /retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -20161,7 +20575,6 @@ packages: /set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - dev: true /set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} @@ -20378,6 +20791,11 @@ packages: is-fullwidth-code-point: 5.0.0 dev: true + /smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + dev: false + /snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} dependencies: @@ -20417,6 +20835,25 @@ packages: websocket-driver: 0.7.4 dev: false + /socks-proxy-agent@7.0.0: + resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} + engines: {node: '>= 10'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4(supports-color@8.1.1) + socks: 2.8.1 + transitivePeerDependencies: + - supports-color + dev: false + + /socks@2.8.1: + resolution: {integrity: sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + dependencies: + ip-address: 9.0.5 + smart-buffer: 4.2.0 + dev: false + /solc@0.7.3(debug@4.3.4): resolution: {integrity: sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==} engines: {node: '>=8.0.0'} @@ -20539,11 +20976,22 @@ packages: /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + /sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + dev: false + /srcset@4.0.0: resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} engines: {node: '>=12'} dev: false + /ssri@9.0.1: + resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + minipass: 3.3.6 + dev: false + /stable@0.1.8: resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' @@ -21024,7 +21472,6 @@ packages: minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 - dev: true /teeny-request@9.0.0: resolution: {integrity: sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==} @@ -21305,7 +21752,6 @@ packages: typescript: 5.3.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - dev: true /ts-node@10.9.1(@types/node@20.12.3)(typescript@5.3.3): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} @@ -21336,6 +21782,7 @@ packages: typescript: 5.3.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + dev: true /tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -21631,6 +22078,20 @@ packages: trough: 2.1.0 vfile: 6.0.1 + /unique-filename@2.0.1: + resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + unique-slug: 3.0.0 + dev: false + + /unique-slug@3.0.0: + resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + imurmurhash: 0.1.4 + dev: false + /unique-string@3.0.0: resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} engines: {node: '>=12'} @@ -21971,7 +22432,6 @@ packages: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: defaults: 1.0.4 - dev: true /web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -22522,6 +22982,12 @@ packages: dependencies: isexe: 2.0.0 + /wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + dependencies: + string-width: 4.2.3 + dev: false + /widest-line@4.0.1: resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} engines: {node: '>=12'}