From 4fe8c6714968540cb8940f2db290efe902d1883c Mon Sep 17 00:00:00 2001 From: Zitao Xiong Date: Mon, 5 Feb 2024 02:58:14 +0800 Subject: [PATCH] init --- .envrc | 79 + .gitignore | 11 + .tool-versions | 3 + build.sh | 8 + configs/gen.sh | 333 ++++ deploy/Pulumi.dev.yaml | 3 + deploy/Pulumi.yaml | 3 + deploy/package.json | 13 + deploy/pnpm-lock.yaml | 1660 +++++++++++++++++ .../docker-composes/opi.docker-compose.yaml | 87 + .../restore.docker-compose.yaml | 30 + deploy/src/index.ts | 230 +++ deploy/src/scripts/config_bashrc.sh | 17 + deploy/src/scripts/print_mnt_name.sh | 28 + deploy/src/scripts/restore.sh | 18 + deploy/src/utils.ts | 44 + deploy/tsconfig.json | 17 + docker/bitcoind/Dockerfile | 28 + docker/bitcoind/docker-entrypoint.sh | 50 + docker/opi/Dockerfile | 66 + docker/opi/remvoe_pubkey_validate.js | 39 + provision/scripts/build/cleanup.sh | 44 + provision/scripts/build/configure-apt-mock.sh | 54 + provision/scripts/build/configure-apt.sh | 43 + provision/scripts/build/pull.sh | 4 + provision/scripts/build/setup.sh | 42 + provision/templates/opi.pkr.hcl | 74 + 27 files changed, 3028 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 .tool-versions create mode 100755 build.sh create mode 100755 configs/gen.sh create mode 100644 deploy/Pulumi.dev.yaml create mode 100644 deploy/Pulumi.yaml create mode 100644 deploy/package.json create mode 100644 deploy/pnpm-lock.yaml create mode 100644 deploy/src/docker-composes/opi.docker-compose.yaml create mode 100644 deploy/src/docker-composes/restore.docker-compose.yaml create mode 100644 deploy/src/index.ts create mode 100644 deploy/src/scripts/config_bashrc.sh create mode 100644 deploy/src/scripts/print_mnt_name.sh create mode 100644 deploy/src/scripts/restore.sh create mode 100644 deploy/src/utils.ts create mode 100644 deploy/tsconfig.json create mode 100644 docker/bitcoind/Dockerfile create mode 100755 docker/bitcoind/docker-entrypoint.sh create mode 100644 docker/opi/Dockerfile create mode 100644 docker/opi/remvoe_pubkey_validate.js create mode 100644 provision/scripts/build/cleanup.sh create mode 100644 provision/scripts/build/configure-apt-mock.sh create mode 100644 provision/scripts/build/configure-apt.sh create mode 100644 provision/scripts/build/pull.sh create mode 100644 provision/scripts/build/setup.sh create mode 100644 provision/templates/opi.pkr.hcl diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..6f1819f --- /dev/null +++ b/.envrc @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +set -Eeo pipefail + +export DOCKER_IMAGE_GROUP=alexgo-io +export OPI_IMAGE=alexgo-io/opi +export BITCOIND_IMAGE=alexgo-io/bitcoind + +export WORKSPACE_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +export OPI_PG_DATA_PATH="${WORKSPACE_ROOT}/data/opi/postgres-data" +export OPI_BITCOIND_PATH="${WORKSPACE_ROOT}/data/bitcoind" + +export DB_USER="postgres" +export DB_HOST="postgres-opi-server" +export DB_PORT="5432" +export DB_DATABASE="db_opi" +export DB_PASSWD="passwd_123!@#" +export DB_SSL="false" +export DB_MAX_CONNECTIONS=10 +export API_HOST="127.0.0.1" +export API_PORT="8001" +export API_TRUSTED_PROXY_CNT="0" + +# export DB_METAPROTOCOL_USER="postgres" +# export DB_METAPROTOCOL_HOST="postgres-metaprotocol-server" +# export DB_METAPROTOCOL_PORT="5432" +# export DB_METAPROTOCOL_DATABASE="db_metaprotocol" +# export DB_METAPROTOCOL_PASSWD="passwd_456!@#" + +export DB_METAPROTOCOL_USER="postgres" +export DB_METAPROTOCOL_HOST="postgres-opi-server" +export DB_METAPROTOCOL_PORT="5432" +export DB_METAPROTOCOL_DATABASE="db_opi" +export DB_METAPROTOCOL_PASSWD="passwd_123!@#" + +export API_HOST="127.0.0.1" +export API_PORT="8001" +export API_TRUSTED_PROXY_CNT="0" + +export NETWORK_TYPE="mainnet" + +export REPORT_TO_INDEXER="true" +export REPORT_URL="https://api.opi.network/report_block" +export REPORT_RETRIES="10" +export REPORT_NAME="alexgo-opi-sir" + +export USE_EXTRA_TABLES="true" + +export DB_MAX_CONNECTIONS="50" +export BITCOIN_CHAIN_FOLDER="/bitcoind/datadir" +# export COOKIE_FILE="/bitcoind/datadir/.cookie" + +export BITCOIN_RPC_USER="bitcoin" +export BITCOIN_RPC_PASSWD="3Pz9zHvEkNrHkKRg" +export BITCOIN_RPC_URL="http://bitcoind:8332" + +export BITCOIN_RPC_PORT="8332" +export BITCOIN_ZMQ_PORT="18543" + +export ORD_BINARY="/usr/local/OPI/ord/target/release/ord" +export ORD_FOLDER="/usr/local/OPI/ord/target/release" +export ORD_DATADIR="/ord_data" + +export OPI_VOLUME_SIZE="1200" +export OPI_VOLUME_SNAPSHOT_ID="" + +# DEPLOY +export DIGITAL_OCEAN_SSH_KEY_NAME="" +export DIGITAL_OCEAN_SSH_KEY_ID="" +export DIGITAL_OCEAN_API_KEY="" + +export PRIVATE_KEY_PATH="~/.ssh/id_rsa" + + +if [[ -f .envrc.override ]]; then + source_env .envrc.override +fi + +configs/gen.sh \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..06f75b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +data +configs/bitmap_api +configs/bitmap_index +configs/brc20_api +configs/brc20_index +configs/main_index +configs/sns_api +configs/sns_index +.envrc.override +deploy/node_modules +.vscode/sftp.json \ No newline at end of file diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..d8cb212 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,3 @@ +packer 1.10.1 +pulumi 3.104.2 +pnpm 8.15.1 \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..d74a2ba --- /dev/null +++ b/build.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -Eeo pipefail + +pushd docker/opi && docker build -t $OPI_IMAGE . && popd; +pushd docker/bitcoind && docker build -t $BITCOIND_IMAGE . && popd; + +docker push $OPI_IMAGE; +docker push $BITCOIND_IMAGE; \ No newline at end of file diff --git a/configs/gen.sh b/configs/gen.sh new file mode 100755 index 0000000..57fbf24 --- /dev/null +++ b/configs/gen.sh @@ -0,0 +1,333 @@ +#!/usr/bin/env bash +set -Eeo pipefail + +DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +cd $DIR + +# genearte env file for bitmap_api +: ' +# .env file +DB_USER="postgres" +DB_HOST="localhost" +DB_PORT="5432" +DB_DATABASE="postgres" +DB_PASSWD="" +DB_SSL="true" +DB_MAX_CONNECTIONS=10 + +API_HOST="127.0.0.1" +API_PORT="8001" +API_TRUSTED_PROXY_CNT="0" +' + +generate_env_bitmap_api() { + mkdir -p bitmap_api + { + echo "DB_USER=\"${DB_USER:-postgres}\"" + echo "DB_HOST=\"${DB_HOST:-localhost}\"" + echo "DB_PORT=\"${DB_PORT:-5432}\"" + echo "DB_DATABASE=\"${DB_DATABASE:-postgres}\"" + echo "DB_PASSWD=\"${DB_PASSWD}\"" + echo "DB_SSL=\"${DB_SSL:-true}\"" + echo "DB_MAX_CONNECTIONS=${DB_MAX_CONNECTIONS:-10}" + + echo "API_HOST=\"${API_HOST:-127.0.0.1}\"" + echo "API_PORT=\"${API_PORT:-8001}\"" + echo "API_TRUSTED_PROXY_CNT=\"${API_TRUSTED_PROXY_CNT:-0}\"" + } >"bitmap_api/.env" +} + +# generate env file for bitmap_index +: ' +DB_USER="postgres" +DB_HOST="localhost" +DB_PORT="5432" +DB_DATABASE="postgres" +DB_PASSWD="" + +DB_METAPROTOCOL_USER="postgres" +DB_METAPROTOCOL_HOST="localhost" +DB_METAPROTOCOL_PORT="5432" +DB_METAPROTOCOL_DATABASE="postgres" +DB_METAPROTOCOL_PASSWD="" + +NETWORK_TYPE="mainnet" + +## reporting system settings +REPORT_TO_INDEXER="true" +REPORT_URL="https://api.opi.network/report_block" +REPORT_RETRIES="10" +# set a name for report dashboard +REPORT_NAME="opi_bitmap_index" +' + +generate_env_bitmap_index() { + mkdir -p bitmap_index + { + echo "DB_USER=\"${DB_USER:-postgres}\"" + echo "DB_HOST=\"${DB_HOST:-localhost}\"" + echo "DB_PORT=\"${DB_PORT:-5432}\"" + echo "DB_DATABASE=\"${DB_DATABASE:-postgres}\"" + echo "DB_PASSWD=\"${DB_PASSWD}\"" + + echo "DB_METAPROTOCOL_USER=\"${DB_METAPROTOCOL_USER:-postgres}\"" + echo "DB_METAPROTOCOL_HOST=\"${DB_METAPROTOCOL_HOST:-localhost}\"" + echo "DB_METAPROTOCOL_PORT=\"${DB_METAPROTOCOL_PORT:-5432}\"" + echo "DB_METAPROTOCOL_DATABASE=\"${DB_METAPROTOCOL_DATABASE:-postgres}\"" + echo "DB_METAPROTOCOL_PASSWD=\"${DB_METAPROTOCOL_PASSWD}\"" + + echo "NETWORK_TYPE=\"${NETWORK_TYPE:-mainnet}\"" + + echo "REPORT_TO_INDEXER=\"${REPORT_TO_INDEXER:-true}\"" + echo "REPORT_URL=\"${REPORT_URL:-https://api.opi.network/report_block}\"" + echo "REPORT_RETRIES=\"${REPORT_RETRIES:-10}\"" + echo "REPORT_NAME=\"${REPORT_NAME:-opi_bitmap_index}\"" + } >"bitmap_index/.env" +} + +# generate env file for brc20_api +: ' +# .env file +DB_USER="postgres" +DB_HOST="localhost" +DB_PORT="5432" +DB_DATABASE="postgres" +DB_PASSWD="" +DB_SSL="true" +DB_MAX_CONNECTIONS=10 + +API_HOST="127.0.0.1" +API_PORT="8000" +API_TRUSTED_PROXY_CNT="0" + +USE_EXTRA_TABLES="true" +' +generate_env_brc20_api() { + mkdir -p brc20_api + { + echo "DB_USER=\"${DB_USER:-postgres}\"" + echo "DB_HOST=\"${DB_HOST:-localhost}\"" + echo "DB_PORT=\"${DB_PORT:-5432}\"" + echo "DB_DATABASE=\"${DB_DATABASE:-postgres}\"" + echo "DB_PASSWD=\"${DB_PASSWD}\"" + echo "DB_SSL=\"${DB_SSL:-true}\"" + echo "DB_MAX_CONNECTIONS=${DB_MAX_CONNECTIONS:-10}" + + echo "API_HOST=\"${API_HOST:-127.0.0.1}\"" + echo "API_PORT=\"${API_PORT:-8000}\"" + echo "API_TRUSTED_PROXY_CNT=\"${API_TRUSTED_PROXY_CNT:-0}\"" + + echo "USE_EXTRA_TABLES=\"${USE_EXTRA_TABLES:-true}\"" + } >"brc20_api/.env" +} + +# genearte env file for brc20_index +: ' +# .env +DB_USER="postgres" +DB_HOST="localhost" +DB_PORT="5432" +DB_DATABASE="postgres" +DB_PASSWD="" + +## main indexer database settings +DB_METAPROTOCOL_USER="postgres" +DB_METAPROTOCOL_HOST="localhost" +DB_METAPROTOCOL_PORT="5432" +DB_METAPROTOCOL_DATABASE="postgres" +DB_METAPROTOCOL_PASSWD="" + +NETWORK_TYPE="mainnet" + +## reporting system settings +REPORT_TO_INDEXER="true" +REPORT_URL="https://api.opi.network/report_block" +REPORT_RETRIES="10" +# set a name for report dashboard +REPORT_NAME="opi_brc20_index" + +# create brc20_current_balances and brc20_unused_tx_inscrs tables +CREATE_EXTRA_TABLES="true" +' +generate_env_brc20_index() { + mkdir -p brc20_index + { + echo "DB_USER=\"${DB_USER:-postgres}\"" + echo "DB_HOST=\"${DB_HOST:-localhost}\"" + echo "DB_PORT=\"${DB_PORT:-5432}\"" + echo "DB_DATABASE=\"${DB_DATABASE:-postgres}\"" + echo "DB_PASSWD=\"${DB_PASSWD}\"" + + echo "DB_METAPROTOCOL_USER=\"${DB_METAPROTOCOL_USER:-postgres}\"" + echo "DB_METAPROTOCOL_HOST=\"${DB_METAPROTOCOL_HOST:-localhost}\"" + echo "DB_METAPROTOCOL_PORT=\"${DB_METAPROTOCOL_PORT:-5432}\"" + echo "DB_METAPROTOCOL_DATABASE=\"${DB_METAPROTOCOL_DATABASE:-postgres}\"" + echo "DB_METAPROTOCOL_PASSWD=\"${DB_METAPROTOCOL_PASSWD}\"" + + echo "NETWORK_TYPE=\"${NETWORK_TYPE:-mainnet}\"" + + echo "REPORT_TO_INDEXER=\"${REPORT_TO_INDEXER:-true}\"" + echo "REPORT_URL=\"${REPORT_URL:-https://api.opi.network/report_block}\"" + echo "REPORT_RETRIES=\"${REPORT_RETRIES:-10}\"" + echo "REPORT_NAME=\"${REPORT_NAME:-opi_brc20_index}\"" + } >"brc20_index/.env" +} + +# generate env file for main_index +: ' +# .env file +DB_USER="postgres" +DB_HOST="localhost" +DB_PORT="5432" +DB_DATABASE="postgres" +DB_PASSWD="" +DB_SSL="true" +DB_MAX_CONNECTIONS=50 + +BITCOIN_CHAIN_FOLDER="~/.bitcoin/" +COOKIE_FILE="" + +# leave these empty to use .cookie file +BITCOIN_RPC_USER="" +BITCOIN_RPC_PASSWD="" +# `--rpc-url` parameter for `ord`, example: `127.0.0.1:8332` +BITCOIN_RPC_URL="" + +# change to ord.exe on Windows (without ./) +ORD_BINARY="./ord" + +# leave default if repository folder structure hasnt been changed +ORD_FOLDER="../../ord/target/release/" +# relative to ord folder +ORD_DATADIR="." + +NETWORK_TYPE="mainnet" +' + +generate_env_main_index() { + mkdir -p main_index + + { + echo "DB_USER=\"${DB_USER:-postgres}\"" + echo "DB_HOST=\"${DB_HOST:-localhost}\"" + echo "DB_PORT=\"${DB_PORT:-5432}\"" + echo "DB_DATABASE=\"${DB_DATABASE:-postgres}\"" + echo "DB_PASSWD=\"${DB_PASSWD}\"" + echo "DB_SSL=\"${DB_SSL:-true}\"" + echo "DB_MAX_CONNECTIONS=${DB_MAX_CONNECTIONS:-50}" + + echo "BITCOIN_CHAIN_FOLDER=\"${BITCOIN_CHAIN_FOLDER:-~/.bitcoin/}\"" + echo "COOKIE_FILE=\"${COOKIE_FILE}\"" + echo "BITCOIN_RPC_USER=\"${BITCOIN_RPC_USER}\"" + echo "BITCOIN_RPC_PASSWD=\"${BITCOIN_RPC_PASSWD}\"" + echo "BITCOIN_RPC_URL=\"${BITCOIN_RPC_URL}\"" + + echo "ORD_BINARY=\"${ORD_BINARY:-./ord}\"" + echo "ORD_FOLDER=\"${ORD_FOLDER:-../../ord/target/release/}\"" + echo "ORD_DATADIR=\"${ORD_DATADIR:-.}\"" + + echo "NETWORK_TYPE=\"${NETWORK_TYPE:-mainnet}\"" + } >"main_index/.env" +} + +# genearte env file for sns_api +: ' +# .env file +DB_USER="postgres" +DB_HOST="localhost" +DB_PORT="5432" +DB_DATABASE="postgres" +DB_PASSWD="" +DB_SSL="true" +DB_MAX_CONNECTIONS=10 + +API_HOST="127.0.0.1" +API_PORT="8002" +API_TRUSTED_PROXY_CNT="0" +' +generate_env_sns_api() { + mkdir -p sns_api + { + echo "DB_USER=\"${DB_USER:-postgres}\"" + echo "DB_HOST=\"${DB_HOST:-localhost}\"" + echo "DB_PORT=\"${DB_PORT:-5432}\"" + echo "DB_DATABASE=\"${DB_DATABASE:-postgres}\"" + echo "DB_PASSWD=\"${DB_PASSWD}\"" + echo "DB_SSL=\"${DB_SSL:-true}\"" + echo "DB_MAX_CONNECTIONS=${DB_MAX_CONNECTIONS:-10}" + + echo "API_HOST=\"${API_HOST:-127.0.0.1}\"" + echo "API_PORT=\"${API_PORT:-8002}\"" + echo "API_TRUSTED_PROXY_CNT=\"${API_TRUSTED_PROXY_CNT:-0}\"" + } >"sns_api/.env" +} + +# generate env file for sns_index +: ' +# .env +DB_USER="postgres" +DB_HOST="localhost" +DB_PORT="5432" +DB_DATABASE="postgres" +DB_PASSWD="" + +DB_METAPROTOCOL_USER="postgres" +DB_METAPROTOCOL_HOST="localhost" +DB_METAPROTOCOL_PORT="5432" +DB_METAPROTOCOL_DATABASE="postgres" +DB_METAPROTOCOL_PASSWD="" + +NETWORK_TYPE="mainnet" + +## reporting system settings +REPORT_TO_INDEXER="true" +REPORT_URL="https://api.opi.network/report_block" +REPORT_RETRIES="10" +# set a name for report dashboard +REPORT_NAME="opi_sns_index" +' + +generate_env_sns_index() { + mkdir -p sns_index + { + echo "DB_USER=\"${DB_USER:-postgres}\"" + echo "DB_HOST=\"${DB_HOST:-localhost}\"" + echo "DB_PORT=\"${DB_PORT:-5432}\"" + echo "DB_DATABASE=\"${DB_DATABASE:-postgres}\"" + echo "DB_PASSWD=\"${DB_PASSWD}\"" + + echo "DB_METAPROTOCOL_USER=\"${DB_METAPROTOCOL_USER:-postgres}\"" + echo "DB_METAPROTOCOL_HOST=\"${DB_METAPROTOCOL_HOST:-localhost}\"" + echo "DB_METAPROTOCOL_PORT=\"${DB_METAPROTOCOL_PORT:-5432}\"" + echo "DB_METAPROTOCOL_DATABASE=\"${DB_METAPROTOCOL_DATABASE:-postgres}\"" + echo "DB_METAPROTOCOL_PASSWD=\"${DB_METAPROTOCOL_PASSWD}\"" + + echo "NETWORK_TYPE=\"${NETWORK_TYPE:-mainnet}\"" + + echo "REPORT_TO_INDEXER=\"${REPORT_TO_INDEXER:-true}\"" + echo "REPORT_URL=\"${REPORT_URL:-https://api.opi.network/report_block}\"" + echo "REPORT_RETRIES=\"${REPORT_RETRIES:-10}\"" + echo "REPORT_NAME=\"${REPORT_NAME:-opi_sns_index}\"" + } >"sns_index/.env" +} + +rm -rf bitmap_api +rm -rf bitmap_index +rm -rf brc20_api +rm -rf brc20_index +rm -rf main_index +rm -rf sns_api +rm -rf sns_index + +generate_env_bitmap_api +generate_env_bitmap_index +generate_env_brc20_api +generate_env_brc20_index +generate_env_main_index +generate_env_sns_api +generate_env_sns_index + +green() { + echo -e "\033[32m$1\033[0m" +} +green "generated env files at $PWD" \ No newline at end of file diff --git a/deploy/Pulumi.dev.yaml b/deploy/Pulumi.dev.yaml new file mode 100644 index 0000000..e93fc7c --- /dev/null +++ b/deploy/Pulumi.dev.yaml @@ -0,0 +1,3 @@ +config: + digitalocean:token: + secure: AAABADkzEMuJAeOt2sSca5LUIUd8qA1X48n3P6jtlAy4OKkGfsv1kjFBhiGT2op77cocNgUyvO7cY/QI8JrxZMvhB4HPimbarL+PDMYEMKabhjHdCzQ1rIw7pybZn9Zn13YxheJCJg== diff --git a/deploy/Pulumi.yaml b/deploy/Pulumi.yaml new file mode 100644 index 0000000..3acd4c2 --- /dev/null +++ b/deploy/Pulumi.yaml @@ -0,0 +1,3 @@ +name: opi-infra +runtime: nodejs +description: DigitalOcean OPI diff --git a/deploy/package.json b/deploy/package.json new file mode 100644 index 0000000..4c3a328 --- /dev/null +++ b/deploy/package.json @@ -0,0 +1,13 @@ +{ + "name": "pulumi-opi", + "main": "src/index.ts", + "devDependencies": { + "@types/node": "^20", + "dotenv": "^16.3.1" + }, + "dependencies": { + "@pulumi/command": "^0.9.2", + "@pulumi/digitalocean": "4.25.1", + "@pulumi/pulumi": "3.104.2" + } +} diff --git a/deploy/pnpm-lock.yaml b/deploy/pnpm-lock.yaml new file mode 100644 index 0000000..92c7993 --- /dev/null +++ b/deploy/pnpm-lock.yaml @@ -0,0 +1,1660 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@pulumi/command': + specifier: ^0.9.2 + version: 0.9.2 + '@pulumi/digitalocean': + specifier: 4.25.1 + version: 4.25.1 + '@pulumi/pulumi': + specifier: 3.104.2 + version: 3.104.2 + +devDependencies: + '@types/node': + specifier: ^20 + version: 20.11.16 + dotenv: + specifier: ^16.3.1 + version: 16.3.1 + +packages: + + /@grpc/grpc-js@1.9.14: + resolution: {integrity: sha512-nOpuzZ2G3IuMFN+UPPpKrC6NsLmWsTqSsm66IRfnBt1D4pwTqE27lmbpcPM+l2Ua4gE7PfjRHI6uedAy7hoXUw==} + engines: {node: ^8.13.0 || >=10.10.0} + dependencies: + '@grpc/proto-loader': 0.7.10 + '@types/node': 20.11.16 + dev: false + + /@grpc/proto-loader@0.7.10: + resolution: {integrity: sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==} + engines: {node: '>=6'} + hasBin: true + dependencies: + lodash.camelcase: 4.3.0 + long: 5.2.3 + protobufjs: 7.2.5 + yargs: 17.7.2 + dev: false + + /@logdna/tail-file@2.2.0: + resolution: {integrity: sha512-XGSsWDweP80Fks16lwkAUIr54ICyBs6PsI4mpfTLQaWgEJRtY9xEV+PeyDpJ+sJEGZxqINlpmAwe/6tS1pP8Ng==} + engines: {node: '>=10.3.0'} + dev: false + + /@opentelemetry/api-metrics@0.32.0: + resolution: {integrity: sha512-g1WLhpG8B6iuDyZJFRGsR+JKyZ94m5LEmY2f+duEJ9Xb4XRlLHrZvh6G34OH6GJ8iDHxfHb/sWjJ1ZpkI9yGMQ==} + engines: {node: '>=14'} + deprecated: Please use @opentelemetry/api >= 1.3.0 + dependencies: + '@opentelemetry/api': 1.7.0 + dev: false + + /@opentelemetry/api@1.7.0: + resolution: {integrity: sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==} + engines: {node: '>=8.0.0'} + dev: false + + /@opentelemetry/context-async-hooks@1.18.1(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-HHfJR32NH2x0b69CACCwH8m1dpNALoCTtpgmIWMNkeMGNUeKT48d4AX4xsF4uIRuUoRTbTgtSBRvS+cF97qwCQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.8.0' + dependencies: + '@opentelemetry/api': 1.7.0 + dev: false + + /@opentelemetry/core@1.18.1(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-kvnUqezHMhsQvdsnhnqTNfAJs3ox/isB0SVrM1dhVFw7SsB7TstuVa6fgWnN2GdPyilIFLUvvbTZoVRmx6eiRg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.8.0' + dependencies: + '@opentelemetry/api': 1.7.0 + '@opentelemetry/semantic-conventions': 1.18.1 + dev: false + + /@opentelemetry/exporter-zipkin@1.18.1(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-RmoWVFXFhvIh3q4szUe8I+/vxuMR0HNsOm39zNxnWJcK7JDwnPra9cLY/M78u6bTgB6Fte8GKgU128vvDzz0Iw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + dependencies: + '@opentelemetry/api': 1.7.0 + '@opentelemetry/core': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/resources': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/sdk-trace-base': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/semantic-conventions': 1.18.1 + dev: false + + /@opentelemetry/instrumentation-grpc@0.32.0(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-Az6wdkPx/Mi26lT9LKFV6GhCA9prwQFPz5eCNSExTnSP49YhQ7XCjzPd2POPeLKt84ICitrBMdE1mj0zbPdLAQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + dependencies: + '@opentelemetry/api': 1.7.0 + '@opentelemetry/api-metrics': 0.32.0 + '@opentelemetry/instrumentation': 0.32.0(@opentelemetry/api@1.7.0) + '@opentelemetry/semantic-conventions': 1.6.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@opentelemetry/instrumentation@0.32.0(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-y6ADjHpkUz/v1nkyyYjsQa/zorhX+0qVGpFvXMcbjU4sHnBnC02c6wcc93sIgZfiQClIWo45TGku1KQxJ5UUbQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + dependencies: + '@opentelemetry/api': 1.7.0 + '@opentelemetry/api-metrics': 0.32.0 + require-in-the-middle: 5.2.0 + semver: 7.5.4 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@opentelemetry/propagator-b3@1.18.1(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-oSTUOsnt31JDx5SoEy27B5jE1/tiPvvE46w7CDKj0R5oZhCCfYH2bbSGa7NOOyDXDNqQDkgqU1DIV/xOd3f8pw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.8.0' + dependencies: + '@opentelemetry/api': 1.7.0 + '@opentelemetry/core': 1.18.1(@opentelemetry/api@1.7.0) + dev: false + + /@opentelemetry/propagator-jaeger@1.18.1(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-Kh4M1Qewv0Tbmts6D8LgNzx99IjdE18LCmY/utMkgVyU7Bg31Yuj+X6ZyoIRKPcD2EV4rVkuRI16WVMRuGbhWA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.8.0' + dependencies: + '@opentelemetry/api': 1.7.0 + '@opentelemetry/core': 1.18.1(@opentelemetry/api@1.7.0) + dev: false + + /@opentelemetry/resources@1.18.1(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-JjbcQLYMttXcIabflLRuaw5oof5gToYV9fuXbcsoOeQ0BlbwUn6DAZi++PNsSz2jjPeASfDls10iaO/8BRIPRA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.8.0' + dependencies: + '@opentelemetry/api': 1.7.0 + '@opentelemetry/core': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/semantic-conventions': 1.18.1 + dev: false + + /@opentelemetry/sdk-trace-base@1.18.1(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-tRHfDxN5dO+nop78EWJpzZwHsN1ewrZRVVwo03VJa3JQZxToRDH29/+MB24+yoa+IArerdr7INFJiX/iN4gjqg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.8.0' + dependencies: + '@opentelemetry/api': 1.7.0 + '@opentelemetry/core': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/resources': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/semantic-conventions': 1.18.1 + dev: false + + /@opentelemetry/sdk-trace-node@1.18.1(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-ML0l9TNlfLoplLF1F8lb95NGKgdm6OezDS3Ymqav9sYxMd5bnH2LZVzd4xEF+ov5vpZJOGdWxJMs2nC9no7+xA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.8.0' + dependencies: + '@opentelemetry/api': 1.7.0 + '@opentelemetry/context-async-hooks': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/core': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/propagator-b3': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/propagator-jaeger': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/sdk-trace-base': 1.18.1(@opentelemetry/api@1.7.0) + semver: 7.5.4 + dev: false + + /@opentelemetry/semantic-conventions@1.18.1: + resolution: {integrity: sha512-+NLGHr6VZwcgE/2lw8zDIufOCGnzsA5CbQIMleXZTrgkBd0TanCX+MiDYJ1TOS4KL/Tqk0nFRxawnaYr6pkZkA==} + engines: {node: '>=14'} + dev: false + + /@opentelemetry/semantic-conventions@1.6.0: + resolution: {integrity: sha512-aPfcBeLErM/PPiAuAbNFLN5sNbZLc3KZlar27uohllN8Zs6jJbHyJU1y7cMA6W/zuq+thkaG8mujiS+3iD/FWQ==} + engines: {node: '>=14'} + dev: false + + /@protobufjs/aspromise@1.1.2: + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + dev: false + + /@protobufjs/base64@1.1.2: + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + dev: false + + /@protobufjs/codegen@2.0.4: + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + dev: false + + /@protobufjs/eventemitter@1.1.0: + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + dev: false + + /@protobufjs/fetch@1.1.0: + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + dev: false + + /@protobufjs/float@1.0.2: + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + dev: false + + /@protobufjs/inquire@1.1.0: + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + dev: false + + /@protobufjs/path@1.1.2: + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + dev: false + + /@protobufjs/pool@1.1.0: + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + dev: false + + /@protobufjs/utf8@1.1.0: + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + dev: false + + /@pulumi/command@0.9.2: + resolution: {integrity: sha512-9RaGDiy8jFCiaarj4EOrMW/fVCM/AgBigzwM6CKzlR49x8UFiRDmKrXfEVHb8r2P9IpC4IaAZf5VbNNAHwN/rA==} + requiresBuild: true + dependencies: + '@pulumi/pulumi': 3.104.2 + transitivePeerDependencies: + - supports-color + dev: false + + /@pulumi/digitalocean@4.25.1: + resolution: {integrity: sha512-Jz/lvlNzUBXh2aYqJi/WBi6AUzLXPaHxkeOZRuy1HWk4WtyeYkZRBV4YcI5IX7kV0FZmF3Q9RuIeVSFk032JLQ==} + dependencies: + '@pulumi/pulumi': 3.104.2 + builtin-modules: 3.0.0 + read-package-tree: 5.3.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + dev: false + + /@pulumi/pulumi@3.104.2: + resolution: {integrity: sha512-Ljl1JaMFjcfse4M4bFQDadU5FTM4R404DZwM7fegcE0KvWJY2RPZj0TzBazv1vbwSlhDvzmuxQhHN1+U9y+EvQ==} + engines: {node: '>=8.13.0 || >=10.10.0'} + dependencies: + '@grpc/grpc-js': 1.9.14 + '@logdna/tail-file': 2.2.0 + '@opentelemetry/api': 1.7.0 + '@opentelemetry/exporter-zipkin': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/instrumentation': 0.32.0(@opentelemetry/api@1.7.0) + '@opentelemetry/instrumentation-grpc': 0.32.0(@opentelemetry/api@1.7.0) + '@opentelemetry/resources': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/sdk-trace-base': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/sdk-trace-node': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/semantic-conventions': 1.18.1 + '@pulumi/query': 0.3.0 + '@types/google-protobuf': 3.15.12 + '@types/semver': 7.5.6 + '@types/tmp': 0.2.6 + execa: 5.1.1 + google-protobuf: 3.21.2 + got: 11.8.6 + ini: 2.0.0 + js-yaml: 3.14.1 + minimist: 1.2.8 + normalize-package-data: 3.0.3 + pkg-dir: 7.0.0 + read-package-tree: 5.3.1 + require-from-string: 2.0.2 + semver: 7.5.4 + source-map-support: 0.5.21 + tmp: 0.2.1 + ts-node: 7.0.1 + typescript: 3.8.3 + upath: 1.2.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@pulumi/query@0.3.0: + resolution: {integrity: sha512-xfo+yLRM2zVjVEA4p23IjQWzyWl1ZhWOGobsBqRpIarzLvwNH/RAGaoehdxlhx4X92302DrpdIFgTICMN4P38w==} + dev: false + + /@sindresorhus/is@4.6.0: + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + dev: false + + /@szmarczak/http-timer@4.0.6: + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} + engines: {node: '>=10'} + dependencies: + defer-to-connect: 2.0.1 + dev: false + + /@types/cacheable-request@6.0.3: + resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + dependencies: + '@types/http-cache-semantics': 4.0.4 + '@types/keyv': 3.1.4 + '@types/node': 20.11.16 + '@types/responselike': 1.0.3 + dev: false + + /@types/google-protobuf@3.15.12: + resolution: {integrity: sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ==} + dev: false + + /@types/http-cache-semantics@4.0.4: + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + dev: false + + /@types/keyv@3.1.4: + resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + dependencies: + '@types/node': 20.11.16 + dev: false + + /@types/node@20.11.16: + resolution: {integrity: sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==} + dependencies: + undici-types: 5.26.5 + + /@types/responselike@1.0.3: + resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + dependencies: + '@types/node': 20.11.16 + dev: false + + /@types/semver@7.5.6: + resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} + dev: false + + /@types/tmp@0.2.6: + resolution: {integrity: sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==} + dev: false + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: false + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: false + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: false + + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.5 + is-array-buffer: 3.0.2 + dev: false + + /array.prototype.reduce@1.0.6: + resolution: {integrity: sha512-UW+Mz8LG/sPSU8jRDCjVr6J/ZKAGpHfwrZ6kWTG5qCxIEiXdVshqGnu5vEZA8S1y6X4aCSbQZ0/EEsfvEvBiSg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-array-method-boxes-properly: 1.0.0 + is-string: 1.0.7 + dev: false + + /arraybuffer.prototype.slice@1.0.2: + resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + is-array-buffer: 3.0.2 + is-shared-array-buffer: 1.0.2 + dev: false + + /arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + dev: false + + /asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + dev: false + + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: false + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: false + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: false + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: false + + /builtin-modules@3.0.0: + resolution: {integrity: sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg==} + engines: {node: '>=6'} + dev: false + + /cacheable-lookup@5.0.4: + resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} + engines: {node: '>=10.6.0'} + dev: false + + /cacheable-request@7.0.4: + resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} + engines: {node: '>=8'} + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.1.1 + keyv: 4.5.4 + lowercase-keys: 2.0.0 + normalize-url: 6.1.0 + responselike: 2.0.1 + dev: false + + /call-bind@1.0.5: + resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} + dependencies: + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + set-function-length: 1.1.1 + dev: false + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: false + + /clone-response@1.0.3: + resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + dependencies: + mimic-response: 1.0.1 + dev: false + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: false + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: false + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: false + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: false + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: false + + /debuglog@1.0.1: + resolution: {integrity: sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + dev: false + + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dependencies: + mimic-response: 3.1.0 + dev: false + + /defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + dev: false + + /define-data-property@1.1.1: + resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: false + + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + has-property-descriptors: 1.0.1 + object-keys: 1.1.1 + dev: false + + /dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + dev: false + + /diff@3.5.0: + resolution: {integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==} + engines: {node: '>=0.3.1'} + dev: false + + /dotenv@16.3.1: + resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + engines: {node: '>=12'} + dev: true + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: false + + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: false + + /es-abstract@1.22.3: + resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + arraybuffer.prototype.slice: 1.0.2 + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + es-set-tostringtag: 2.0.2 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.2 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + internal-slot: 1.0.6 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.12 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.5.1 + safe-array-concat: 1.0.1 + safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.8 + string.prototype.trimend: 1.0.7 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.0 + typed-array-byte-length: 1.0.0 + typed-array-byte-offset: 1.0.0 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.13 + dev: false + + /es-array-method-boxes-properly@1.0.0: + resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} + dev: false + + /es-set-tostringtag@2.0.2: + resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + has-tostringtag: 1.0.0 + hasown: 2.0.0 + dev: false + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: false + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: false + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: false + + /find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + dev: false + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: false + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: false + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: false + + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + functions-have-names: 1.2.3 + dev: false + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: false + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: false + + /get-intrinsic@1.2.2: + resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} + dependencies: + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + dev: false + + /get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + dependencies: + pump: 3.0.0 + dev: false + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: false + + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + dev: false + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: false + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + dev: false + + /google-protobuf@3.21.2: + resolution: {integrity: sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==} + dev: false + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.2 + dev: false + + /got@11.8.6: + resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} + engines: {node: '>=10.19.0'} + dependencies: + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.3 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.4 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 + dev: false + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: false + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: false + + /has-property-descriptors@1.0.1: + resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} + dependencies: + get-intrinsic: 1.2.2 + dev: false + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: false + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: false + + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: false + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: false + + /hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: false + + /hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + dependencies: + lru-cache: 6.0.0 + dev: false + + /http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + dev: false + + /http2-wrapper@1.0.3: + resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} + engines: {node: '>=10.19.0'} + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + dev: false + + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: false + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: false + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: false + + /ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + dev: false + + /internal-slot@1.0.6: + resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + hasown: 2.0.0 + side-channel: 1.0.4 + dev: false + + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-typed-array: 1.1.12 + dev: false + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: false + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + has-tostringtag: 1.0.0 + dev: false + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: false + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.0 + dev: false + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: false + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: false + + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: false + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: false + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + has-tostringtag: 1.0.0 + dev: false + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.5 + dev: false + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: false + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: false + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: false + + /is-typed-array@1.1.12: + resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.13 + dev: false + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.5 + dev: false + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: false + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: false + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: false + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: false + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: false + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: false + + /locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-locate: 6.0.0 + dev: false + + /lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + dev: false + + /long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + dev: false + + /lowercase-keys@2.0.0: + resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} + engines: {node: '>=8'} + dev: false + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: false + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: false + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: false + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: false + + /mimic-response@1.0.1: + resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + engines: {node: '>=4'} + dev: false + + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: false + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: false + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: false + + /mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: false + + /module-details-from-path@1.0.3: + resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} + dev: false + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: false + + /normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.8 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + dev: false + + /normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.13.1 + semver: 7.5.4 + validate-npm-package-license: 3.0.4 + dev: false + + /normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + dev: false + + /npm-normalize-package-bin@1.0.1: + resolution: {integrity: sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==} + dev: false + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: false + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + dev: false + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: false + + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: false + + /object.getownpropertydescriptors@2.1.7: + resolution: {integrity: sha512-PrJz0C2xJ58FNn11XV2lr4Jt5Gzl94qpy9Lu0JlfEj14z88sqbSBJCBEzdlNUCzY2gburhbrwOZ5BHCmuNUy0g==} + engines: {node: '>= 0.8'} + dependencies: + array.prototype.reduce: 1.0.6 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + safe-array-concat: 1.0.1 + dev: false + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: false + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: false + + /p-cancelable@2.1.1: + resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} + engines: {node: '>=8'} + dev: false + + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.0.0 + dev: false + + /p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-limit: 4.0.0 + dev: false + + /path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: false + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: false + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: false + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: false + + /pkg-dir@7.0.0: + resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} + engines: {node: '>=14.16'} + dependencies: + find-up: 6.3.0 + dev: false + + /protobufjs@7.2.5: + resolution: {integrity: sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==} + engines: {node: '>=12.0.0'} + requiresBuild: true + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 20.11.16 + long: 5.2.3 + dev: false + + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: false + + /quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: false + + /read-package-json@2.1.2: + resolution: {integrity: sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==} + dependencies: + glob: 7.2.3 + json-parse-even-better-errors: 2.3.1 + normalize-package-data: 2.5.0 + npm-normalize-package-bin: 1.0.1 + dev: false + + /read-package-tree@5.3.1: + resolution: {integrity: sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==} + deprecated: The functionality that this package provided is now in @npmcli/arborist + dependencies: + read-package-json: 2.1.2 + readdir-scoped-modules: 1.1.0 + util-promisify: 2.1.0 + dev: false + + /readdir-scoped-modules@1.1.0: + resolution: {integrity: sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==} + deprecated: This functionality has been moved to @npmcli/fs + dependencies: + debuglog: 1.0.1 + dezalgo: 1.0.4 + graceful-fs: 4.2.11 + once: 1.4.0 + dev: false + + /regexp.prototype.flags@1.5.1: + resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + set-function-name: 2.0.1 + dev: false + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: false + + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: false + + /require-in-the-middle@5.2.0: + resolution: {integrity: sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==} + engines: {node: '>=6'} + dependencies: + debug: 4.3.4 + module-details-from-path: 1.0.3 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + dev: false + + /resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + dev: false + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: false + + /responselike@2.0.1: + resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + dependencies: + lowercase-keys: 2.0.0 + dev: false + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: false + + /safe-array-concat@1.0.1: + resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: false + + /safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-regex: 1.1.4 + dev: false + + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + dev: false + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: false + + /set-function-length@1.1.1: + resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: false + + /set-function-name@2.0.1: + resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.1 + dev: false + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: false + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: false + + /shimmer@1.2.1: + resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} + dev: false + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + object-inspect: 1.13.1 + dev: false + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: false + + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: false + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: false + + /spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.16 + dev: false + + /spdx-exceptions@2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: false + + /spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.16 + dev: false + + /spdx-license-ids@3.0.16: + resolution: {integrity: sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==} + dev: false + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: false + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: false + + /string.prototype.trim@1.2.8: + resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: false + + /string.prototype.trimend@1.0.7: + resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: false + + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: false + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: false + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: false + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: false + + /tmp@0.2.1: + resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} + engines: {node: '>=8.17.0'} + dependencies: + rimraf: 3.0.2 + dev: false + + /ts-node@7.0.1: + resolution: {integrity: sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==} + engines: {node: '>=4.2.0'} + hasBin: true + dependencies: + arrify: 1.0.1 + buffer-from: 1.1.2 + diff: 3.5.0 + make-error: 1.3.6 + minimist: 1.2.8 + mkdirp: 0.5.6 + source-map-support: 0.5.21 + yn: 2.0.0 + dev: false + + /typed-array-buffer@1.0.0: + resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-typed-array: 1.1.12 + dev: false + + /typed-array-byte-length@1.0.0: + resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: false + + /typed-array-byte-offset@1.0.0: + resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: false + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.5 + for-each: 0.3.3 + is-typed-array: 1.1.12 + dev: false + + /typescript@3.8.3: + resolution: {integrity: sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: false + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.5 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: false + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + /upath@1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + dev: false + + /util-promisify@2.1.0: + resolution: {integrity: sha512-K+5eQPYs14b3+E+hmE2J6gCZ4JmMl9DbYS6BeP2CHq6WMuNxErxf5B/n0fz85L8zUuoO6rIzNNmIQDu/j+1OcA==} + dependencies: + object.getownpropertydescriptors: 2.1.7 + dev: false + + /validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + dev: false + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: false + + /which-typed-array@1.1.13: + resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: false + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: false + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: false + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: false + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: false + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: false + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: false + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: false + + /yn@2.0.0: + resolution: {integrity: sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==} + engines: {node: '>=4'} + dev: false + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: false diff --git a/deploy/src/docker-composes/opi.docker-compose.yaml b/deploy/src/docker-composes/opi.docker-compose.yaml new file mode 100644 index 0000000..fca7f02 --- /dev/null +++ b/deploy/src/docker-composes/opi.docker-compose.yaml @@ -0,0 +1,87 @@ +version: "3.7" +services: + postgres-opi-server: + image: postgres:15-alpine + restart: always + environment: + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWD} + POSTGRES_DB: ${DB_DATABASE} + volumes: + - ${OPI_PG_DATA_PATH}:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"] + interval: 10s + timeout: 5s + retries: 5 + ports: + - "21432:5432" + bitcoind: + image: ${BITCOIND_IMAGE} + volumes: + - ${OPI_BITCOIND_PATH}:/usr/local/bitcoind + environment: + - BITCOIN_RPC_USER=${BITCOIN_RPC_USER} + - BITCOIN_RPC_PASSWD=${BITCOIN_RPC_PASSWD} + ports: + - "${BITCOIN_RPC_PORT}:${BITCOIN_RPC_PORT}" + meta-protocol-indexer: + image: ${OPI_IMAGE} + restart: always + command: 'bash -c "cd /usr/local/OPI/modules/main_index && node index.js"' + volumes: + - ./configs/main_index/.env:/usr/local/OPI/modules/main_index/.env + depends_on: + postgres-opi-server: + condition: service_healthy + brc20-indexer: + image: ${OPI_IMAGE} + restart: always + command: 'bash -c "cd /usr/local/OPI/modules/brc20_index && python brc20_index.py"' + volumes: + - ./configs/brc20_index/.env:/usr/local/OPI/modules/brc20_index/.env + depends_on: + postgres-opi-server: + condition: service_healthy + brc20-api: + image: ${OPI_IMAGE} + command: 'bash -c "cd /usr/local/OPI/modules/brc20_api && node api.js"' + volumes: + - ./configs/brc20_api/.env:/usr/local/OPI/modules/brc20_api/.env + depends_on: + postgres-opi-server: + condition: service_healthy + bitmap-indexer: + image: ${OPI_IMAGE} + restart: always + command: 'bash -c "cd /usr/local/OPI/modules/bitmap_index && python bitmap_index.py"' + volumes: + - ./configs/bitmap_index/.env:/usr/local/OPI/modules/bitmap_index/.env + depends_on: + postgres-opi-server: + condition: service_healthy + bitmap-api: + image: ${OPI_IMAGE} + command: 'bash -c "cd /usr/local/OPI/modules/bitmap_api && node api.js"' + volumes: + - ./configs/bitmap_api/.env:/usr/local/OPI/modules/bitmap_api/.env + depends_on: + postgres-opi-server: + condition: service_healthy + sns-indexer: + image: ${OPI_IMAGE} + restart: always + command: 'bash -c "cd /usr/local/OPI/modules/sns_index && python sns_index.py"' + volumes: + - ./configs/sns_index/.env:/usr/local/OPI/modules/sns_index/.env + depends_on: + postgres-opi-server: + condition: service_healthy + sns-api: + image: ${OPI_IMAGE} + command: 'bash -c "cd /usr/local/OPI/modules/sns_api && node api.js"' + volumes: + - ./configs/sns_api/.env:/usr/local/OPI/modules/sns_api/.env + depends_on: + postgres-opi-server: + condition: service_healthy \ No newline at end of file diff --git a/deploy/src/docker-composes/restore.docker-compose.yaml b/deploy/src/docker-composes/restore.docker-compose.yaml new file mode 100644 index 0000000..1097a6d --- /dev/null +++ b/deploy/src/docker-composes/restore.docker-compose.yaml @@ -0,0 +1,30 @@ +version: "3.7" +services: + postgres-opi-server: + image: postgres:15-alpine + restart: always + environment: + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWD} + POSTGRES_DB: ${DB_DATABASE} + volumes: + - ${OPI_PG_DATA_PATH}:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"] + interval: 10s + timeout: 5s + retries: 5 + restore: + image: ${OPI_IMAGE} + command: "bash -c \"cd /usr/local/OPI/modules/ && python restore.py\"" + volumes: + - ${WORKSPACE_ROOT}/configs/bitmap_api/.env:/usr/local/OPI/modules/bitmap_api/.env + - ${WORKSPACE_ROOT}/configs/bitmap_index/.env:/usr/local/OPI/modules/bitmap_index/.env + - ${WORKSPACE_ROOT}/configs/brc20_api/.env:/usr/local/OPI/modules/brc20_api/.env + - ${WORKSPACE_ROOT}/configs/brc20_index/.env:/usr/local/OPI/modules/brc20_index/.env + - ${WORKSPACE_ROOT}/configs/main_index/.env:/usr/local/OPI/modules/main_index/.env + - ${WORKSPACE_ROOT}/configs/sns_api/.env:/usr/local/OPI/modules/sns_api/.env + - ${WORKSPACE_ROOT}/configs/sns_index/.env:/usr/local/OPI/modules/sns_index/.env + depends_on: + postgres-opi-server: + condition: service_healthy \ No newline at end of file diff --git a/deploy/src/index.ts b/deploy/src/index.ts new file mode 100644 index 0000000..852f0cb --- /dev/null +++ b/deploy/src/index.ts @@ -0,0 +1,230 @@ +import * as digitalocean from "@pulumi/digitalocean"; +import { getPrivateKey, getScript, root, sshKey } from './utils' +import assert from "assert"; +import { local, remote, types } from "@pulumi/command"; +import fs from 'fs' +import path from 'path' +import os from 'os'; +import { join } from 'path'; +import { createHash } from 'crypto'; +import * as pulumi from "@pulumi/pulumi"; +import { Output } from '@pulumi/pulumi'; + + +function create(params: { name: string, region: string, size: string, image: string }) { + const { region, size, name, image } = params + const snapshotId = (() => { + const id = process.env['OPI_VOLUME_SNAPSHOT_ID'] + return id?.length == 0 ? undefined : id + })() + + const volume = new digitalocean.Volume(`${name}volume`, { + region, + size: parseInt(process.env['OPI_VOLUME_SIZE'] ?? "1000", 10), + initialFilesystemType: "ext4", + snapshotId, + }) + + const droplet = new digitalocean.Droplet(`${name}-droplet`, { + image, + region, + size, + // monitoring: true, + sshKeys: [sshKey.id], + }); + const copyFiles = (loc: string, remotePath: pulumi.Output) => { + return new local.Command(`${name}:copyFiles: ${loc}`, { + create: pulumi.interpolate`rsync -avP ${root(loc)} ${connection.user}@${droplet.ipv4Address}:${remotePath}`, + }) + } + + const volumeAttachment = new digitalocean.VolumeAttachment( + `${name}-volume-attachment`, + { + dropletId: droplet.id.apply(id => parseInt(id, 10)), + volumeId: volume.id, + } + ); + + const privateKey = getPrivateKey(); + + const connection: types.input.remote.ConnectionArgs = { + host: droplet.ipv4Address, + user: "root", + privateKey, + }; + + const volumePathPrint = new remote.Command(`${name}-read-volume-path`, { + connection, + create: getScript('print_mnt_name.sh'), + }, { dependsOn: [droplet, volumeAttachment, volume], customTimeouts: { create: '5m' } }); + + const cpRestoreDockerCompose = new remote.CopyFile(`${name}:restore`, { + connection, + localPath: volumePathPrint.stdout.apply(volumeName => transformFile(name, './src/docker-composes/restore.docker-compose.yaml', [ + ['${OPI_PG_DATA_PATH}', `${volumeName}/pg_data`], + ['${OPI_IMAGE}', process.env['OPI_IMAGE']!], + // DB_USER + ['${DB_USER}', process.env['DB_USER']!], + // DB_PASSWORD + ['${DB_PASSWD}', process.env['DB_PASSWD']!], + // DB_DATABASE + ['${DB_DATABASE}', process.env['DB_DATABASE']!], + // WORKSPACE_ROOT + ['${WORKSPACE_ROOT}', volumeName], + ])), + remotePath: volumePathPrint.stdout.apply(name => (`${name}/restore.docker-compose.yaml`)), + }); + + const execScriptOnRemote = (loc: string, options: { cwd?: pulumi.Output, commandOpts?: any } = {}) => { + // cwd is the CWD + const createContent = fs.readFileSync(root(loc), "utf-8"); + + if (options.cwd) { + return new remote.Command(`${name}:run[remote]: ${loc}`, { + connection, + create: pulumi.interpolate`mkdir -p ${options.cwd}; + cd ${options.cwd}; + ${createContent}` + }, options.commandOpts); + } + else { + return new remote.Command(`${name}:run[remote]: ${loc}`, { + connection, + create: createContent + }, options.commandOpts); + } + + } + + + const cpConfig = copyFiles('configs', pulumi.interpolate`${volumePathPrint.stdout}`) + + const restore = execScriptOnRemote('deploy/src/scripts/restore.sh', { + cwd: pulumi.interpolate`/${volumePathPrint.stdout}`, + commandOpts: { + dependsOn: [cpConfig, cpRestoreDockerCompose], + } + }) + + const cpDockerCompose = volumePathPrint.stdout.apply(volumeName => { + return new remote.CopyFile(`${name}:cp:opi.docker-compose -> ${volumeName}`, { + connection, + localPath: transformFile(name, './src/docker-composes/opi.docker-compose.yaml', [ + ['${OPI_PG_DATA_PATH}', `${volumeName}/pg_data`], + ['${OPI_BITCOIND_PATH}', `${volumeName}/bitcoind_data`], + ['${OPI_IMAGE}', process.env['OPI_IMAGE']!], + ['${BITCOIND_IMAGE}', process.env['BITCOIND_IMAGE']!], + // DB_USER + ['${DB_USER}', process.env['DB_USER']!], + // DB_PASSWORD + ['${DB_PASSWD}', process.env['DB_PASSWD']!], + // DB_DATABASE + ['${DB_DATABASE}', process.env['DB_DATABASE']!], + // WORKSPACE_ROOT + ['${WORKSPACE_ROOT}', volumeName], + // BITCOIN_RPC_USER + ['${BITCOIN_RPC_USER}', process.env['BITCOIN_RPC_USER']!], + // BITCOIN_RPC_PASSWD + ['${BITCOIN_RPC_PASSWD}', process.env['BITCOIN_RPC_PASSWD']!], + // BITCOIN_RPC_PORT + ['${BITCOIN_RPC_PORT}', process.env['BITCOIN_RPC_PORT']!], + ]), + remotePath: `${volumeName}/opi.docker-compose.yaml`, + }, { dependsOn: [restore] }); + }) + + new remote.Command(`${name}:start-opi..`, { + connection, + create: pulumi.interpolate`cd ${volumePathPrint.stdout} && docker-compose -f opi.docker-compose.yaml pull && docker-compose -f opi.docker-compose.yaml up -d`, + }, { dependsOn: [cpDockerCompose] }) + + + + exports[`ip_${name}`] = droplet.ipv4Address; + exports[`name_${name}`] = droplet.name; + exports[`volume_id_${name}`] = volume.id; + exports[`volume_attachment_id_${name}`] = volumeAttachment.id; + exports[`volume_path_${name}`] = volumePathPrint.stdout; + + return { droplet, volume, name }; +} + +// write takeSnapshot function which input is the output of function create. +function takeSnapshot(params: { name: string, volume: digitalocean.Volume }) { + const { name, volume } = params; + + const createSnapshot = new digitalocean.VolumeSnapshot(`${name}-snapshot`, { + volumeId: volume.id, + name: `${name}-snapshot`, + }); + + exports[`volume_snapshot_${name}`] = createSnapshot.id; + + return { createSnapshot }; +} + + +function transformFile(seed: string, filePath: string, transforms: string[][]): string { + // Read the content of the source file + const content = fs.readFileSync(filePath, 'utf8'); + + // Apply all transformations + let transformedContent = content; + for (const transform of transforms) { + const [original, replacement] = transform; + transformedContent = transformedContent.split(original).join(replacement); + } + + // Create a temp file in a random location + const tempDir = createTmpDirFromSeed(filePath + seed); + const tempFilePath = path.join(tempDir, path.basename(filePath)); + + // Write the transformed content to the temp file + fs.writeFileSync(tempFilePath, transformedContent); + + // Return the path of the temp file + return tempFilePath; +} +const createTmpDirFromSeed = (seed: string): string => { + const hash = createHash('sha256').update(seed).digest('hex'); + const tmpBaseDir = '/tmp'; + const dirPath = join(tmpBaseDir, hash); + + try { + fs.mkdirSync(dirPath, { recursive: true }); + return dirPath; + } catch (error) { + throw new Error(`Failed to create temp directory: ${error}`); + } +}; + + +// =============== +// Create Droplet +// =============== + +const instances = [ + +create({ + name: "opi1sfo", + region: 'sfo3', + size: 's-8vcpu-16gb-amd', + image: '149367446' +}), + +create({ + name: "opi1lon", + region: 'lon1', + size: 's-8vcpu-16gb-amd', + image: '149439505' +}), + +create({ + name: "opi1sgp", + region: 'sgp1', + size: 's-8vcpu-16gb-amd', + image: '149439499' +})]; + +// takeSnapshot(instances[0]) \ No newline at end of file diff --git a/deploy/src/scripts/config_bashrc.sh b/deploy/src/scripts/config_bashrc.sh new file mode 100644 index 0000000..811f616 --- /dev/null +++ b/deploy/src/scripts/config_bashrc.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# The target file to update +BASHRC_FILE="$HOME/.bashrc" +# The command to source the .env file +SOURCE_COMMAND='source $HOME/.env' + +# Escape any PATH or other environment variables in .bashrc that may interfere +ESCAPED_SOURCE_COMMAND=$(printf '%q' "$SOURCE_COMMAND") + +# Check if .env sourcing command is already in .bashrc, if not, append it +if ! grep -qxF "$ESCAPED_SOURCE_COMMAND" "$BASHRC_FILE"; then + echo "$SOURCE_COMMAND" >> "$BASHRC_FILE" + echo ".env sourcing added to $BASHRC_FILE" +else + echo ".env sourcing already exists in $BASHRC_FILE" +fi \ No newline at end of file diff --git a/deploy/src/scripts/print_mnt_name.sh b/deploy/src/scripts/print_mnt_name.sh new file mode 100644 index 0000000..c182482 --- /dev/null +++ b/deploy/src/scripts/print_mnt_name.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +retry_count=0 +max_retries=100 +retry_delay=5 + +while [ $retry_count -lt $max_retries ]; do + # Search for directories containing 'volume' in their name under /mnt + directories=$(find /mnt -type d -name "*volume*" 2>/dev/null) + dir_count=$(echo "$directories" | grep -c 'volume') + + if [ "$dir_count" -eq 1 ]; then + # If exactly one directory is found, print the directory name and exit with success + echo "$directories" + exit 0 + elif [ "$dir_count" -gt 1 ]; then + # More than one directory found, exit with code 2 (misuse of shell builtins according to Bash documentation) + echo "Multiple directories found." + exit 2 + else + # No directories found, increment the retry counter and wait + ((retry_count++)) + sleep $retry_delay + fi +done + +# If no directory is found after the maximum number of retries, exit with code 3 +exit 3 diff --git a/deploy/src/scripts/restore.sh b/deploy/src/scripts/restore.sh new file mode 100644 index 0000000..fe0b232 --- /dev/null +++ b/deploy/src/scripts/restore.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Start all services +docker-compose -f restore.docker-compose.yaml up -d + +# Wait for the "restore" service to finish and exit with a 0 +docker wait $(docker-compose -f restore.docker-compose.yaml ps -q restore) + +# Capture the exit code +exit_code=$? + +# If exit code is 0, shutdown all services +if [ $exit_code -eq 0 ]; then + docker-compose -f restore.docker-compose.yaml down +else + echo "Restore service exited with code $exit_code" + exit $exit_code +fi \ No newline at end of file diff --git a/deploy/src/utils.ts b/deploy/src/utils.ts new file mode 100644 index 0000000..9b07df4 --- /dev/null +++ b/deploy/src/utils.ts @@ -0,0 +1,44 @@ +import * as digitalocean from "@pulumi/digitalocean"; +import assert from "assert"; +import path from 'path'; +import os from 'os'; +import fs from 'fs'; + +export function root(filePath: string) { + const p = path.resolve(__dirname, `../../${filePath}`); + if (fs.existsSync(p)) { + return p; + } + throw new Error(`File not found: ${p}`); +} + +const id = process.env['DIGITAL_OCEAN_SSH_KEY_ID']; +const name = process.env['DIGITAL_OCEAN_SSH_KEY_NAME']; + +assert(id, "DIGITAL_OCEAN_SSH_KEY_ID is required"); +assert(name, "DIGITAL_OCEAN_SSH_KEY_NAME is required"); + +export const sshKey = digitalocean.SshKey.get(name, id); + +export const getPrivateKey = () => { + // Assuming your environment variable is named 'PRIVATE_KEY_PATH' + const privateKeyPath = process.env['PRIVATE_KEY_PATH']; + + if (!privateKeyPath) { + console.error('The environment variable PRIVATE_KEY_PATH is not set.'); + process.exit(1); // Exit with an error code + } + + // Handles the tilde by replacing it with the user's home directory + const resolvedPrivateKeyPath = privateKeyPath.startsWith('~') + ? path.join(os.homedir(), privateKeyPath.slice(1)) + : path.resolve(privateKeyPath); + + const key = fs.readFileSync(resolvedPrivateKeyPath, 'utf-8'); + + return key; +} + +export function getScript(scriptName: string) { + return fs.readFileSync(`./src/scripts/${scriptName}`, "utf-8"); +} \ No newline at end of file diff --git a/deploy/tsconfig.json b/deploy/tsconfig.json new file mode 100644 index 0000000..e99f872 --- /dev/null +++ b/deploy/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "strict": true, + "outDir": "bin", + "target": "ES2020", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "experimentalDecorators": true, + "esModuleInterop": true, + "pretty": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["stacks"] +} diff --git a/docker/bitcoind/Dockerfile b/docker/bitcoind/Dockerfile new file mode 100644 index 0000000..e837026 --- /dev/null +++ b/docker/bitcoind/Dockerfile @@ -0,0 +1,28 @@ +FROM debian:bullseye-slim + +RUN set -ex; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + gnupg \ + less \ + curl \ + wget \ + ca-certificates \ + git \ + ; \ + rm -rf /var/lib/apt/lists/*; + +RUN set -eux; \ + curl -L https://bitcoincore.org/bin/bitcoin-core-25.1/bitcoin-25.1-x86_64-linux-gnu.tar.gz -o bitcoin.tar.gz; \ + tar -xzvf bitcoin.tar.gz; \ + install -m 0755 -o root -g root -t /usr/local/bin bitcoin-25.1/bin/*; \ + rm -rf bitcoin.tar.gz bitcoin-25.1; \ + bitcoind --version; + +ENV BITCOIN_HOME /usr/local/bitcoind +VOLUME /usr/local/bitcoind +EXPOSE 8332 8333 8334 18543 + +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["docker-entrypoint.sh"] \ No newline at end of file diff --git a/docker/bitcoind/docker-entrypoint.sh b/docker/bitcoind/docker-entrypoint.sh new file mode 100755 index 0000000..cfaa970 --- /dev/null +++ b/docker/bitcoind/docker-entrypoint.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -Eeo pipefail + +export BITCOIN_RPC_PORT=${BITCOIN_RPC_PORT:-"8332"} +export BITCOIN_ZMQ_PORT=${BITCOIN_ZMQ_PORT:-"18543"} +export BITCOIN_RPC_USER=${BITCOIN_RPC_USER:-"bitcoin"} +export BITCOIN_RPC_PASSWD=${BITCOIN_RPC_PASSWD:-"abcd1234"} + +export BITCOIN_HOME="/usr/local/bitcoind" +_main() { + mkdir -p $BITCOIN_HOME/datadir + mkdir -p $BITCOIN_HOME/blocksdir + + echo " + # Generated by https://jlopp.github.io/bitcoin-core-config-generator/ + # [core] + blocksdir=$BITCOIN_HOME/blocksdir + datadir=$BITCOIN_HOME/datadir + dbcache=14000 + txindex=1 + pid=/bitcoind.pid + daemon=0 + + # [rpc] + server=1 + rest=1 + rpcuser=$BITCOIN_RPC_USER + rpcpassword=$BITCOIN_RPC_PASSWD + + # [wallet] + disablewallet=1 + rpcport=$BITCOIN_RPC_PORT + rpcallowip=0.0.0.0/0 + rpcallowip=::/0 + listen=1 + discover=0 + dns=0 + dnsseed=0 + listenonion=0 + rpcserialversion=1 + fallbackfee=0.00001 + rpcthreads=8 + blocksonly=1 + + " > /bitcoin.conf + + exec /usr/local/bin/bitcoind -conf=/bitcoin.conf +} + +_main "$@" \ No newline at end of file diff --git a/docker/opi/Dockerfile b/docker/opi/Dockerfile new file mode 100644 index 0000000..3b109fe --- /dev/null +++ b/docker/opi/Dockerfile @@ -0,0 +1,66 @@ +FROM python:3.9.18-bookworm + +ENV NODE_MAJOR=20 \ + RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=$PATH:/usr/local/cargo/bin:/usr/local/OPI/ord/target/release \ + RUST_VERSION=1.75.0 + +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + postgresql-client-common \ + postgresql-client-15 \ + build-essential \ + ca-certificates \ + curl \ + gnupg \ + wget \ + git \ + pbzip2 \ + ; \ + rm -rf /var/lib/apt/lists/* + +# install nodejs +RUN set -eux; \ + mkdir -p /etc/apt/keyrings; \ + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; \ + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list; \ + apt-get update; \ + apt-get install nodejs -y; \ + rm -rf /var/lib/apt/lists/* + +# install rust +RUN set -eux; \ + dpkgArch="$(dpkg --print-architecture)"; \ + case "${dpkgArch##*-}" in \ + amd64) rustArch='x86_64-unknown-linux-gnu'; rustupSha256='0b2f6c8f85a3d02fde2efc0ced4657869d73fccfce59defb4e8d29233116e6db' ;; \ + armhf) rustArch='armv7-unknown-linux-gnueabihf'; rustupSha256='f21c44b01678c645d8fbba1e55e4180a01ac5af2d38bcbd14aa665e0d96ed69a' ;; \ + arm64) rustArch='aarch64-unknown-linux-gnu'; rustupSha256='673e336c81c65e6b16dcdede33f4cc9ed0f08bde1dbe7a935f113605292dc800' ;; \ + i386) rustArch='i686-unknown-linux-gnu'; rustupSha256='e7b0f47557c1afcd86939b118cbcf7fb95a5d1d917bdd355157b63ca00fc4333' ;; \ + *) echo >&2 "unsupported architecture: ${dpkgArch}"; exit 1 ;; \ + esac; \ + url="https://static.rust-lang.org/rustup/archive/1.26.0/${rustArch}/rustup-init"; \ + wget "$url"; \ + echo "${rustupSha256} *rustup-init" | sha256sum -c -; \ + chmod +x rustup-init; \ + ./rustup-init -y --no-modify-path --profile minimal --default-toolchain $RUST_VERSION --default-host ${rustArch}; \ + rm rustup-init; \ + chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \ + rustup default stable; \ + rustup --version; \ + cargo --version; \ + rustc --version; + +RUN pip install --no-cache-dir python-dotenv psycopg2 psycopg2-binary boto3 tqdm json5 stdiomask requests; + +RUN git clone https://github.com/alexgo-io/OPI.git --branch feat/none-interactive /usr/local/OPI; \ + cd /usr/local/OPI/ord; cargo build --release; \ + cd /usr/local/OPI/modules/main_index; npm install; \ + cd /usr/local/OPI/modules/brc20_api; npm install; \ + cd /usr/local/OPI/modules/bitmap_api; npm install; \ + cd /usr/local/OPI/modules/sns_api; npm install; + +COPY remvoe_pubkey_validate.js /usr/local/OPI/modules/main_index +RUN node /usr/local/OPI/modules/main_index/remvoe_pubkey_validate.js && rm /usr/local/OPI/modules/main_index/remvoe_pubkey_validate.js +RUN mkdir /ord_data; mkdir -p /bitcoind \ No newline at end of file diff --git a/docker/opi/remvoe_pubkey_validate.js b/docker/opi/remvoe_pubkey_validate.js new file mode 100644 index 0000000..04bea36 --- /dev/null +++ b/docker/opi/remvoe_pubkey_validate.js @@ -0,0 +1,39 @@ +const fs = require('fs'); +const path = require('path') +const TARGET_FILE = path.resolve(__dirname, 'node_modules/bitcoinjs-lib/src/payments/p2tr.js'); +const BACKUP_FILE = `${TARGET_FILE}.backup`; + +// Exact content to be removed +const contentToRemove = ` + if (pubkey && pubkey.length) { + if (!(0, ecc_lib_1.getEccLib)().isXOnlyPoint(pubkey)) + throw new TypeError('Invalid pubkey for p2tr'); + } +`.trim(); // trim() to remove the extra newlines + +fs.readFile(TARGET_FILE, 'utf8', (err, data) => { + if (err) { + return console.error(`Error reading ${TARGET_FILE}: ${err.message}`); + } + + // Check if the content is present + if (!data.includes(contentToRemove)) { + return console.log('The specified content is not present in the file.'); + } + + // Create a backup if it doesn't exist + if (!fs.existsSync(BACKUP_FILE)) { + fs.writeFileSync(BACKUP_FILE, data); + console.log(`Backup created at ${BACKUP_FILE}`); + } + + // Remove the specified content + const updatedData = data.replace(contentToRemove, ''); + + fs.writeFile(TARGET_FILE, updatedData, 'utf8', (writeErr) => { + if (writeErr) { + return console.error(`Error writing to ${TARGET_FILE}: ${writeErr.message}`); + } + console.log(`The specified content has been removed from ${TARGET_FILE}.`); + }); +}); \ No newline at end of file diff --git a/provision/scripts/build/cleanup.sh b/provision/scripts/build/cleanup.sh new file mode 100644 index 0000000..2c02c08 --- /dev/null +++ b/provision/scripts/build/cleanup.sh @@ -0,0 +1,44 @@ +#!/bin/bash -e +################################################################################ +## File: cleanup.sh +## Desc: Perform cleanup +################################################################################ + +# before cleanup +before=$(df / -Pm | awk 'NR==2{print $4}') + +# clears out the local repository of retrieved package files +# It removes everything but the lock file from /var/cache/apt/archives/ and /var/cache/apt/archives/partial +apt-get clean +rm -rf /tmp/* +rm -rf /root/.cache + +# journalctl +if command -v journalctl; then + journalctl --rotate + journalctl --vacuum-time=1s +fi + +# delete all .gz and rotated file +find /var/log -type f -regex ".*\.gz$" -delete +find /var/log -type f -regex ".*\.[0-9]$" -delete + +# wipe log files +find /var/log/ -type f -exec cp /dev/null {} \; + +# delete symlink for tests running +rm -f /usr/local/bin/invoke_tests + +# remove apt mock +prefix=/usr/local/bin +for tool in apt apt-get apt-fast apt-key;do + sudo rm -f $prefix/$tool +done + +# after cleanup +after=$(df / -Pm | awk 'NR==2{print $4}') + +# display size +echo "Before: $before MB" +echo "After : $after MB" +echo "Delta : $(($after-$before)) MB" \ No newline at end of file diff --git a/provision/scripts/build/configure-apt-mock.sh b/provision/scripts/build/configure-apt-mock.sh new file mode 100644 index 0000000..e12b0f5 --- /dev/null +++ b/provision/scripts/build/configure-apt-mock.sh @@ -0,0 +1,54 @@ +#!/bin/bash -e +################################################################################ +## File: configure-apt-mock.sh +## Desc: A temporary workaround for https://github.com/Azure/azure-linux-extensions/issues/1238. +## Cleaned up during configure-cleanup.sh. +################################################################################ + +prefix=/usr/local/bin + +for real_tool in /usr/bin/apt /usr/bin/apt-get /usr/bin/apt-fast /usr/bin/apt-key; do + tool=$(basename $real_tool) + cat >$prefix/$tool <\$err + + # no errors, break the loop and continue normal flow + test -f \$err || break + cat \$err >&2 + + retry=false + + if grep -q 'Could not get lock' \$err;then + # apt db locked needs retry + retry=true + elif grep -q 'Could not open file /var/lib/apt/lists' \$err;then + # apt update is not completed, needs retry + retry=true + elif grep -q 'IPC connect call failed' \$err;then + # the delay should help with gpg-agent not ready + retry=true + elif grep -q 'Temporary failure in name resolution' \$err;then + # It looks like DNS is not updated with random generated hostname yet + retry=true + elif grep -q 'dpkg frontend is locked by another process' \$err;then + # dpkg process is busy by another process + retry=true + fi + + rm \$err + if [ \$retry = false ]; then + break + fi + + sleep 5 + echo "...retry \$i" + i=\$((i + 1)) +done +EOT + chmod +x $prefix/$tool +done diff --git a/provision/scripts/build/configure-apt.sh b/provision/scripts/build/configure-apt.sh new file mode 100644 index 0000000..c2625cf --- /dev/null +++ b/provision/scripts/build/configure-apt.sh @@ -0,0 +1,43 @@ +#!/bin/bash -e + +export DEBIAN_FRONTEND=noninteractive + +# Stop and disable apt-daily upgrade services; +systemctl stop apt-daily.timer +systemctl disable apt-daily.timer +systemctl disable apt-daily.service +systemctl stop apt-daily-upgrade.timer +systemctl disable apt-daily-upgrade.timer +systemctl disable apt-daily-upgrade.service + +# Enable retry logic for apt up to 10 times +echo "APT::Acquire::Retries \"10\";" > /etc/apt/apt.conf.d/80-retries + +# Configure apt to always assume Y +echo "APT::Get::Assume-Yes \"true\";" > /etc/apt/apt.conf.d/90assumeyes + +# APT understands a field called Phased-Update-Percentage which can be used to control the rollout of a new version. It is an integer between 0 and 100. +# In case you have multiple systems that you want to receive the same set of updates, +# you can set APT::Machine-ID to a UUID such that they all phase the same, +# or set APT::Get::Never-Include-Phased-Updates or APT::Get::Always-Include-Phased-Updates to true such that APT will never/always consider phased updates. +# apt-cache policy pkgname +echo 'APT::Get::Always-Include-Phased-Updates "true";' > /etc/apt/apt.conf.d/99-phased-updates + +# Fix bad proxy and http headers settings +cat <> /etc/apt/apt.conf.d/99bad_proxy +Acquire::http::Pipeline-Depth 0; +Acquire::http::No-Cache true; +Acquire::BrokenProxy true; +EOF + +# Uninstall unattended-upgrades +rm -rf /var/log/unattended-upgrades +apt-get purge unattended-upgrades -y + +apt-get install -y apt-transport-https ca-certificates curl software-properties-common +apt-get -yq update +apt-get -yq dist-upgrade + +# Install apt-fast using quick-install.sh +# https://github.com/ilikenwf/apt-fast +bash -c "$(curl -fsSL https://raw.githubusercontent.com/ilikenwf/apt-fast/master/quick-install.sh)" \ No newline at end of file diff --git a/provision/scripts/build/pull.sh b/provision/scripts/build/pull.sh new file mode 100644 index 0000000..e354b73 --- /dev/null +++ b/provision/scripts/build/pull.sh @@ -0,0 +1,4 @@ +#!/bin/bash -e + +docker pull caoer/opi:latest +docker pull caoer/bitcoind:latest \ No newline at end of file diff --git a/provision/scripts/build/setup.sh b/provision/scripts/build/setup.sh new file mode 100644 index 0000000..2e32356 --- /dev/null +++ b/provision/scripts/build/setup.sh @@ -0,0 +1,42 @@ +#!/bin/bash -e + +## Common + +# while sudo lsof /var/lib/dpkg/lock-frontend ; do sleep 1; done; +# Install packages to allow apt to use a repository over HTTPS +sudo apt-get install -y \ + ca-certificates \ + curl \ + gnupg \ + wget \ + git \ + build-essential \ + ncdu \ + bpytop \ + pbzip2 \ + lsb-release + +## Install Docker +curl -fsSL https://get.docker.com | sh + +# Enable docker.service +systemctl is-active --quiet docker.service || systemctl start docker.service +systemctl is-enabled --quiet docker.service || systemctl enable docker.service + +# Docker daemon takes time to come up after installing +sleep 10 +docker info + +## Install docker-compose + +# Download the current stable release of Docker Compose +sudo curl -L "https://github.com/docker/compose/releases/download/$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep -Po '"tag_name": "\K.*?(?=")')/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + +# Apply executable permissions to the binary +sudo chmod +x /usr/local/bin/docker-compose + +# Optionally, install command completion for the bash shell +sudo curl -L https://raw.githubusercontent.com/docker/compose/$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep -Po '"tag_name": "\K.*?(?=")')/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose + +# Check the installation +docker-compose version diff --git a/provision/templates/opi.pkr.hcl b/provision/templates/opi.pkr.hcl new file mode 100644 index 0000000..d685bee --- /dev/null +++ b/provision/templates/opi.pkr.hcl @@ -0,0 +1,74 @@ +packer { + required_plugins { + digitalocean = { + version = ">= 1.0.4" + source = "github.com/digitalocean/digitalocean" + } + } +} + +variable "digitalocean_api_token" { + type = string + default = "${env("DIGITAL_OCEAN_API_KEY")}" +} + +source "digitalocean" "opi-sfo3" { + api_token = "${var.digitalocean_api_token}" + image = "ubuntu-22-04-x64" + region = "sfo3" + size = "s-1vcpu-1gb" + ssh_username = "root" +} +source "digitalocean" "opi-sgp1" { + api_token = "${var.digitalocean_api_token}" + image = "ubuntu-22-04-x64" + region = "sgp1" + size = "s-1vcpu-1gb" + ssh_username = "root" +} +source "digitalocean" "opi-lon1" { + api_token = "${var.digitalocean_api_token}" + image = "ubuntu-22-04-x64" + region = "lon1" + size = "s-1vcpu-1gb" + ssh_username = "root" +} + + +build { + name = "opi-ubuntu-22-04-x64" + sources = [ + "source.digitalocean.opi-sfo3", + "source.digitalocean.opi-lon1", + "source.digitalocean.opi-", + ] + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + script = "${path.root}/../scripts/build/configure-apt-mock.sh" + } + + provisioner "shell" { + environment_vars = ["DEBIAN_FRONTEND=noninteractive"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = [ + "${path.root}/../scripts/build/configure-apt-mock.sh", + "${path.root}/../scripts/build/configure-apt.sh", + "${path.root}/../scripts/build/setup.sh", + "${path.root}/../scripts/build/pull.sh", + ] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "sudo reboot"] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + pause_before = "1m0s" + scripts = ["${path.root}/../scripts/build/cleanup.sh"] + start_retry_timeout = "10m" + } +} \ No newline at end of file