diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..7d028477 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +COINBASE_APP_ID=coinbaseappid +MOONPAY_API_KEY=moonpayapikey +SEGMENT_WRITE_KEY=segmentwritekey +SENTRY_DSN=sentrydsn +TRANSAK_API_KEY=transakapikey +WALLET_ENVIRONMENT=development diff --git a/.github/workflows/build-extension.yml b/.github/workflows/build-extension.yml index abaec7d5..6b3142f2 100644 --- a/.github/workflows/build-extension.yml +++ b/.github/workflows/build-extension.yml @@ -2,8 +2,11 @@ name: PR Extensions builds on: [pull_request, workflow_dispatch] env: + COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }} + MOONPAY_API_KEY: ${{ secrets.MOONPAY_API_KEY }} SENTRY_DSN: ${{ secrets.SENTRY_DSN }} SEGMENT_WRITE_KEY: ${{ secrets.SEGMENT_WRITE_KEY_STAGING }} + TRANSAK_API_KEY: ${{ secrets.TRANSAK_API_KEY }} WALLET_ENVIRONMENT: feature jobs: diff --git a/.github/workflows/publish-dev-extension.yml b/.github/workflows/publish-dev-extension.yml index d9f2adb6..54f214db 100644 --- a/.github/workflows/publish-dev-extension.yml +++ b/.github/workflows/publish-dev-extension.yml @@ -6,9 +6,12 @@ on: workflow_dispatch: env: + COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }} + MOONPAY_API_KEY: ${{ secrets.MOONPAY_API_KEY }} # To use another sentry DSN SENTRY_DSN: ${{ secrets.SENTRY_DSN }} SEGMENT_WRITE_KEY: ${{ secrets.SEGMENT_WRITE_KEY_STAGING }} + TRANSAK_API_KEY: ${{ secrets.TRANSAK_API_KEY }} PREVIEW_RELEASE: true WALLET_ENVIRONMENT: preview diff --git a/.github/workflows/publish-extensions.yml b/.github/workflows/publish-extensions.yml index 1f772af1..731a1fa4 100644 --- a/.github/workflows/publish-extensions.yml +++ b/.github/workflows/publish-extensions.yml @@ -6,8 +6,11 @@ on: workflow_dispatch: env: + COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }} + MOONPAY_API_KEY: ${{ secrets.MOONPAY_API_KEY }} SENTRY_DSN: ${{ secrets.SENTRY_DSN }} SEGMENT_WRITE_KEY: ${{ secrets.SEGMENT_WRITE_KEY }} + TRANSAK_API_KEY: ${{ secrets.TRANSAK_API_KEY }} WALLET_ENVIRONMENT: production jobs: diff --git a/package.json b/package.json index 4fa5612e..3c57fbb8 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "version": "3.13.5", "author": "Hiro Systems PBC", "scripts": { - "dev": "concurrently --raw 'node webpack/dev-server.js' 'redux-devtools --hostname=localhost --port=8000'", + "dev": "cross-env NODE_ENV=development concurrently --raw 'node webpack/dev-server.js' 'redux-devtools --hostname=localhost --port=8000'", "dev:test-app": "webpack serve --config test-app/webpack/webpack.config.dev.js", "build": "cross-env NODE_ENV=production EXT_ENV=prod webpack --config webpack/webpack.config.prod.js", "build:analyze": "cross-env ANALYZE=true NODE_ENV=production EXT_ENV=prod webpack --config webpack/webpack.config.prod.js", @@ -271,6 +271,7 @@ "deepmerge": "4.2.2", "dependency-cruiser": "11.11.0", "dotenv": "10.0.0", + "dotenv-webpack": "8.0.0", "esbuild": "0.14.7", "esbuild-loader": "2.18.0", "eslint-plugin-deprecation": "1.2.1", diff --git a/src/app/common/dev/types/globals.d.ts b/src/app/common/dev/types/globals.d.ts index b7407720..c900f5d8 100644 --- a/src/app/common/dev/types/globals.d.ts +++ b/src/app/common/dev/types/globals.d.ts @@ -1,8 +1,2 @@ declare const EXT_ENV: string; -declare const NODE_ENV: string; -declare const SEGMENT_KEY: string; -declare const STATS_URL: string; -declare const COMMIT_SHA: string; declare const VERSION: string; -declare const BRANCH: string; -declare const BRANCH_NAME: string; diff --git a/src/app/common/hooks/analytics/use-analytics.ts b/src/app/common/hooks/analytics/use-analytics.ts index 12e5757b..01ad7053 100644 --- a/src/app/common/hooks/analytics/use-analytics.ts +++ b/src/app/common/hooks/analytics/use-analytics.ts @@ -1,10 +1,10 @@ import { useCurrentNetworkState } from '@app/store/network/networks.hooks'; import { useLocation } from 'react-router-dom'; -import { IS_TEST_ENV } from '@shared/constants'; import { EventParams, PageParams } from '@segment/analytics-next/dist/pkg/core/arguments-resolver'; import { analytics } from '@app/common/segment-init'; import { logger } from '@shared/logger'; import { useWalletType } from '@app/common/use-wallet-type'; +import { IS_TEST_ENV } from '@shared/environment'; const IGNORED_PATH_REGEXPS = [/^\/$/]; diff --git a/src/app/common/persistence.ts b/src/app/common/persistence.ts index 93bed08e..f959ef92 100644 --- a/src/app/common/persistence.ts +++ b/src/app/common/persistence.ts @@ -2,7 +2,8 @@ import { QueryClient } from 'react-query'; import { createWebStoragePersistor } from 'react-query/createWebStoragePersistor-experimental'; import { persistQueryClient } from 'react-query/persistQueryClient-experimental'; -import { IS_TEST_ENV, PERSISTENCE_CACHE_TIME } from '@shared/constants'; +import { PERSISTENCE_CACHE_TIME } from '@shared/constants'; +import { IS_TEST_ENV } from '@shared/environment'; const localStoragePersistor = createWebStoragePersistor({ storage: window.localStorage }); diff --git a/src/app/common/segment-init.ts b/src/app/common/segment-init.ts index f5709ab4..840825eb 100644 --- a/src/app/common/segment-init.ts +++ b/src/app/common/segment-init.ts @@ -1,21 +1,21 @@ import { Analytics, AnalyticsBrowser } from '@segment/analytics-next'; -import { IS_TEST_ENV } from '@shared/constants'; + +import { IS_PROD_ENV, IS_TEST_ENV, SEGMENT_WRITE_KEY } from '@shared/environment'; import { logger } from '@shared/logger'; import { checkUserHasGrantedPermission } from '@shared/utils/sentry-init'; export let analytics: Analytics; export function initSegment() { - const writeKey = process.env.SEGMENT_WRITE_KEY; + const writeKey = SEGMENT_WRITE_KEY; const hasPermission = checkUserHasGrantedPermission(); if (!hasPermission) { logger.info('segment init aborted: has no permission.'); return; } if (IS_TEST_ENV) return; - if (!writeKey) { - if (process.env.WALLET_ENVIRONMENT === 'production') - logger.error('segment init aborted: No WRITE_KEY setup.'); + if (!SEGMENT_WRITE_KEY) { + if (IS_PROD_ENV) logger.error('segment init aborted: No WRITE_KEY setup.'); return; } diff --git a/src/app/common/store-utils.ts b/src/app/common/store-utils.ts index 2749bf63..da8e9618 100644 --- a/src/app/common/store-utils.ts +++ b/src/app/common/store-utils.ts @@ -1,7 +1,8 @@ -import { userHasAllowedDiagnosticsKey } from '@shared/utils/storage'; import hash from 'object-hash'; import { hashQueryKey, QueryKey } from 'react-query'; +import { userHasAllowedDiagnosticsKey } from '@shared/utils/storage'; + export function textToBytes(content: string) { return new TextEncoder().encode(content); } diff --git a/src/app/components/header.tsx b/src/app/components/header.tsx index 6a986105..1bb0fe75 100644 --- a/src/app/components/header.tsx +++ b/src/app/components/header.tsx @@ -11,6 +11,7 @@ import { Title } from '@app/components/typography'; import { OnboardingSelectors } from '@tests/integration/onboarding/onboarding.selectors'; import { RouteUrls } from '@shared/route-urls'; import { SettingsSelectors } from '@tests/integration/settings.selectors'; +import { BRANCH } from '@shared/environment'; interface HeaderProps extends FlexProps { actionButton?: JSX.Element; diff --git a/src/app/pages/fund/components/fiat-providers.utils.ts b/src/app/pages/fund/components/fiat-providers.utils.ts index d06ac73d..7207af4d 100644 --- a/src/app/pages/fund/components/fiat-providers.utils.ts +++ b/src/app/pages/fund/components/fiat-providers.utils.ts @@ -12,13 +12,7 @@ import MoonPayIcon from '@assets/images/fund/fiat-providers/moonpay-icon.png'; import OkcoinIcon from '@assets/images/fund/fiat-providers/okcoin-icon.png'; import OkxIcon from '@assets/images/fund/fiat-providers/okx-icon.png'; import TransakIcon from '@assets/images/fund/fiat-providers/transak-icon.png'; -import { - COINBASE_APP_ID, - IS_PRODUCTION_ENV, - MOONPAY_API_KEY, - TRANSAK_API_KEY_PRODUCTION, - TRANSAK_API_KEY_STAGING, -} from '@shared/constants'; +import { COINBASE_APP_ID, MOONPAY_API_KEY, TRANSAK_API_KEY } from '@shared/environment'; // Keys are set in wallet-config.json enum ActiveFiatProviders { @@ -74,11 +68,9 @@ function makeOkcoinUrl(address: string) { } function makeTransakUrl(address: string) { - const apiKey = IS_PRODUCTION_ENV ? TRANSAK_API_KEY_PRODUCTION : TRANSAK_API_KEY_STAGING; - const subdomain = IS_PRODUCTION_ENV ? 'global' : 'staging-global'; const screenTitle = 'Buy Stacks'; - return `https://${subdomain}.transak.com?apiKey=${apiKey}&cryptoCurrencyCode=STX&exchangeScreenTitle=${encodeURI( + return `https://global.transak.com?apiKey=${TRANSAK_API_KEY}&cryptoCurrencyCode=STX&exchangeScreenTitle=${encodeURI( screenTitle )}&defaultPaymentMethod=credit_debit_card&walletAddress=${address}`; } diff --git a/src/app/pages/transaction-request/components/post-conditions/post-conditions.tsx b/src/app/pages/transaction-request/components/post-conditions/post-conditions.tsx index 326595a3..eb1f2c1e 100644 --- a/src/app/pages/transaction-request/components/post-conditions/post-conditions.tsx +++ b/src/app/pages/transaction-request/components/post-conditions/post-conditions.tsx @@ -3,10 +3,10 @@ import { TransactionTypes } from '@stacks/connect'; import { color, Flex } from '@stacks/ui'; import { PostConditionMode } from '@stacks/transactions'; -import { IS_TEST_ENV } from '@shared/constants'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; import { useTransactionPostConditions } from '@app/store/transactions/transaction.hooks'; import { usePostConditionModeState } from '@app/store/transactions/post-conditions.hooks'; +import { IS_TEST_ENV } from '@shared/environment'; import { StxPostCondition } from './stx-post-condition'; import { PostConditionsList } from './post-conditions-list'; diff --git a/src/app/query/hiro-config/hiro-config.query.ts b/src/app/query/hiro-config/hiro-config.query.ts index a71fbc70..db6a8dc4 100644 --- a/src/app/query/hiro-config/hiro-config.query.ts +++ b/src/app/query/hiro-config/hiro-config.query.ts @@ -1,6 +1,7 @@ import { useQuery } from 'react-query'; import { GITHUB_ORG, GITHUB_REPO } from '@shared/constants'; +import { BRANCH_NAME } from '@shared/environment'; import { isUndefined } from '@shared/utils'; export interface HiroMessage { @@ -35,9 +36,9 @@ interface HiroConfig { feeEstimationsMinMax?: FeeEstimationsConfig; } -const DEFAULT_BRANCH = 'main'; +const defaultBranch = 'main'; const githubWalletConfigRawUrl = `https://raw.githubusercontent.com/${GITHUB_ORG}/${GITHUB_REPO}/${ - BRANCH_NAME || DEFAULT_BRANCH + BRANCH_NAME || defaultBranch }/config/wallet-config.json`; async function fetchHiroMessages(): Promise { diff --git a/src/app/store/index.ts b/src/app/store/index.ts index 2368707a..4c61766f 100644 --- a/src/app/store/index.ts +++ b/src/app/store/index.ts @@ -13,6 +13,8 @@ import { REGISTER, } from 'redux-persist'; +import { IS_DEV_ENV } from '@shared/environment'; + import { keySlice } from './keys/key.slice'; import { stxChainSlice } from './chains/stx-chain.slice'; import { broadcastActionTypeToOtherFramesMiddleware } from './utils/broadcast-action-types'; @@ -55,16 +57,15 @@ export const store = configureStore({ }), broadcastActionTypeToOtherFramesMiddleware, ], - enhancers: - process.env.WALLET_ENVIRONMENT === 'development' - ? [ - devToolsEnhancer({ - hostname: 'localhost', - port: 8000, - realtime: true, - }), - ] - : [], + enhancers: IS_DEV_ENV + ? [ + devToolsEnhancer({ + hostname: 'localhost', + port: 8000, + realtime: true, + }), + ] + : [], }); export const persistor = persistStore(store); diff --git a/src/inpage/inpage.ts b/src/inpage/inpage.ts index 4948373f..6fa43d81 100644 --- a/src/inpage/inpage.ts +++ b/src/inpage/inpage.ts @@ -1,4 +1,6 @@ import { StacksProvider } from '@stacks/connect'; + +import { BRANCH, COMMIT_SHA } from '@shared/environment'; import { AuthenticationRequestEventDetails, DomEventName, diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 369c5cdb..f51de117 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -1,5 +1,7 @@ import { ChainID } from '@stacks/transactions'; +import { IS_TEST_ENV } from './environment'; + export const gaiaUrl = 'https://hub.blockstack.org'; export const POPUP_CENTER_WIDTH = 442; @@ -11,10 +13,6 @@ export const DEFAULT_FEE_RATE = 400; export const HUMAN_REACTION_DEBOUNCE_TIME = 200; -export const IS_TEST_ENV = process.env.TEST_ENV === 'true'; -export const IS_PRODUCTION_ENV = - process.env.WALLET_ENVIRONMENT === 'production' || process.env.WALLET_ENVIRONMENT === 'preview'; - export const PERSISTENCE_CACHE_TIME = 1000 * 60 * 60 * 12; // 12 hours export const STX_DECIMALS = 6; @@ -66,9 +64,3 @@ export enum QueryRefreshRates { } export const DEFAULT_LIST_LIMIT = 50; - -// TODO: Relocate to env file -export const COINBASE_APP_ID = 'ca72edbc-0a08-468c-952d-6cde50b1dce6'; -export const MOONPAY_API_KEY = 'pk_live_Bctok4Wp6KZHX0YfS4Ie7dFOYnNw8lqv'; -export const TRANSAK_API_KEY_PRODUCTION = '7300ebf7-c657-46b1-9c72-c0d91bbed0a8'; -export const TRANSAK_API_KEY_STAGING = '4055d318-9d41-4b74-9253-e73e3ca13602'; diff --git a/src/shared/environment.ts b/src/shared/environment.ts new file mode 100644 index 00000000..bff27a89 --- /dev/null +++ b/src/shared/environment.ts @@ -0,0 +1,12 @@ +export const BRANCH = process.env.GITHUB_REF; +export const BRANCH_NAME = process.env.GITHUB_HEAD_REF; +export const COINBASE_APP_ID = process.env.COINBASE_APP_ID ?? ''; +export const COMMIT_SHA = process.env.GITHUB_SHA; +export const IS_DEV_ENV = process.env.WALLET_ENVIRONMENT === 'development'; +export const IS_PROD_ENV = + process.env.WALLET_ENVIRONMENT === 'production' || process.env.WALLET_ENVIRONMENT === 'preview'; +export const MOONPAY_API_KEY = process.env.MOONPAY_API_KEY ?? ''; +export const IS_TEST_ENV = process.env.TEST_ENV === 'true'; +export const SEGMENT_WRITE_KEY = process.env.SEGMENT_WRITE_KEY ?? ''; +export const SENTRY_DSN = process.env.SENTRY_DSN ?? ''; +export const TRANSAK_API_KEY = process.env.TRANSAK_API_KEY ?? ''; diff --git a/src/shared/logger.ts b/src/shared/logger.ts index 958d170f..79146ba5 100644 --- a/src/shared/logger.ts +++ b/src/shared/logger.ts @@ -1,5 +1,6 @@ import pino from 'pino'; -import { IS_TEST_ENV } from './constants'; + +import { IS_TEST_ENV } from './environment'; const loggingTool = pino({ enabled: process.env.NODE_ENV !== 'production' && !IS_TEST_ENV, diff --git a/src/shared/utils/sentry-init.ts b/src/shared/utils/sentry-init.ts index 044eda0d..f3cead14 100644 --- a/src/shared/utils/sentry-init.ts +++ b/src/shared/utils/sentry-init.ts @@ -1,5 +1,8 @@ import * as Sentry from '@sentry/react'; import { Integrations } from '@sentry/tracing'; + +import { SENTRY_DSN } from '@shared/environment'; + import { userHasAllowedDiagnosticsKey } from './storage'; export function checkUserHasGrantedPermission() { @@ -8,7 +11,7 @@ export function checkUserHasGrantedPermission() { export function initSentry() { Sentry.init({ - dsn: process.env.SENTRY_DSN, + dsn: SENTRY_DSN, integrations: [new Integrations.BrowserTracing()], tracesSampleRate: 0, enabled: checkUserHasGrantedPermission(), diff --git a/webpack/dev-server.js b/webpack/dev-server.js index 63b1fe51..09d012f0 100644 --- a/webpack/dev-server.js +++ b/webpack/dev-server.js @@ -1,10 +1,3 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ - -process.env.BABEL_ENV = 'development'; -process.env.NODE_ENV = 'development'; -process.env.ASSET_PATH = '/'; -process.env.PORT = '8080'; - const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); const WebpackDevServer = require('webpack-dev-server'); const webpack = require('webpack'); @@ -17,8 +10,8 @@ const PORT = process.env.PORT || '8080'; const config = require('./webpack.config.dev'); -// This is important, allows for fast refresh to work -// we don't want to inject our fast refresh helpers into these entry points +// This is important bc it allows for fast refresh to work +// We don't want to inject our fast refresh helpers into these entry points const excludeEntriesFromHotModuleReload = ['content-script', 'inpage']; Object.keys(config.entry).forEach(entryName => { diff --git a/webpack/webpack.config.base.js b/webpack/webpack.config.base.js index d8338ecc..b91732e7 100755 --- a/webpack/webpack.config.base.js +++ b/webpack/webpack.config.base.js @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-var-requires */ const path = require('path'); const webpack = require('webpack'); -const { execSync } = require('child_process'); const { version: _version } = require('../package.json'); const generateManifest = require('../scripts/generate-manifest'); +const Dotenv = require('dotenv-webpack'); const GenerateJsonPlugin = require('generate-json-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); @@ -16,31 +16,23 @@ const ProgressBarPlugin = require('progress-bar-webpack-plugin'); const SRC_ROOT_PATH = path.join(__dirname, '../', 'src'); const DIST_ROOT_PATH = path.join(__dirname, '../', 'dist'); + const NODE_ENV = process.env.NODE_ENV || 'development'; -const IS_DEV = NODE_ENV === 'development'; -const IS_PROD = !IS_DEV; -const TEST_ENV = !!process.env.TEST_ENV; const ANALYZE_BUNDLE = process.env.ANALYZE === 'true'; const IS_PUBLISHING = !!process.env.IS_PUBLISHING; -const MAIN_BRANCH = 'refs/heads/main'; -const GITHUB_HEAD_REF = process.env.GITHUB_HEAD_REF; -const GITHUB_REF = process.env.GITHUB_REF; -const GITHUB_SHA = process.env.GITHUB_SHA; +const BRANCH = process.env.GITHUB_REF; -/** - * For non main branch builds, we add a random number after the patch version. - */ +const IS_DEV = NODE_ENV === 'development'; +const IS_PROD = !IS_DEV; +const MAIN_BRANCH = 'refs/heads/main'; + +// For non main branch builds, add a random number const getVersionWithRandomSuffix = ref => { if (ref === MAIN_BRANCH || !ref || IS_PUBLISHING) return _version; return `${_version}.${Math.floor(Math.floor(Math.random() * 1000))}`; }; - -const BRANCH = GITHUB_REF; -const BRANCH_NAME = GITHUB_HEAD_REF; -const COMMIT_SHA = GITHUB_SHA; const VERSION = getVersionWithRandomSuffix(BRANCH); -// to measure speed :~) const smp = new SpeedMeasurePlugin({ disable: !ANALYZE_BUNDLE, granularLoaderData: true, @@ -208,19 +200,14 @@ const config = { new CopyWebpackPlugin({ patterns: [{ from: 'node_modules/webextension-polyfill/dist/browser-polyfill.js' }], }), - new webpack.DefinePlugin({ - NODE_ENV: JSON.stringify(NODE_ENV), - VERSION: JSON.stringify(VERSION), - COMMIT_SHA: JSON.stringify(COMMIT_SHA), - BRANCH: JSON.stringify(BRANCH), - BRANCH_NAME: JSON.stringify(BRANCH_NAME), - 'process.env.TEST_ENV': JSON.stringify(TEST_ENV ? 'true' : 'false'), + + new Dotenv({ + allowEmptyValues: true, + systemvars: true, }), - new webpack.EnvironmentPlugin({ - SENTRY_DSN: process.env.SENTRY_DSN ?? '', - SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY ?? '', - WALLET_ENVIRONMENT: process.env.WALLET_ENVIRONMENT ?? 'development', + new webpack.DefinePlugin({ + VERSION: JSON.stringify(VERSION), }), new webpack.ProvidePlugin({ diff --git a/yarn.lock b/yarn.lock index fce9d53c..b2d09aeb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8489,11 +8489,30 @@ dot-prop@6.0.1, dot-prop@^5.1.0, dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" +dotenv-defaults@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz#6b3ec2e4319aafb70940abda72d3856770ee77ac" + integrity sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg== + dependencies: + dotenv "^8.2.0" + +dotenv-webpack@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/dotenv-webpack/-/dotenv-webpack-8.0.0.tgz#bde8750eebda8fd1c6d7134f02bf56ad1f865772" + integrity sha512-vsWj11yWbIxLUPcQDbifCGW1+Mp03XfApFHJTC+/Ag9g3D/AnxoaVZcp76LpuBmReRwIJ+YO1fVdhmpzh+LL1A== + dependencies: + dotenv-defaults "^2.0.2" + dotenv@10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== +dotenv@^8.2.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== + dotgitignore@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/dotgitignore/-/dotgitignore-2.1.0.tgz#a4b15a4e4ef3cf383598aaf1dfa4a04bcc089b7b"